OpenTelemetry is a collection of tools, APIs, and SDKs. Use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) to help you analyze your software’s performance and behavior.
SDK Setup
Right now, there is only one package for OpenTelemetry on flutter, it is https://pub.dev/packages/opentelemetry. This package is not null-safety, so we need to add --no-sound-null-safety
whenever we want to run our app.
To set up this SDK in our app, these items are required:
Exporter: this is the one that will send ours to our OTLP Receiver (
CollectorExporter
) or StdOut (ConsoleExporter
). In this case, we are using CollectorExporter.SpanProcessor: It will handle how our traces will be sent. It requires one Exporter. This package provides us with two types:
SimpleSpanProcessor: send the trace whenever a new trace is emitted.
BatchSpanProcessor: collect the traces within the configured delay then send it.
TracerProvider: Registers span processors, and is responsible for managing any tracers. It requires one or more SpanProcessor.
To simplify and isolate the non-null safety, we create a wrapper class called BaseOtel.
Base OTel
Our wrapper class isolates and simplifies the OTel trace usages.
NewRelicExporterClient
An HttpClient provides us to include the API Key so we can send our traces to NewRelic. It will be used as the client of CollectorExporter. It is an example client if our OTLP Receiver requires some authentication.
BaseOtel
The wrapper class migrates from non-null-safety to more null-safety.
getCollectorExporter: create a CollectorExporter by providing the Server URI.
getNewRelicExporter: same as getCollectorExporter, but it uses NewRelicExporterClient as the HTTP client.
getConsoleExporter: create a ConsoleExporter.
context (getter): return current OTel context.
withSpan: record a trace that runs an async process or function.
withSpanSync: same as withSpan, but it runs a sync process or function.
getSimpleSpanProcessor: create a SimpleSpanProcessor, it requires a SpanExporter.
getBatchSpanProcessor: create a BatchSpanProcessor, it requires a SpanExporter.
getProvider: create a TracerProvider by providing the SpanProcessor(s).
getGlobalTracer: return current global tracer.
globalTracer (setter): set a TracerProvider to be global.
customerId (setter): set/unset customerId as a common span attribute and set it to the current span (if exists).
_spanAttributeMapping: a mapping function that converts MapEntry<String, dynamic> to supported SpanAttribute. Returns null if value null, throws an
UnsupportedError
if the value is not String, List<String>, bool, List<bool>, int, List<int>, double, List<double>.
Collecting Span
The SDK provides two ways to collect the span, they are Passing The Span or Use The Current Span. We choose to Use The Current Span, this is the simplest way to collect the span. In general to collect the span we only need to create the span from a tracer.
final tracer = globalTracerProvider.getTracer('instrumentationName'); final span = tracer.startSpan('doingWork'); // Our Code span.end();
But with our wrapper class, we can use withSpan or withSpanSync.
withSpan
Future<List<AccountEntity>> getAll({ bool forceRemote = false, }) { return BaseOtel.withSpan( instrumentationName: _instrumentationName, spanName: 'getAll', fn: () async { final accounts = await accountRepository.getAll( forceRemote: forceRemote, ); return accounts; }, ); }
We need to wrap up our Bloc, Usecase, Repository Implementation, and Datasource functions. So we can record the span.
withSpanSync
bool doWork() { return BaseOtel.withSpanSync( instrumentationName: _instrumentationName, spanName: 'getAll', fn: () { // sync process }, ); }
Result:
Summary:
Pros | Const |
---|---|
Export/send traces processes are relatively fast (using protobuf). | Not null-safety. (Hopefully they migrate it soon https://github.com/Workiva/opentelemetry-dart/issues/79 ) |
Since BE already use OTel, so it will centralized the way we trace App Behavior. | No auto-instrumentation. Need to wrap all our functions to be recorded. |
Provide as the waterfall graph and the execution time. | |
Can trace the BE call to App Behavior | |
Can trace widget render time (Need to figure out how to get the flutter render event first) |
Attachments:
image-20221205-094953.png (image/png)
image-20221205-095037.png (image/png)
image-20221205-095124.png (image/png)