多くの場合、ヘッダーの抽出と注入は自動的に行われます。しかし、いくつかのケースでは分散トレースが切断される可能性があります。たとえば、分散キューからメッセージを読み込む場合に、一部のライブラリがスパンコンテキストを失ってしまうことがあります。また、Kafka メッセージを取得するときに DD_TRACE_KAFKA_CREATE_CONSUMER_SCOPE_ENABLED
を false
に設定している場合も同様です。こうしたケースでは、以下のコード例のようにカスタムトレースを追加できます。
var spanContextExtractor = new SpanContextExtractor();
var parentContext = spanContextExtractor.Extract(headers, (headers, key) => GetHeaderValues(headers, key));
var spanCreationSettings = new SpanCreationSettings() { Parent = parentContext };
using var scope = Tracer.Instance.StartActive("operation", spanCreationSettings);
GetHeaderValues
メソッドを提供します。このメソッドの実装方法は、SpanContext
を保持する構造に依存します。
以下はその例です。
// Confluent.Kafka
IEnumerable<string> GetHeaderValues(Headers headers, string name)
{
if (headers.TryGetLastBytes(name, out var bytes))
{
try
{
return new[] { Encoding.UTF8.GetString(bytes) };
}
catch (Exception)
{
// 無視
}
}
return Enumerable.Empty<string>();
}
// RabbitMQ
IEnumerable<string> GetHeaderValues(IDictionary<string, object> headers, string name)
{
if (headers.TryGetValue(name, out object value) && value is byte[] bytes)
{
return new[] { Encoding.UTF8.GetString(bytes) };
}
return Enumerable.Empty<string>();
}
// SQS
public static IEnumerable<string> GetHeaderValues(IDictionary<string, MessageAttributeValue> headers, string name)
{
// SQS の場合、メッセージ属性ヘッダーは最大 10 個なので、
// Datadog のヘッダーは以下のプロパティを持つ 1 つのヘッダーに統合されます。
// - Key: "_datadog"
// - Value: MessageAttributeValue object
// - DataType: "String"
// - StringValue: <JSON map with key-value headers>
if (headers.TryGetValue("_datadog", out var messageAttributeValue)
&& messageAttributeValue.StringValue is string jsonString)
{
var datadogDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
if (datadogDictionary.TryGetValue(name, out string value))
{
return new[] { value };
}
}
return Enumerable.Empty<string>();
}
Kafka コンシューマースパンをトレースするために SpanContextExtractor
API を使用する場合、DD_TRACE_KAFKA_CREATE_CONSUMER_SCOPE_ENABLED
を false
に設定します。これにより、メッセージがトピックから消費された直後にコンシューマースパンが正しく閉じられ、メタデータ (partition
や offset
など) が正しく記録されることが保証されます。SpanContextExtractor
API を使用して Kafka メッセージから作成されたスパンは、プロデューサーのスパンの子であり、コンシューマーのスパンの兄弟になります。
(WCF クライアントのように自動的にインスツルメンテーションされないライブラリに対して) トレースコンテキストを手動で伝播する必要がある場合、SpanContextInjection
API を使用することができます。以下は WCF の例で、this
は WCF クライアントです。
using (OperationContextScope ocs = new OperationContextScope(this.InnerChannel))
{
var spanContextInjector = new SpanContextInjector();
spanContextInjector.Inject(OperationContext.Current.OutgoingMessageHeaders, SetHeaderValues, Tracer.Instance.ActiveScope?.Span?.Context);
}
void SetHeaderValues(MessageHeaders headers, string name, string value)
{
MessageHeader header = MessageHeader.CreateHeader(name, "datadog", value);
headers.Add(header);
}