Amostragem

A Amostragem é um processo que restringe a quantidade de trechos gerados por um sistema. A configuração de amostragem exata que você deve usar depende das suas necessidades específicas, mas, em geral, você deve tomar uma decisão no início de um rastro e permitir que a decisão de amostragem se propague para outros serviços.

Um Sampler pode ser definido no TracerProvider utilizando o método WithSampler, conforme o exemplo a seguir:

provider := trace.NewTracerProvider(
    trace.WithSampler(trace.AlwaysSample()),
)

AlwaysSample e NeverSample são valores autoexplicativos. AlwaysSample significa que cada trecho será amostrado, enquanto NeverSample significa que nenhum trecho será amostrado. Ao iniciar um projeto, ou em ambiente de desenvolvimento, utilize AlwaysSample.

Outros amostradores disponíveis são:

  • TraceIDRatioBased, que amostra uma fração dos trechos, com base na fração fornecida ao amostrador. Caso esta fração seja .5, metade de todos os trechos serão amostrados.
  • ParentBased, é um decorador de amostrador que se comporta de maneira diferente, com base no pai do trecho. Caso o trecho não possua um pai, o amostrador decorado é usado para tomar a decisão de amostragem. Por padrão, ParentBased amostra trechos que possuem pais que foram amostrados, e não amostra trechos cujos pais não foram amostrados.

Por padrão, o Tracer Provider utiliza o amostrador ParentBased com o amostrador AlwaysSample

Caso esteja em um ambiente de produção, considere utilizar o amostrador ParentBased com o amostrador TraceIDRatioBased.

Amostradores personalizados

Caso os amostradores nativos não atendam às suas necessidades, você pode criar um amostrador personalizado implementando a interface Sampler. Um amostrador personalizado deve implementar dois métodos:

  • ShouldSample(parameters SamplingParameters) SamplingResult: Toma a decisão de amostragem com base nos parâmetros fornecidos.
  • Description() string: Retorna uma descrição do amostrador.

Exemplo

O exemplo a seguir demonstra um amostrador personalizado que amostra trechos com base no valor de um atributo, preservando corretamente o tracestate:

package main

import (
    "context"

    "go.opentelemetry.io/otel/attribute"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    "go.opentelemetry.io/otel/trace"
)

// AttributeBasedSampler amostra trechos com base no valor de um atributo.
// Ele sempre amostra trechos com o atributo "high.priority" definido como true.
type AttributeBasedSampler struct {
    fallback sdktrace.Sampler
}

// NewAttributeBasedSampler cria um novo AttributeBasedSampler.
func NewAttributeBasedSampler(fallback sdktrace.Sampler) *AttributeBasedSampler {
    return &AttributeBasedSampler{fallback: fallback}
}

func (s *AttributeBasedSampler) ShouldSample(p sdktrace.SamplingParameters) sdktrace.SamplingResult {
    // Sempre extraia o contexto do trecho pai para obter o tracestate.
    psc := trace.SpanContextFromContext(p.ParentContext)

    // Verifica se algum atributo indica alta prioridade.
    for _, attr := range p.Attributes {
        if attr.Key == "high.priority" && attr.Value.AsBool() {
            return sdktrace.SamplingResult{
                Decision:   sdktrace.RecordAndSample,
                Attributes: p.Attributes,
                // Crítico: preserva o tracestate do pai
                Tracestate: psc.TraceState(),
            }
        }
    }

    // Recorre ao amostrador padrão para os demais trechos.
    result := s.fallback.ShouldSample(p)

    // Garante que o tracestate seja preservado mesmo ao usar o fallback.
    // Os amostradores nativos já tratam isso, mas é uma boa prática verificar.
    return sdktrace.SamplingResult{
        Decision:   result.Decision,
        Attributes: result.Attributes,
        Tracestate: psc.TraceState(),
    }
}

func (s *AttributeBasedSampler) Description() string {
    return "AttributeBasedSampler"
}

Utilizando o seu amostrador personalizado

Você pode utilizar o seu amostrador personalizado com o TracerProvider:

sampler := NewAttributeBasedSampler(sdktrace.TraceIDRatioBased(0.1))

provider := sdktrace.NewTracerProvider(
    sdktrace.WithSampler(sampler),
)

Você também pode combiná-lo com o amostrador ParentBased:

provider := sdktrace.NewTracerProvider(
    sdktrace.WithSampler(
        sdktrace.ParentBased(
            NewAttributeBasedSampler(sdktrace.TraceIDRatioBased(0.1)),
        ),
    ),
)

Considerações adicionais

Ao implementar amostradores personalizados, tenha em mente os seguintes pontos:

  1. Ignorar a decisão de amostragem do pai: Caso queira respeitar as decisões de amostragem do pai, envolva o seu amostrador com ParentBased ou verifique psc.IsSampled() manualmente.

  2. Processamento pesado em ShouldSample: A função ShouldSample é chamada de forma síncrona a cada criação de trecho. Evite operações custosas, como chamadas de rede ou processamentos complexos, que possam impactar o desempenho.