]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / jaegertracing / opentelemetry-cpp / exporters / etw / include / opentelemetry / exporters / etw / etw_tracer.h
1 // Copyright The OpenTelemetry Authors
2 // SPDX-License-Identifier: Apache-2.0
3
4 #pragma once
5
6 #include <algorithm>
7 #include <atomic>
8
9 #include <cstdint>
10 #include <cstdio>
11 #include <cstdlib>
12
13 #include <fstream>
14 #include <iomanip>
15 #include <iostream>
16 #include <map>
17 #include <memory>
18 #include <mutex>
19 #include <sstream>
20 #include <vector>
21
22 #include "opentelemetry/nostd/shared_ptr.h"
23 #include "opentelemetry/nostd/string_view.h"
24 #include "opentelemetry/nostd/unique_ptr.h"
25 #include "opentelemetry/nostd/variant.h"
26
27 #include "opentelemetry/common/key_value_iterable_view.h"
28
29 #include "opentelemetry/trace/span.h"
30 #include "opentelemetry/trace/span_context_kv_iterable_view.h"
31 #include "opentelemetry/trace/span_id.h"
32 #include "opentelemetry/trace/trace_id.h"
33 #include "opentelemetry/trace/tracer_provider.h"
34
35 #include "opentelemetry/sdk/trace/exporter.h"
36
37 #include "opentelemetry/exporters/etw/etw_config.h"
38 #include "opentelemetry/exporters/etw/etw_fields.h"
39 #include "opentelemetry/exporters/etw/etw_properties.h"
40 #include "opentelemetry/exporters/etw/etw_provider.h"
41 #include "opentelemetry/exporters/etw/utils.h"
42
43 OPENTELEMETRY_BEGIN_NAMESPACE
44 namespace exporter
45 {
46 namespace etw
47 {
48
49 class Span;
50
51 /**
52 * @brief Template that allows to instantiate new Span object for header-only forward-declared
53 * etw::Span type
54 *
55 * @tparam SpanType Expected to be etw::Span
56 * @tparam TracerType expected to be etw::Tracer
57 * @param objPtr Pointer to parent
58 * @param name Span Name
59 * @param options Span Options
60 * @return Span instance
61 */
62 template <class SpanType, class TracerType>
63 SpanType *new_span(TracerType *objPtr,
64 nostd::string_view name,
65 const opentelemetry::trace::StartSpanOptions &options)
66 {
67 return new (std::nothrow) SpanType{*objPtr, name, options};
68 }
69
70 /**
71 * @brief Template that allows to convert etw::Span pointer to smart shared pointer to
72 * `opentelemetry::trace::Span`
73 * @tparam SpanType Expected to be etw::Span
74 * @param ptr Pointer to etw::Span
75 * @return Smart shared pointer to `opentelemetry::trace::Span`
76 */
77 template <class SpanType>
78 nostd::shared_ptr<opentelemetry::trace::Span> to_span_ptr(SpanType *ptr)
79 {
80 return nostd::shared_ptr<opentelemetry::trace::Span>{ptr};
81 }
82
83 class TracerProvider;
84
85 /**
86 * @brief Utility template for obtaining Span Name
87 * @tparam T etw::Span
88 * @param t instance of etw::Span
89 * @return Span Name
90 */
91 template <class T>
92 std::string GetName(T &t)
93 {
94 auto sV = t.GetName();
95 return std::string(sV.data(), sV.length());
96 }
97
98 /**
99 * @brief Utility template to obtain Span start time
100 * @tparam T etw::Span
101 * @param t instance of etw::Span
102 * @return Span Start timestamp
103 */
104 template <class T>
105 common::SystemTimestamp GetStartTime(T &t)
106 {
107 return t.GetStartTime();
108 }
109
110 /**
111 * @brief Utility template to obtain Span end time
112 * @tparam T etw::Span
113 * @param t instance of etw::Span
114 * @return Span Stop timestamp
115 */
116 template <class T>
117 common::SystemTimestamp GetEndTime(T &t)
118 {
119 return t.GetEndTime();
120 }
121
122 class Properties;
123
124 /**
125 * @brief Utility template to store Attributes on Span
126 * @tparam T etw::Span
127 * @param instance instance of etw::Span
128 * @param t Properties to store as Attributes
129 */
130 template <class T>
131 void SetSpanAttributes(T &instance, Properties &t)
132 {
133 instance.SetAttributes(t);
134 }
135
136 /**
137 * @brief Utility template to obtain Span Attributes
138 * @tparam T etw::Span
139 * @param instance instance of etw::Span
140 * @return ref to Span Attributes
141 */
142 template <class T>
143 Properties &GetSpanAttributes(T &instance)
144 {
145 return instance.GetAttributes();
146 }
147
148 template <class T>
149 void UpdateStatus(T &t, Properties &props)
150 {
151 t.UpdateStatus(props);
152 }
153
154 /**
155 * @brief Tracer class that allows to send spans to ETW Provider.
156 */
157 class Tracer : public opentelemetry::trace::Tracer
158 {
159
160 /**
161 * @brief Parent provider of this Tracer
162 */
163 etw::TracerProvider &tracerProvider_;
164
165 /**
166 * @brief ProviderId (Name or GUID)
167 */
168 std::string provId;
169
170 /**
171 * @brief Encoding (Manifest, MessagePack or XML)
172 */
173 ETWProvider::EventFormat encoding;
174
175 /**
176 * @brief Provider Handle
177 */
178 ETWProvider::Handle &provHandle;
179
180 opentelemetry::trace::TraceId traceId_;
181
182 std::atomic<bool> isClosed_{true};
183
184 /**
185 * @brief ETWProvider is a singleton that aggregates all ETW writes.
186 * @return
187 */
188 static ETWProvider &etwProvider()
189 {
190 static ETWProvider instance; // C++11 magic static
191 return instance;
192 }
193
194 /**
195 * @brief Internal method that allows to populate Links to other Spans.
196 * Span links are in hexadecimal representation, comma-separated in their
197 * order of appearance.
198 *
199 * @param attributes
200 * @param links
201 */
202 virtual void DecorateLinks(Properties &attributes,
203 const opentelemetry::trace::SpanContextKeyValueIterable &links) const
204 {
205 // Add `SpanLinks` attribute if the list is not empty
206 if (links.size())
207 {
208 size_t idx = 0;
209 std::string linksValue;
210 links.ForEachKeyValue(
211 [&](opentelemetry::trace::SpanContext ctx, const common::KeyValueIterable &) {
212 if (!linksValue.empty())
213 {
214 linksValue += ',';
215 linksValue += ToLowerBase16(ctx.span_id());
216 }
217 idx++;
218 return true;
219 });
220 attributes[ETW_FIELD_SPAN_LINKS] = linksValue;
221 }
222 }
223
224 /**
225 * @brief Allow our friendly etw::Span to end itself on Tracer.
226 * @param span
227 * @param
228 */
229 virtual void EndSpan(const Span &span,
230 const opentelemetry::trace::Span *parentSpan = nullptr,
231 const opentelemetry::trace::EndSpanOptions & = {})
232 {
233 const auto &cfg = GetConfiguration(tracerProvider_);
234 const opentelemetry::trace::Span &spanBase =
235 reinterpret_cast<const opentelemetry::trace::Span &>(span);
236 auto spanContext = spanBase.GetContext();
237
238 // Populate Span with presaved attributes
239 Span &currentSpan = const_cast<Span &>(span);
240 Properties evt = GetSpanAttributes(currentSpan);
241 evt[ETW_FIELD_NAME] = GetName(span);
242
243 if (cfg.enableSpanId)
244 {
245 evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
246 }
247
248 if (cfg.enableTraceId)
249 {
250 evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
251 }
252
253 // Populate ActivityId if enabled
254 GUID ActivityId;
255 LPGUID ActivityIdPtr = nullptr;
256 if (cfg.enableActivityId)
257 {
258 if (CopySpanIdToActivityId(spanBase.GetContext().span_id(), ActivityId))
259 {
260 ActivityIdPtr = &ActivityId;
261 }
262 }
263
264 // Populate RelatedActivityId if enabled
265 GUID RelatedActivityId;
266 LPGUID RelatedActivityIdPtr = nullptr;
267 if (cfg.enableRelatedActivityId)
268 {
269 if (parentSpan != nullptr)
270 {
271 if (CopySpanIdToActivityId(parentSpan->GetContext().span_id(), RelatedActivityId))
272 {
273 RelatedActivityIdPtr = &RelatedActivityId;
274 }
275 }
276 }
277
278 if (cfg.enableActivityTracking)
279 {
280 // TODO: check what EndSpanOptions should be supported for this exporter.
281 // The only option available currently (end_steady_time) does not apply.
282 //
283 // This event on Span Stop enables generation of "non-transactional"
284 // OpCode=Stop in alignment with TraceLogging Activity "EventSource"
285 // spec.
286 etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 2, encoding);
287 }
288
289 {
290 // Now since the span has ended, we need to emit the "Span" event that
291 // contains the entire span information, attributes, time, etc. on it.
292 evt[ETW_FIELD_NAME] = ETW_VALUE_SPAN;
293 evt[ETW_FIELD_PAYLOAD_NAME] = GetName(span);
294
295 // Add timing details in ISO8601 format, which adequately represents
296 // the actual time, taking Timezone into consideration. This is NOT
297 // local time, but rather UTC time (Z=0).
298 std::chrono::system_clock::time_point startTime = GetStartTime(currentSpan);
299 std::chrono::system_clock::time_point endTime = GetEndTime(currentSpan);
300 int64_t startTimeMs =
301 std::chrono::duration_cast<std::chrono::milliseconds>(startTime.time_since_epoch())
302 .count();
303 int64_t endTimeMs =
304 std::chrono::duration_cast<std::chrono::milliseconds>(endTime.time_since_epoch()).count();
305
306 // It may be more optimal to enable passing timestamps as UTC milliseconds
307 // since Unix epoch instead of string, but that implies additional tooling
308 // is needed to convert it, rendering it NOT human-readable.
309 evt[ETW_FIELD_STARTTIME] = utils::formatUtcTimestampMsAsISO8601(startTimeMs);
310 #ifdef ETW_FIELD_ENDTTIME
311 // ETW has its own precise timestamp at envelope layer for every event.
312 // However, in some scenarios it is easier to deal with ISO8601 strings.
313 // In that case we convert the app-created timestamp and place it into
314 // Payload[$ETW_FIELD_TIME] field. The option configurable at compile-time.
315 evt[ETW_FIELD_ENDTTIME] = utils::formatUtcTimestampMsAsISO8601(endTimeMs);
316 #endif
317 // Duration of Span in milliseconds
318 evt[ETW_FIELD_DURATION] = endTimeMs - startTimeMs;
319 // Presently we assume that all spans are server spans
320 evt[ETW_FIELD_SPAN_KIND] = uint32_t(opentelemetry::trace::SpanKind::kServer);
321 UpdateStatus(currentSpan, evt);
322 etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 0, encoding);
323 }
324 }
325
326 const opentelemetry::trace::TraceId &trace_id() { return traceId_; }
327
328 friend class Span;
329
330 /**
331 * @brief Init a reference to etw::ProviderHandle
332 * @return Provider Handle
333 */
334 ETWProvider::Handle &initProvHandle()
335 {
336 isClosed_ = false;
337 return etwProvider().open(provId, encoding);
338 }
339
340 public:
341 /**
342 * @brief Tracer constructor
343 * @param parent Parent TraceProvider
344 * @param providerId ProviderId - Name or GUID
345 * @param encoding ETW encoding format to use.
346 */
347 Tracer(etw::TracerProvider &parent,
348 nostd::string_view providerId = "",
349 ETWProvider::EventFormat encoding = ETWProvider::EventFormat::ETW_MANIFEST)
350 : opentelemetry::trace::Tracer(),
351 tracerProvider_(parent),
352 provId(providerId.data(), providerId.size()),
353 encoding(encoding),
354 provHandle(initProvHandle())
355 {
356 // Generate random GUID
357 GUID trace_id;
358 CoCreateGuid(&trace_id);
359 // Populate TraceId of the Tracer with the above GUID
360 const auto *traceIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(trace_id));
361 nostd::span<const uint8_t, opentelemetry::trace::TraceId::kSize> traceIdBytes(
362 traceIdPtr, traceIdPtr + opentelemetry::trace::TraceId::kSize);
363 traceId_ = opentelemetry::trace::TraceId(traceIdBytes);
364 }
365
366 /**
367 * @brief Start Span
368 * @param name Span name
369 * @param attributes Span attributes
370 * @param links Span links
371 * @param options Span options
372 * @return
373 */
374 nostd::shared_ptr<opentelemetry::trace::Span> StartSpan(
375 nostd::string_view name,
376 const common::KeyValueIterable &attributes,
377 const opentelemetry::trace::SpanContextKeyValueIterable &links,
378 const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override
379 {
380 #ifdef OPENTELEMETRY_RTTI_ENABLED
381 common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
382 Properties *evt = dynamic_cast<Properties *>(&attribs);
383 if (evt != nullptr)
384 {
385 // Pass as a reference to original modifyable collection without creating a copy
386 return StartSpan(name, *evt, links, options);
387 }
388 #endif
389 Properties evtCopy = attributes;
390 return StartSpan(name, evtCopy, links, options);
391 }
392
393 /**
394 * @brief Start Span
395 * @param name Span name
396 * @param attributes Span attributes
397 * @param links Span links
398 * @param options Span options
399 * @return
400 */
401 virtual nostd::shared_ptr<opentelemetry::trace::Span> StartSpan(
402 nostd::string_view name,
403 Properties &evt,
404 const opentelemetry::trace::SpanContextKeyValueIterable &links,
405 const opentelemetry::trace::StartSpanOptions &options = {}) noexcept
406 {
407 const auto &cfg = GetConfiguration(tracerProvider_);
408
409 // Parent Context:
410 // - either use current span
411 // - or attach to parent SpanContext specified in options
412 opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext();
413 if (nostd::holds_alternative<opentelemetry::trace::SpanContext>(options.parent))
414 {
415 auto span_context = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
416 if (span_context.IsValid())
417 {
418 parentContext = span_context;
419 }
420 }
421
422 // Populate Etw.RelatedActivityId at envelope level if enabled
423 GUID RelatedActivityId;
424 LPCGUID RelatedActivityIdPtr = nullptr;
425 if (cfg.enableAutoParent)
426 {
427 if (cfg.enableRelatedActivityId)
428 {
429 if (CopySpanIdToActivityId(parentContext.span_id(), RelatedActivityId))
430 {
431 RelatedActivityIdPtr = &RelatedActivityId;
432 }
433 }
434 }
435
436 // This template pattern allows us to forward-declare the etw::Span,
437 // create an instance of it, then assign it to tracer::Span result.
438 auto currentSpan = new_span<Span, Tracer>(this, name, options);
439 nostd::shared_ptr<opentelemetry::trace::Span> result = to_span_ptr<Span>(currentSpan);
440
441 auto spanContext = result->GetContext();
442
443 // Decorate with additional standard fields
444 std::string eventName = name.data();
445
446 // Populate Etw.EventName attribute at envelope level
447 evt[ETW_FIELD_NAME] = eventName;
448
449 // Populate Payload["SpanId"] attribute
450 // Populate Payload["ParentSpanId"] attribute if parent Span is valid
451 if (cfg.enableSpanId)
452 {
453 if (parentContext.IsValid())
454 {
455 evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id());
456 }
457 evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
458 }
459
460 // Populate Etw.Payload["TraceId"] attribute
461 if (cfg.enableTraceId)
462 {
463 evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
464 }
465
466 // Populate Etw.ActivityId at envelope level if enabled
467 GUID ActivityId;
468 LPCGUID ActivityIdPtr = nullptr;
469 if (cfg.enableActivityId)
470 {
471 if (CopySpanIdToActivityId(result.get()->GetContext().span_id(), ActivityId))
472 {
473 ActivityIdPtr = &ActivityId;
474 }
475 }
476
477 // Links
478 DecorateLinks(evt, links);
479
480 // Remember Span attributes to be passed down to ETW on Span end
481 SetSpanAttributes(*currentSpan, evt);
482
483 if (cfg.enableActivityTracking)
484 {
485 // TODO: add support for options that are presently ignored :
486 // - options.kind
487 // - options.start_steady_time
488 // - options.start_system_time
489 etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 1, encoding);
490 }
491
492 return result;
493 }
494
495 /**
496 * @brief Force flush data to Tracer, spending up to given amount of microseconds to flush.
497 * NOTE: this method has no effect for the realtime streaming Tracer.
498 *
499 * @param timeout Allow Tracer to drop data if timeout is reached
500 * @return
501 */
502 void ForceFlushWithMicroseconds(uint64_t) noexcept override {}
503
504 /**
505 * @brief Close tracer, spending up to given amount of microseconds to flush and close.
506 * NOTE: This method decrements the reference count on current ETW Provider Handle and
507 * closes it if reference count on that provider handle is zero.
508 *
509 * @param timeout Allow Tracer to drop data if timeout is reached.
510 * @return
511 */
512 void CloseWithMicroseconds(uint64_t) noexcept override
513 {
514 // Close once only
515 if (!isClosed_.exchange(true))
516 {
517 etwProvider().close(provHandle);
518 }
519 }
520
521 /**
522 * @brief Add event data to span associated with tracer.
523 * @param span Parent span.
524 * @param name Event name.
525 * @param timestamp Event timestamp.
526 * @param attributes Event attributes.
527 * @return
528 */
529 void AddEvent(opentelemetry::trace::Span &span,
530 nostd::string_view name,
531 common::SystemTimestamp timestamp,
532 const common::KeyValueIterable &attributes) noexcept
533 {
534 #ifdef OPENTELEMETRY_RTTI_ENABLED
535 common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
536 Properties *evt = dynamic_cast<Properties *>(&attribs);
537 if (evt != nullptr)
538 {
539 // Pass as a reference to original modifyable collection without creating a copy
540 return AddEvent(span, name, timestamp, *evt);
541 }
542 #endif
543 // Pass a copy converted to Properties object on stack
544 Properties evtCopy = attributes;
545 return AddEvent(span, name, timestamp, evtCopy);
546 }
547
548 /**
549 * @brief Add event data to span associated with tracer.
550 * @param span Parent span.
551 * @param name Event name.
552 * @param timestamp Event timestamp.
553 * @param attributes Event attributes.
554 * @return
555 */
556 void AddEvent(opentelemetry::trace::Span &span,
557 nostd::string_view name,
558 common::SystemTimestamp timestamp,
559 Properties &evt) noexcept
560 {
561 // TODO: respect originating timestamp. Do we need to reserve
562 // a special 'Timestamp' field or is it an overkill? The delta
563 // between when `AddEvent` API is called and when ETW layer
564 // timestamp is appended is nanos- to micros-, thus handling
565 // the explicitly provided timestamp is only necessary in case
566 // if a process wants to submit back-dated or future-dated
567 // timestamp. Unless there is a strong ask from any ETW customer
568 // to have it, this feature (custom timestamp) remains unimplemented.
569 (void)timestamp;
570
571 const auto &cfg = GetConfiguration(tracerProvider_);
572
573 evt[ETW_FIELD_NAME] = name.data();
574
575 const auto &spanContext = span.GetContext();
576 if (cfg.enableSpanId)
577 {
578 evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
579 }
580
581 if (cfg.enableTraceId)
582 {
583 evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
584 }
585
586 LPGUID ActivityIdPtr = nullptr;
587 GUID ActivityId;
588 if (cfg.enableActivityId)
589 {
590 if (CopySpanIdToActivityId(spanContext.span_id(), ActivityId))
591 {
592 ActivityIdPtr = &ActivityId;
593 }
594 }
595
596 #ifdef HAVE_FIELD_TIME
597 {
598 auto timeNow = std::chrono::system_clock::now().time_since_epoch();
599 auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow).count();
600 evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis);
601 }
602 #endif
603
604 etwProvider().write(provHandle, evt, ActivityIdPtr, nullptr, 0, encoding);
605 }
606
607 /**
608 * @brief Add event data to span associated with tracer.
609 * @param span Span.
610 * @param name Event name.
611 * @param timestamp Event timestamp.
612 * @return
613 */
614 void AddEvent(opentelemetry::trace::Span &span,
615 nostd::string_view name,
616 common::SystemTimestamp timestamp) noexcept
617 {
618 AddEvent(span, name, timestamp, sdk::GetEmptyAttributes());
619 }
620
621 /**
622 * @brief Add event data to span associated with tracer.
623 * @param span Spab.
624 * @param name Event name.
625 */
626 void AddEvent(opentelemetry::trace::Span &span, nostd::string_view name)
627 {
628 AddEvent(span, name, std::chrono::system_clock::now(), sdk::GetEmptyAttributes());
629 }
630
631 /**
632 * @brief Tracer destructor.
633 */
634 virtual ~Tracer() { CloseWithMicroseconds(0); }
635 };
636
637 /**
638 * @brief etw::Span allows to send event data to ETW listener.
639 */
640 class Span : public opentelemetry::trace::Span
641 {
642 protected:
643 friend class Tracer;
644
645 /**
646 * @brief Span properties are attached on "Span" event on end of Span.
647 */
648 Properties attributes_;
649
650 common::SystemTimestamp start_time_;
651 common::SystemTimestamp end_time_;
652
653 opentelemetry::trace::StatusCode status_code_{opentelemetry::trace::StatusCode::kUnset};
654 std::string status_description_;
655
656 /**
657 * @brief Owner Tracer of this Span
658 */
659 Tracer &owner_;
660
661 /**
662 * @brief Span name.
663 */
664 nostd::string_view name_;
665
666 /**
667 * @brief Attribute indicating that the span has ended.
668 */
669 std::atomic<bool> has_ended_{false};
670
671 /**
672 * @brief Attribute indicating that the span has started.
673 */
674 std::atomic<bool> has_started_{false};
675
676 /**
677 * @brief Parent Span of this nested Span (optional)
678 */
679 Span *parent_{nullptr};
680
681 /**
682 * @brief Get Parent Span of this nested Span.
683 * @return Pointer to Parent or nullptr if no Parent.
684 */
685 Span *GetParent() const { return parent_; }
686
687 opentelemetry::trace::SpanContext context_;
688
689 const opentelemetry::trace::SpanContext CreateContext()
690 {
691 GUID activity_id;
692 // Generate random GUID
693 CoCreateGuid(&activity_id);
694 const auto *activityIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(activity_id));
695
696 // Populate SpanId with that GUID
697 nostd::span<const uint8_t, opentelemetry::trace::SpanId::kSize> spanIdBytes(
698 activityIdPtr, activityIdPtr + opentelemetry::trace::SpanId::kSize);
699 const opentelemetry::trace::SpanId spanId(spanIdBytes);
700
701 // Inherit trace_id from Tracer
702 const opentelemetry::trace::TraceId traceId{owner_.trace_id()};
703 // TODO: TraceFlags are not supported by ETW exporter.
704 const opentelemetry::trace::TraceFlags flags{0};
705 // TODO: Remote parent is not supported by ETW exporter.
706 const bool hasRemoteParent = false;
707 return opentelemetry::trace::SpanContext{traceId, spanId, flags, hasRemoteParent};
708 }
709
710 public:
711 /**
712 * @brief Update Properties object with current Span status
713 * @param evt
714 */
715 void UpdateStatus(Properties &evt)
716 {
717 /* Should we avoid populating this extra field if status is unset? */
718 if ((status_code_ == opentelemetry::trace::StatusCode::kUnset) ||
719 (status_code_ == opentelemetry::trace::StatusCode::kOk))
720 {
721 evt[ETW_FIELD_SUCCESS] = "True";
722 evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_);
723 evt[ETW_FIELD_STATUSMESSAGE] = status_description_;
724 }
725 else
726 {
727 evt[ETW_FIELD_SUCCESS] = "False";
728 evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_);
729 evt[ETW_FIELD_STATUSMESSAGE] = status_description_;
730 }
731 }
732
733 /**
734 * @brief Get start time of this Span.
735 * @return
736 */
737 common::SystemTimestamp GetStartTime() { return start_time_; }
738
739 /**
740 * @brief Get end time of this Span.
741 * @return
742 */
743 common::SystemTimestamp GetEndTime() { return end_time_; }
744
745 /**
746 * @brief Get Span Name.
747 * @return Span Name.
748 */
749 nostd::string_view GetName() const { return name_; }
750
751 /**
752 * @brief Span constructor
753 * @param owner Owner Tracer
754 * @param name Span name
755 * @param options Span options
756 * @param parent Parent Span (optional)
757 * @return
758 */
759 Span(Tracer &owner,
760 nostd::string_view name,
761 const opentelemetry::trace::StartSpanOptions &options,
762 Span *parent = nullptr) noexcept
763 : opentelemetry::trace::Span(),
764 start_time_(std::chrono::system_clock::now()),
765 owner_(owner),
766 parent_(parent),
767 context_(CreateContext())
768 {
769 name_ = name;
770 UNREFERENCED_PARAMETER(options);
771 }
772
773 /**
774 * @brief Span Destructor
775 */
776 ~Span() { End(); }
777
778 /**
779 * @brief Add named event with no attributes.
780 * @param name Event name.
781 * @return
782 */
783 void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); }
784
785 /**
786 * @brief Add named event with custom timestamp.
787 * @param name
788 * @param timestamp
789 * @return
790 */
791 void AddEvent(nostd::string_view name, common::SystemTimestamp timestamp) noexcept override
792 {
793 owner_.AddEvent(*this, name, timestamp);
794 }
795
796 /**
797 * @brief Add named event with custom timestamp and attributes.
798 * @param name Event name.
799 * @param timestamp Event timestamp.
800 * @param attributes Event attributes.
801 * @return
802 */
803 void AddEvent(nostd::string_view name,
804 common::SystemTimestamp timestamp,
805 const common::KeyValueIterable &attributes) noexcept override
806 {
807 owner_.AddEvent(*this, name, timestamp, attributes);
808 }
809
810 /**
811 * @brief Set Span status
812 * @param code Span status code.
813 * @param description Span description.
814 * @return
815 */
816 void SetStatus(opentelemetry::trace::StatusCode code,
817 nostd::string_view description) noexcept override
818 {
819 status_code_ = code;
820 status_description_ = description.data();
821 }
822
823 void SetAttributes(Properties attributes) { attributes_ = attributes; }
824
825 /**
826 * @brief Obtain span attributes specified at Span start.
827 * NOTE: please consider that this method is NOT thread-safe.
828 *
829 * @return ref to Properties collection
830 */
831 Properties &GetAttributes() { return attributes_; }
832
833 /**
834 * @brief Sets an attribute on the Span. If the Span previously contained a mapping
835 * for the key, the old value is replaced.
836 *
837 * @param key
838 * @param value
839 * @return
840 */
841 void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override
842 {
843 // don't override fields propagated from span data.
844 if (key == ETW_FIELD_NAME || key == ETW_FIELD_SPAN_ID || key == ETW_FIELD_TRACE_ID)
845 {
846 return;
847 }
848 attributes_[std::string{key}].FromAttributeValue(value);
849 }
850
851 /**
852 * @brief Update Span name.
853 *
854 * NOTE: this method is a no-op for streaming implementation.
855 * We cannot change the Span name after it started streaming.
856 *
857 * @param name
858 * @return
859 */
860 void UpdateName(nostd::string_view) noexcept override
861 {
862 // We can't do that!
863 // name_ = name;
864 }
865
866 /**
867 * @brief End Span.
868 * @param EndSpanOptions
869 * @return
870 */
871 void End(const opentelemetry::trace::EndSpanOptions &options = {}) noexcept override
872 {
873 end_time_ = std::chrono::system_clock::now();
874
875 if (!has_ended_.exchange(true))
876 {
877 owner_.EndSpan(*this, parent_, options);
878 }
879 }
880
881 /**
882 * @brief Obtain SpanContext
883 * @return
884 */
885 opentelemetry::trace::SpanContext GetContext() const noexcept override { return context_; }
886
887 /**
888 * @brief Check if Span is recording data.
889 * @return
890 */
891 bool IsRecording() const noexcept override
892 {
893 // For streaming implementation this should return the state of ETW Listener.
894 // In certain unprivileged environments, ex. containers, it is impossible
895 // to determine if a listener is registered. Thus, we always return true.
896 return true;
897 }
898
899 virtual void SetToken(nostd::unique_ptr<context::Token> &&token) noexcept
900 {
901 // TODO: not implemented
902 UNREFERENCED_PARAMETER(token);
903 }
904
905 /// <summary>
906 /// Get Owner tracer of this Span
907 /// </summary>
908 /// <returns></returns>
909 opentelemetry::trace::Tracer &tracer() const noexcept { return this->owner_; }
910 };
911
912 /**
913 * @brief ETW TracerProvider
914 */
915 class TracerProvider : public opentelemetry::trace::TracerProvider
916 {
917 public:
918 /**
919 * @brief TracerProvider options supplied during initialization.
920 */
921 TelemetryProviderConfiguration config_;
922
923 /**
924 * @brief Construct instance of TracerProvider with given options
925 * @param options Configuration options
926 */
927 TracerProvider(TelemetryProviderOptions options) : opentelemetry::trace::TracerProvider()
928 {
929 // By default we ensure that all events carry their with TraceId and SpanId
930 GetOption(options, "enableTraceId", config_.enableTraceId, true);
931 GetOption(options, "enableSpanId", config_.enableSpanId, true);
932
933 // Backwards-compatibility option that allows to reuse ETW-specific parenting described here:
934 // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.diagnostics.loggingoptions.relatedactivityid
935 // https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwritetransfer
936
937 // Emit separate events compatible with TraceLogging Activity/Start and Activity/Stop
938 // format for every Span emitted.
939 GetOption(options, "enableActivityTracking", config_.enableActivityTracking, false);
940
941 // Map current `SpanId` to ActivityId - GUID that uniquely identifies this activity. If NULL,
942 // ETW gets the identifier from the thread local storage. For details on getting this
943 // identifier, see EventActivityIdControl.
944 GetOption(options, "enableActivityId", config_.enableActivityId, false);
945
946 // Map parent `SpanId` to RelatedActivityId - Activity identifier from the previous
947 // component. Use this parameter to link your component's events to the previous component's
948 // events.
949 GetOption(options, "enableRelatedActivityId", config_.enableRelatedActivityId, false);
950
951 // When a new Span is started, the current span automatically becomes its parent.
952 GetOption(options, "enableAutoParent", config_.enableAutoParent, false);
953
954 // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc.
955 config_.encoding = GetEncoding(options);
956 }
957
958 TracerProvider() : opentelemetry::trace::TracerProvider()
959 {
960 config_.enableTraceId = true;
961 config_.enableSpanId = true;
962 config_.enableActivityId = false;
963 config_.enableActivityTracking = false;
964 config_.enableRelatedActivityId = false;
965 config_.enableAutoParent = false;
966 config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST;
967 }
968
969 /**
970 * @brief Obtain ETW Tracer.
971 * @param name ProviderId (instrumentation name) - Name or GUID
972 *
973 * @param args Additional arguments that controls `codec` of the provider.
974 * Possible values are:
975 * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events.
976 * - "MSGPACK" - MessagePack-encoded binary payload ETW events.
977 * - "XML" - XML events (reserved for future use)
978 * @return
979 */
980 nostd::shared_ptr<opentelemetry::trace::Tracer> GetTracer(
981 nostd::string_view name,
982 nostd::string_view args = "",
983 nostd::string_view schema_url = "") noexcept override
984 {
985 UNREFERENCED_PARAMETER(args);
986 UNREFERENCED_PARAMETER(schema_url);
987 ETWProvider::EventFormat evtFmt = config_.encoding;
988 return nostd::shared_ptr<opentelemetry::trace::Tracer>{new (std::nothrow)
989 Tracer(*this, name, evtFmt)};
990 }
991 };
992
993 } // namespace etw
994 } // namespace exporter
995 OPENTELEMETRY_END_NAMESPACE