Catalog · How the catalog works
opentelemetry-delphi
OpenTelemetry SDK for Delphi: traces and logs over OTLP/HTTP in one unit
- ★ 5 on GitHub
Anyone may press Interesting, signed in or not. It helps others find this project.
README
OpenTelemetry for Delphi
A small OpenTelemetry SDK for Delphi in one file: OpenTelemetry.pas.
It sends traces and logs from your Delphi application to any OpenTelemetry backend (OpenTelemetry Collector, Jaeger, Grafana Tempo / Loki, Datadog, Dynatrace and others).
Features
- Traces: spans, parent and child spans, attributes, events, status
- Record exceptions in a span
- Logs with severity, attributes and trace context
- W3C
traceparentheader: read and write - Samplers, and a ready trace id ratio sampler
- Export with OTLP/HTTP (JSON), with custom HTTP headers (API keys)
- Configuration with standard
OTEL_*environment variables - Export runs in a background thread, in batches
- No external libraries, only Delphi RTL
Requirements
- Delphi 10.3 Rio or newer (tested with Delphi 11, Win32 and Win64)
- Windows (32-bit and 64-bit)
Install
Copy OpenTelemetry.pas to your project and add OpenTelemetry to the uses list.
Example: traces
uses
OpenTelemetry;
var
Provider: ITracerProvider;
Tracer: IOTLPTracer;
Span: IOTLPSpan;
begin
Provider := TTracerProvider.Create('my-service', 'production');
Provider.AddSpanProcessor(TOTLPSpanProcessor.Create(
TOTLPTraceExporter.Create('http://localhost:4318/v1/traces')));
Tracer := Provider.GetTracer('MyApp');
Span := Tracer.StartActiveSpan('load-orders', skInternal);
try
try
Span.SetAttribute('orders.count', Int64(42));
// your code
except
on E: Exception do
begin
Span.RecordException(E);
Span.SetStatus(scError, E.Message);
raise;
end;
end;
finally
Span.EndSpan;
end;
end;One tracer is one trace. Spans that start while another span is active in the same thread become its children.
You can also give a service version:
Provider := TTracerProvider.Create('my-service', 'production', '2.4.1');Example: logs
var
LogProvider: ILoggerProvider;
Logger: IOTLPLogger;
begin
LogProvider := TLoggerProvider.Create('my-service', 'production');
LogProvider.AddLogProcessor(TOTLPLogRecordProcessor.Create(
TOTLPLogExporter.Create('http://localhost:4318/v1/logs')));
Logger := LogProvider.GetLogger('MyApp');
Logger.Emit(OTLP_SEVERITY_INFO, 'INFO', 'Application started');
end;To link a log to a trace, pass Span.TraceId and Span.Id to Emit.
Distributed tracing
Send the trace to another service with an HTTP header:
Request.CustomHeaders['traceparent'] := FormatTraceParent(Span);Continue the trace in the other service:
var TraceId, ParentId: string;
// HeaderValue is the 'traceparent' header of the incoming request
if ParseTraceParent(HeaderValue, TraceId, ParentId) then
Span := Provider.GetTracer('MyServer', TraceId)
.StartActiveSpan('handle-request', skServer, ParentId);FormatTraceParent sets the sampled flag (01) unless a trace id ratio sampler
has already decided not to keep the trace. To read the flag of an incoming header,
use the overload with a fourth parameter:
var Sampled: Boolean;
if ParseTraceParent(HeaderValue, TraceId, ParentId, Sampled) then
...Send to a hosted backend
Many hosted backends need an API key in an HTTP header:
Provider.AddSpanProcessor(TOTLPSpanProcessor.Create(
TOTLPTraceExporter.Create('https://otlp.example.com/v1/traces',
[TOTLPAttribute.Create('Authorization', 'Bearer my-api-key')],
10000))); // timeout in millisecondsThe timeout is the response timeout. The connection timeout is the same value,
but at most 3 seconds, so a collector that is down does not block ForceFlush for long.
Headers that contain a line break are ignored.
Configuration with environment variables
Use FromEnvironment to read the standard OpenTelemetry variables:
Provider.AddSpanProcessor(TOTLPSpanProcessor.Create(TOTLPTraceExporter.FromEnvironment));
LogProvider.AddLogProcessor(TOTLPLogRecordProcessor.Create(TOTLPLogExporter.FromEnvironment));| Variable | Example |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
http://collector:4318 (/v1/traces and /v1/logs are added) |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT |
full URL, used as is |
OTEL_EXPORTER_OTLP_HEADERS (also _TRACES_ / _LOGS_) |
api-key=abc123,x-team=web |
OTEL_EXPORTER_OTLP_TIMEOUT (also _TRACES_ / _LOGS_) |
10000 (milliseconds) |
OTEL_SERVICE_NAME |
my-service |
OTEL_RESOURCE_ATTRIBUTES |
service.namespace=shop,team=backend |
OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG |
traceidratio, 0.1 |
Without these variables the endpoint is http://localhost:4318.
Values that you give in code (service name, version, environment) win over the variables.
Sampling
To keep only a part of the traces, for example 10%:
Provider.AddSpanSampler(TOTLPTraceIdRatioSampler.Create(0.1));The decision is made from the 56 least significant bits of the trace id, as the OpenTelemetry specification says, so all services that use this algorithm keep the same traces, and trace ids made from GUIDs are sampled correctly too. A trace is exported when at least one sampler says yes.
Notes
- Only traces and logs. Metrics are not supported yet.
- Only OTLP/HTTP with JSON. gRPC and protobuf are not supported.
- If the collector is not available, data is dropped for 5 seconds.
- Before the application closes, call
ForceFlushto send all data. - The SDK does not use the sampled flag of an incoming
traceparentfor its own sampling decision; read it withParseTraceParentif you need it.
Self-test
test/OtlpTest.dpr is a console program that compiles the whole public API and checks
ParseKeyValueList, ParseTraceParent, the trace id ratio sampler, FormatTraceParent
and the OTEL_* variables without a network. Build it from the RAD Studio Command Prompt:
cd test
build.cmd
bin64\OtlpTest.exeWith a collector it also sends one trace and one log:
bin64\OtlpTest.exe net http://localhost:4318Changes
- 1.1.0: HTTP headers and timeout for exporters,
FromEnvironment,OTEL_*variables, trace id ratio sampler, real sampled flag inFormatTraceParent,service.version,telemetry.sdk.name/telemetry.sdk.version,deployment.environment.name. - 1.0.0: first version.
Author
Volodymyr Plavshudin
License
MIT License. See LICENSE.

