Skip to content

Latest commit

 

History

History
346 lines (277 loc) · 13.3 KB

File metadata and controls

346 lines (277 loc) · 13.3 KB

Модуль для сбора трассировка приложения по стандарту OpenTelemetry и экспорта трассировки по gRPC/HTTP в формате OTLP.

gRPC

Модуль позволяет собирать трассировку с помощью gRPC протокола посредствам GrpcSender.

===! ":fontawesome-brands-java: Java"

[Зависимость](general.md#_4) `build.gradle`:
```groovy
implementation "ru.tinkoff.kora:opentelemetry-tracing-exporter-grpc"
```

Модуль:
```java
@KoraApp
public interface Application extends OpentelemetryGrpcExporterModule { }
```

=== ":simple-kotlin: Kotlin"

[Зависимость](general.md#_4) `build.gradle.kts`:
```groovy
implementation("ru.tinkoff.kora:opentelemetry-tracing-exporter-grpc")
```

Модуль:
```kotlin
@KoraApp
interface Application : OpentelemetryGrpcExporterModule
```

HTTP

Модуль позволяет собирать трассировку с помощью HTTP протокола посредствам HttpSender.

===! ":fontawesome-brands-java: Java"

[Зависимость](general.md#_4) `build.gradle`:
```groovy
implementation "ru.tinkoff.kora:opentelemetry-tracing-exporter-http"
```

Модуль:
```java
@KoraApp
public interface Application extends OpentelemetryHttpExporterModule { }
```

=== ":simple-kotlin: Kotlin"

[Зависимость](general.md#_4) `build.gradle.kts`:
```groovy
implementation("ru.tinkoff.kora:opentelemetry-tracing-exporter-http")
```

Модуль:
```kotlin
@KoraApp
interface Application : OpentelemetryHttpExporterModule
```

Конфигурация

Обязательным полем является только endpoint, аттрибуты из поля attributes будут отправляться с каждым спаном.

Пример полной конфигурации, описанной в классе OpentelemetryGrpcExporterConfig или OpentelemetryHttpExporterConfig, а также OpentelemetryResourceConfig (указаны примеры значений или значения по умолчанию):

===! ":material-code-json: Hocon"

```javascript
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"
  }
}
```

1.  URL от [OpenTelemetry](https://opentelemetry.io/docs/collector/) коллектор сервиса (**обязательный**)
2.  Время ожидания соедининя с экспортером
3.  Максимальное время ожидания обработки телеметрии коллектором 
4.  Время между экспортом телеметрии в коллектор
5.  Максимальная кол-во телеметрии в рамках одного экспорта
6.  Максимальный размер очереди неотправленной телеметрии
7.  Максимальное вреия ожидания экспорта
8.  Механизм сжатия телеметрии при экпорте
9.  Экспортировать ли не сэмплированную телеметрию
10. Максимальное кол-во попыток на экспорт
11. Начальное значение ожидания перед следующей попыткой экспорта
12. Максимальное значение ожидания перед следующей попыткой экспорта
13. Мультипликатор значения задержки ожидания
14. Дополнительные атрибуты телеметрии

=== ":simple-yaml: YAML"

```yaml
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
```

1.  URL от [OpenTelemetry](https://opentelemetry.io/docs/collector/) коллектор сервиса (**обязательный**)
2.  Время ожидания соедининя с экспортером
3.  Максимальное время ожидания обработки телеметрии коллектором 
4.  Время между экспортом телеметрии в коллектор
5.  Максимальная кол-во телеметрии в рамках одного экспорта
6.  Максимальный размер очереди неотправленной телеметрии
7.  Максимальное вреия ожидания экспорта
8.  Механизм сжатия телеметрии при экпорте
9.  Экспортировать ли не сэмплированную телеметрию
10. Максимальное кол-во попыток на экспорт
11. Начальное значение ожидания перед следующей попыткой экспорта
12. Максимальное значение ожидания перед следующей попыткой экспорта
13. Мультипликатор значения задержки ожидания
14. Дополнительные атрибуты телеметрии

Параметры конфигурации сбора трассировки описываются в модулях в которых присутствует сбор трассировки, например HTTP сервер, HTTP клиент и т.д.

Контекст трассировки

Чтобы получить текущий Span трассировки можно использовать метод getSpan у OpentelemetryContext:

===! ":fontawesome-brands-java: Java"

```java
var span = OpentelemetryContext.getSpan();
```

=== ":simple-kotlin: Kotlin"

```kotlin
val span = OpentelemetryContext.getSpan();
```

Для получения текущего идентификатора трассировки можно использовать метод getTraceId() у OpentelemetryContext:

===! ":fontawesome-brands-java: Java"

```java
var traceId = OpentelemetryContext.getTraceId();
```

=== ":simple-kotlin: Kotlin"

```kotlin
val traceId = OpentelemetryContext.getTraceId();
```

Синхронная трассировка

Помимо автоматически создаваемых фреймворком спанов, можно пользоваться объектом Tracer из контейнера для создания своих трассировок.

Создать трассировку синхронного кода от текущего родительского можно следующим образом:

===! ":fontawesome-brands-java: Java"

```java
@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
    }
}
```

=== ":simple-kotlin: Kotlin"

```kotlin
@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
}
```

Асинхронная трассировка

Помимо автоматически создаваемых фреймворком спанов, можно пользоваться объектом Tracer из контейнера для создания своих трассировок. Главная сложность заключается в прокидывании Fork'а контекста в другой поток исполнения, для корректной работы трассировки.

Создать трассировку асинхронного кода от текущего родительского можно следующим образом:

===! ":fontawesome-brands-java: Java"

Пример показан для `CompletableStage` асинхронного подхода:

```java
@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
    }
}
```

=== ":simple-kotlin: Kotlin"

Пример показан для `suspend` асинхронного подхода:

```kotlin
@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
}
```