Line data Source code
1 : // Licensed under the Apache License, Version 2.0
2 : // Copyright 2025, Michael Bushe, All rights reserved.
3 :
4 : import 'package:dartastic_opentelemetry/src/trace/export/span_exporter.dart';
5 : import 'package:dartastic_opentelemetry/src/trace/span.dart';
6 :
7 : /// A simple span exporter that prints spans to the console.
8 : ///
9 : /// This exporter is primarily used for debugging and testing, as it formats
10 : /// and prints span information to the standard output rather than sending them
11 : /// to a telemetry backend.
12 : ///
13 : /// The output includes span name, trace ID, span ID, parent span ID, duration,
14 : /// status, and attributes for better debugging visibility.
15 : class ConsoleExporter extends SpanExporter {
16 1 : @override
17 : Future<void> export(List<Span> spans) async {
18 2 : for (final span in spans) {
19 1 : _printSpan(span);
20 : }
21 : }
22 :
23 1 : void _printSpan(Span span) {
24 1 : final buffer = StringBuffer();
25 1 : buffer.writeln('=== OpenTelemetry Span ===');
26 3 : buffer.writeln('Name: ${span.name}');
27 4 : buffer.writeln('Trace ID: ${span.spanContext.traceId}');
28 4 : buffer.writeln('Span ID: ${span.spanContext.spanId}');
29 :
30 2 : if (span.spanContext.parentSpanId != null &&
31 3 : span.spanContext.parentSpanId!.isValid) {
32 0 : buffer.writeln('Parent Span ID: ${span.spanContext.parentSpanId}');
33 : } else {
34 1 : buffer.writeln('Parent Span ID: (root span)');
35 : }
36 :
37 3 : buffer.writeln('Kind: ${span.kind}');
38 3 : buffer.writeln('Status: ${span.status}');
39 :
40 1 : if (span.statusDescription != null) {
41 0 : buffer.writeln('Status Description: ${span.statusDescription}');
42 : }
43 :
44 4 : buffer.writeln('Start Time: ${span.startTime.toIso8601String()}');
45 :
46 1 : if (span.endTime != null) {
47 4 : buffer.writeln('End Time: ${span.endTime!.toIso8601String()}');
48 3 : final duration = span.endTime!.difference(span.startTime);
49 1 : buffer.writeln(
50 3 : 'Duration: ${duration.inMicroseconds}μs (${duration.inMilliseconds}ms)');
51 : } else {
52 0 : buffer.writeln('End Time: (not ended)');
53 : }
54 :
55 : // Print attributes if any
56 : // ignore: invalid_use_of_visible_for_testing_member
57 2 : final attributes = span.attributes.toList();
58 1 : if (attributes.isNotEmpty) {
59 1 : buffer.writeln('Attributes:');
60 2 : for (final attr in attributes) {
61 4 : buffer.writeln(' ${attr.key}: ${attr.value}');
62 : }
63 : }
64 :
65 : // Print events if any
66 1 : final events = span.spanEvents;
67 1 : if (events != null && events.isNotEmpty) {
68 1 : buffer.writeln('Events:');
69 2 : for (final event in events) {
70 5 : buffer.writeln(' ${event.timestamp.toIso8601String()}: ${event.name}');
71 1 : if (event.attributes != null) {
72 0 : for (final attr in event.attributes!.toList()) {
73 0 : buffer.writeln(' ${attr.key}: ${attr.value}');
74 : }
75 : }
76 : }
77 : }
78 :
79 : // Print links if any
80 1 : final links = span.spanLinks;
81 0 : if (links != null && links.isNotEmpty) {
82 0 : buffer.writeln('Links:');
83 0 : for (final link in links) {
84 0 : buffer.writeln(
85 0 : ' -> Trace: ${link.spanContext.traceId}, Span: ${link.spanContext.spanId}');
86 0 : for (final attr in link.attributes.toList()) {
87 0 : buffer.writeln(' ${attr.key}: ${attr.value}');
88 : }
89 : }
90 : }
91 :
92 1 : buffer.writeln('==========================');
93 1 : print(buffer);
94 : }
95 :
96 1 : @override
97 : Future<void> forceFlush() async {
98 : // ConsoleExporter writes immediately, so nothing to flush
99 : }
100 :
101 72 : @override
102 : Future<void> shutdown() async {
103 : // ConsoleExporter has no resources to clean up
104 : }
105 : }
|