Tracing
Module for collecting application trace according to [OpenTelemetry] standard(https://opentelemetry.io/docs/what-is-opentelemetry/) and export trace by gRPC in OTLP format.
For a step-by-step walkthrough before the reference details, see Observability.
gRPC¶
Module allows trace collection using gRPC protocol by means of GrpcSender.
Зависимость build.gradle:
Модуль:
HTTP¶
Module allows to collect trace using HTTP protocol by means of HttpSender.
Зависимость build.gradle:
Модуль:
Configuration¶
endpoint is the only a required field, attributes from the attributes field will be sent with each span.
Parameters described in the OpentelemetryGrpcExporterConfig/OpentelemetryHttpExporterConfig and OpentelemetryResourceConfig classes:
tracing {
exporter {
endpoint = "http://localhost:4317" //(1)!
connectTimeout = "60s" //(2)!
exportTimeout = "3s" //(3)!
scheduleDelay = "2s" //(4)!
maxExportBatchSize = 512 //(5)!
maxQueueSize = 2048 //(6)!
batchExportTimeout = "30s" //(7)!
compression = "gzip" //(8)!
exportUnsampledSpans = false //(9)!
retry {
maxAttempts = 5 //(10)!
initialBackoff = "1s" //(11)!
maxBackoff = "5s" //(12)!
backoffMultiplier = 1.5 //(13)!
}
}
attributes { //(14)!
"service.name" = "example-service"
"service.namespace" = "kora"
}
}
- URL from OpenTelemetry service collector (mandatory)
- Time to wait for connection to exporter
- Maximum time to wait for telemetry processing by collector
- Time between exporting telemetry to the collector
- Maximum number of telemetry within one export
- Maximum queue size of unsent telemetry
- Maximum waiting time for export
- Telemetry compression mechanism when exporting
- Whether to export unsampled telemetry
- Maximum number of export attempts
- Initial value of waiting time before next export attempt
- Maximum wait value before next export attempt
- Waiting delay value multiplier
- Additional telemetry attributes
Translated with DeepL.com (free version)
tracing:
exporter:
endpoint: http://localhost:4317 #(1)!
connectTimeout: 60s #(2)!
exportTimeout: 3s #(3)!
scheduleDelay: 2s #(4)!
maxExportBatchSize: 512 #(5)!
maxQueueSize: 2048 #(6)!
batchExportTimeout: 30s #(7)!
compression: gzip #(8)!
exportUnsampledSpans: false #(9)!
retry:
maxAttempts: 5 #(10)!
initialBackoff: 1s #(11)!
maxBackoff: 5s #(12)!
backoffMultiplier: 1.5 #(13)!
attributes: #(14)!
service.name: example-service
service.namespace: kora
- URL from OpenTelemetry service collector (mandatory)
- Time to wait for connection to exporter
- Maximum time to wait for telemetry processing by collector
- Time between exporting telemetry to the collector
- Maximum number of telemetry within one export
- Maximum queue size of unsent telemetry
- Maximum waiting time for export
- Telemetry compression mechanism when exporting
- Whether to export unsampled telemetry
- Maximum number of export attempts
- Initial value of waiting time before next export attempt
- Maximum wait value before next export attempt
- Waiting delay value multiplier
- Additional telemetry attributes
Trace collection configuration parameters are described in modules that include trace collection, e.g. HTTP server, HTTP client, etc.
Tracing context¶
Obtain the current tracing Span, you can use the getSpan method in OpentelemetryContext:
Obtain the current trace ID, you can use the getTraceId() method in OpentelemetryContext:
Tracing sync¶
In addition to automatically created spans, you can use the Tracer object from the dependency container.
You can create a span with the current one in parent as follows:
@Component
public final class MyService {
private final io.opentelemetry.api.trace.Tracer tracer;
public MyService(Tracer tracer) {
this.tracer = tracer;
}
public String doTraceWork() {
var ctx = ru.tinkoff.kora.common.Context.current();
var otctx = OpentelemetryContext.get(ctx);
var span = tracer.spanBuilder("myOperation")
.setParent(otctx.getContext())
.startSpan();
OpentelemetryContext.set(ctx, otctx.add(span));
try {
var result = doWork();
span.setStatus(StatusCode.OK);
return result;
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getMessage());
throw e;
} finally {
span.end();
OpentelemetryContext.set(ctx, otctx);
}
}
public String doWork() {
// do some work
}
}
@Component
class MyService(private val tracer: io.opentelemetry.api.trace.Tracer) {
fun doTraceWork(): String {
val ctx = ru.tinkoff.kora.common.Context.current()
val otctx = OpentelemetryContext.get(ctx)
val span = tracer.spanBuilder("myOperation")
.setParent(otctx.getContext())
.startSpan()
OpentelemetryContext.set(ctx, otctx.add(span))
try {
val result = doWork()
span.setStatus(StatusCode.OK)
return result
} catch (e: Exception) {
span.recordException(e)
span.setStatus(StatusCode.ERROR, e.message)
throw e
} finally {
span.end()
OpentelemetryContext.set(ctx, otctx)
}
}
fun doWork(): String = // do some work
}
Асинхронная трассировка¶
In addition to spans automatically created by the framework, you can use the Tracer object from the container to create your own traces. The main challenge lies in correctly propagating the context Fork to another execution thread to ensure the trace works properly.
To create a trace for asynchronous code inherited from the current parent context, you can do the following:
Example is shown for the CompletableStage asynchronous approach:
@Component
public final class MyService {
private final io.opentelemetry.api.trace.Tracer tracer;
public MyService(Tracer tracer) {
this.tracer = tracer;
}
public CompletionStage<String> doTraceWork() {
var ctx = ru.tinkoff.kora.common.Context.current().fork();
var otctx = OpentelemetryContext.get(ctx);
var span = tracer.spanBuilder("myOperation")
.setParent(otctx.getContext())
.startSpan();
return CompletableFuture.supplyAsync(() -> {
OpentelemetryContext.set(ctx, otctx.add(span));
var result = doWork();
return result;
})
.whenComplete((r, e) -> {
if (e != null) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getMessage());
} else {
span.setStatus(StatusCode.OK);
}
span.end();
});
}
public String doWork() {
// do some work
}
}
Example is shown for the suspend asynchronous approach:
@Component
class MyService(private val tracer: io.opentelemetry.api.trace.Tracer) {
suspend fun doTraceWork(): String {
val ctx = ru.tinkoff.kora.common.Context.current()
val otctx = OpentelemetryContext.get(ctx)
val span = tracer.spanBuilder("myOperation")
.setParent(otctx.getContext())
.startSpan()
OpentelemetryContext.set(ctx, otctx.add(span))
return withContext(Context.Kotlin.asCoroutineContext(ctx)) {
try {
val result = doWork()
span.setStatus(StatusCode.OK)
result
} catch (e: Exception) {
span.recordException(e)
span.setStatus(StatusCode.ERROR, e.message)
throw e
} finally {
span.end()
OpentelemetryContext.set(ctx, otctx)
}
}
}
fun doWork(): String = // do some work
}