分层采样

了解如何在 .NET 中为 OpenTelemetry 跟踪实现分层抽样

本指南演示了一种在 OpenTelemetry .NET 中实现分层抽样的方法。

什么是分层抽样?

分层抽样是将一个总体划分为互斥的子总体或“层”的一种方法。例如,“查询”总体的层可以是“用户发起的查询”和“程序化查询”。然后,使用概率抽样方法对每个层进行抽样。这确保了所有子总体都得到代表。

实现方法

SDK 通过使用一个自定义的 Sampler 来实现这一点,该 Sampler 内部包含两个 Sampler。根据层,会调用相应的 Sampler。

其中一个先决条件是,用于分层抽样决策的标签(例如 queryType)必须在创建活动时提供。

SDK 使用的是分层整群抽样,也称为“不等概率抽样”。例如,每个子总体的样本量与其在总体中的出现次数不成比例。在此示例中,我们希望确保所有用户发起的查询都得到代表,因此我们对其使用 100% 的采样率,而为程序化查询选择的采样率则低得多。

示例代码

关键组件是自定义的 StratifiedSampler

public class StratifiedSampler : Sampler
{
    private readonly string _stratifyByTagName;
    private readonly Dictionary<string, Sampler> _samplersByStratum;
    private readonly Sampler _defaultSampler;

    public StratifiedSampler(
        string stratifyByTagName,
        Dictionary<string, Sampler> samplersByStratum,
        Sampler defaultSampler)
        : base()
    {
        _stratifyByTagName = stratifyByTagName;
        _samplersByStratum = samplersByStratum;
        _defaultSampler = defaultSampler;
    }

    public override SamplingResult ShouldSample(
        in SamplingParameters samplingParameters)
    {
        ReadOnlySpan<KeyValuePair<string, object>> attributes =
            samplingParameters.Tags;

        for (int i = 0; i < attributes.Length; i++)
        {
            if (attributes[i].Key.Equals(_stratifyByTagName,
                StringComparison.OrdinalIgnoreCase))
            {
                string stratum = attributes[i].Value.ToString().ToLowerInvariant();
                if (_samplersByStratum.TryGetValue(stratum, out Sampler sampler))
                {
                    Console.WriteLine($"StratifiedSampler handling {stratum} query");
                    return sampler.ShouldSample(samplingParameters);
                }

                break;
            }
        }

        return _defaultSampler.ShouldSample(samplingParameters);
    }

    public override string Description => $"StratifiedSampler: {_stratifyByTagName}";
}

示例输出

运行使用此 Sampler 的应用程序时,您应该会看到类似以下的输出

StratifiedSampler handling userinitiated query
Activity.TraceId:            1a122d63e5f8d32cb8ebd3e402eb5389
Activity.SpanId:             83bdc6bbebea1df8
Activity.TraceFlags:         Recorded
Activity.ParentSpanId:       1ddd00d845ad645e
Activity.ActivitySourceName: StratifiedSampling.POC
Activity.DisplayName:        Main
Activity.Kind:               Internal
Activity.StartTime:          2023-02-09T05:19:30.8156879Z
Activity.Duration:           00:00:00.0008656
Activity.Tags:
    queryType: userInitiated
    foo: child
Resource associated with Activity:
    service.name: unknown_service:Examples.StratifiedSamplingByQueryType

Activity.TraceId:            1a122d63e5f8d32cb8ebd3e402eb5389
Activity.SpanId:             1ddd00d845ad645e
Activity.TraceFlags:         Recorded
Activity.ActivitySourceName: StratifiedSampling.POC
Activity.DisplayName:        Main
Activity.Kind:               Internal
Activity.StartTime:          2023-02-09T05:19:30.8115186Z
Activity.Duration:           00:00:00.0424036
Activity.Tags:
    queryType: userInitiated
    foo: bar
Resource associated with Activity:
    service.name: unknown_service:Examples.StratifiedSamplingByQueryType

这表明两个子总体(层)正在独立抽样,每个层应用不同的采样率。

完整示例

有关包含工作应用程序的完整示例,请参阅 OpenTelemetry .NET 存储库