Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/Datadog.Trace.OpenTracing/HttpHeadersCodec.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Datadog.Trace.Headers;
using OpenTracing.Propagation;
Expand All @@ -20,7 +21,8 @@ internal class HttpHeadersCodec : ICodec

IHeadersCollection headers = new TextMapHeadersCollection(map);
var propagationContext = SpanContextPropagator.Instance.Extract(headers);
return new OpenTracingSpanContext(propagationContext);
var baggage = ExtractBaggage(map);
return new OpenTracingSpanContext(propagationContext, baggage);
}

public void Inject(global::OpenTracing.ISpanContext context, object carrier)
Expand All @@ -33,6 +35,7 @@ public void Inject(global::OpenTracing.ISpanContext context, object carrier)
}

IHeadersCollection headers = new TextMapHeadersCollection(map);
InjectBaggage(context.GetBaggageItems(), headers);

if (context is OpenTracingSpanContext otSpanContext && otSpanContext.Context is SpanContext ddSpanContext)
{
Expand All @@ -46,5 +49,36 @@ public void Inject(global::OpenTracing.ISpanContext context, object carrier)
headers.Set(HttpHeaderNames.ParentId, context.SpanId.ToString(InvariantCulture));
}
}

private void InjectBaggage(IEnumerable<KeyValuePair<string, string>> baggage, IHeadersCollection headers)
{
if (baggage == null)
{
return;
}

foreach (var kv in baggage)
{
headers.Set($"{OpenTracingHttpHeaderNames.BaggagePrefix}{kv.Key}", kv.Value);
}
}

private IEnumerable<KeyValuePair<string, string>> ExtractBaggage(ITextMap headers)
{
if (headers == null)
{
yield break;
}

foreach (var kv in headers)
{
if (kv.Key.StartsWith(OpenTracingHttpHeaderNames.BaggagePrefix, StringComparison.OrdinalIgnoreCase))
{
yield return new KeyValuePair<string, string>(
kv.Key.Substring(OpenTracingHttpHeaderNames.BaggagePrefix.Length),
kv.Value);
}
}
}
}
}
13 changes: 13 additions & 0 deletions src/Datadog.Trace.OpenTracing/OpenTracingHttpHeaderNames.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Datadog.Trace.OpenTracing
{
/// <summary>
/// Open Tracing related http header names
/// </summary>
public static class OpenTracingHttpHeaderNames
{
/// <summary>
/// Http header suffix for propagated baggage items;
/// </summary>
public const string BaggagePrefix = "x-datadog-baggage-";
}
}
12 changes: 8 additions & 4 deletions src/Datadog.Trace.OpenTracing/OpenTracingSpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ namespace Datadog.Trace.OpenTracing
{
internal class OpenTracingSpan : ISpan
{
internal OpenTracingSpan(Span span)
internal OpenTracingSpan(Span span, IEnumerable<KeyValuePair<string, string>> baggage = null)
{
Span = span;
Context = new OpenTracingSpanContext(span.Context);
Context = new OpenTracingSpanContext(span.Context, baggage);
}

public OpenTracingSpanContext Context { get; }
Expand All @@ -28,7 +28,7 @@ internal OpenTracingSpan(Span span)

internal TimeSpan Duration => Span.Duration;

public string GetBaggageItem(string key) => null;
public string GetBaggageItem(string key) => Context?.GetBaggageItem(key);

public ISpan Log(DateTimeOffset timestamp, IEnumerable<KeyValuePair<string, object>> fields) => this;

Expand All @@ -38,7 +38,11 @@ internal OpenTracingSpan(Span span)

public ISpan Log(IEnumerable<KeyValuePair<string, object>> fields) => this;

public ISpan SetBaggageItem(string key, string value) => this;
public ISpan SetBaggageItem(string key, string value)
{
Context?.SetBaggageItem(key, value);
return this;
}

public ISpan SetOperationName(string operationName)
{
Expand Down
19 changes: 18 additions & 1 deletion src/Datadog.Trace.OpenTracing/OpenTracingSpanBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Datadog.Trace.Logging;
using OpenTracing;
using OpenTracing.Tag;
Expand Down Expand Up @@ -71,7 +72,9 @@ public ISpan Start()
{
ISpanContext parentContext = GetParentContext();
Span ddSpan = _tracer.DatadogTracer.StartSpan(_operationName, parentContext, _serviceName, _start, _ignoreActiveSpan);
var otSpan = new OpenTracingSpan(ddSpan);

IEnumerable<KeyValuePair<string, string>> parentBaggage = GetParentBaggage();
var otSpan = new OpenTracingSpan(ddSpan, parentBaggage);

if (_tags != null)
{
Expand Down Expand Up @@ -173,5 +176,19 @@ private ISpanContext GetParentContext()

return parentContext;
}

private IEnumerable<KeyValuePair<string, string>> GetParentBaggage()
{
var openTracingSpanContext = _parent as OpenTracingSpanContext;
var parentContext = openTracingSpanContext;

if (parentContext == null && !_ignoreActiveSpan)
{
// if parent was not set explicitly, default to active span as parent (unless disabled)
return _tracer.ActiveSpan?.Context?.GetBaggageItems();
}

return parentContext?.GetBaggageItems();
}
}
}
19 changes: 16 additions & 3 deletions src/Datadog.Trace.OpenTracing/OpenTracingSpanContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Datadog.Trace.Logging;

namespace Datadog.Trace.OpenTracing
Expand All @@ -9,9 +9,10 @@ internal class OpenTracingSpanContext : global::OpenTracing.ISpanContext
{
private static ILog _log = LogProvider.For<OpenTracingSpanContext>();

public OpenTracingSpanContext(ISpanContext context)
public OpenTracingSpanContext(ISpanContext context, IEnumerable<KeyValuePair<string, string>> baggage = null)
{
Context = context;
Baggage = baggage != null ? new ConcurrentDictionary<string, string>(baggage) : new ConcurrentDictionary<string, string>();
}

public string TraceId => Context.TraceId.ToString(CultureInfo.InvariantCulture);
Expand All @@ -20,9 +21,21 @@ public OpenTracingSpanContext(ISpanContext context)

internal ISpanContext Context { get; }

private ConcurrentDictionary<string, string> Baggage { get; }

public IEnumerable<KeyValuePair<string, string>> GetBaggageItems()
{
return Enumerable.Empty<KeyValuePair<string, string>>();
return Baggage.ToArray();
}

public string GetBaggageItem(string key)
{
return Baggage.TryGetValue(key, out var value) ? value : null;
}

public void SetBaggageItem(string key, string value)
{
Baggage.AddOrUpdate(key, value, (k, cmp) => value);
}
}
}
55 changes: 54 additions & 1 deletion test/Datadog.Trace.OpenTracing.Tests/HttpHeaderCodecTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Xunit;

namespace Datadog.Trace.OpenTracing.Tests
Expand All @@ -9,6 +10,7 @@ public class HttpHeaderCodecTests
private const string HttpHeaderTraceId = "x-datadog-trace-id";
private const string HttpHeaderParentId = "x-datadog-parent-id";
private const string HttpHeaderSamplingPriority = "x-datadog-sampling-priority";
private const string HttpHeaderBaggagePrefix = "x-datadog-baggage-";

private readonly HttpHeadersCodec _codec = new HttpHeadersCodec();

Expand All @@ -29,23 +31,63 @@ public void Extract_ValidParentAndTraceId_ProperSpanContext()
Assert.Equal(parentId, spanContext.Context.SpanId);
}

[Fact]
public void Extract_ValidBaggageItems_ProperSpanContext()
{
var baggage = new KeyValuePair<string, string>[]
{
new KeyValuePair<string, string>("Potato", "Cannon"),
new KeyValuePair<string, string>("Foo", "Bar")
};

var headers = new MockTextMap();
foreach (var kv in baggage)
{
headers.Set($"{HttpHeaderBaggagePrefix}{kv.Key}", kv.Value);
}

var spanContext = _codec.Extract(headers) as OpenTracingSpanContext;

Assert.NotNull(spanContext);
foreach (var kv in baggage)
{
Assert.Equal(kv.Value, spanContext.GetBaggageItem(kv.Key));
}
}

[Fact]
public void Extract_WrongHeaderCase_ExtractionStillWorks()
{
const ulong traceId = 10;
const ulong parentId = 120;
const SamplingPriority samplingPriority = SamplingPriority.UserKeep;

var baggage = new KeyValuePair<string, string>[]
{
new KeyValuePair<string, string>("Potato", "Cannon"),
new KeyValuePair<string, string>("Foo", "Bar")
};

var headers = new MockTextMap();
headers.Set(HttpHeaderTraceId.ToUpper(), traceId.ToString());
headers.Set(HttpHeaderParentId.ToUpper(), parentId.ToString());
headers.Set(HttpHeaderSamplingPriority.ToUpper(), ((int)samplingPriority).ToString());

foreach (var kv in baggage)
{
headers.Set($"{HttpHeaderBaggagePrefix.ToUpper()}{kv.Key}", kv.Value);
}

var spanContext = _codec.Extract(headers) as OpenTracingSpanContext;

Assert.NotNull(spanContext);
Assert.Equal(traceId, spanContext.Context.TraceId);
Assert.Equal(parentId, spanContext.Context.SpanId);

foreach (var kv in baggage)
{
Assert.Equal(kv.Value, spanContext.GetBaggageItem(kv.Key));
}
}

[Fact]
Expand All @@ -55,15 +97,26 @@ public void Inject_SpanContext_HeadersWithCorrectInfo()
const ulong traceId = 7;
const SamplingPriority samplingPriority = SamplingPriority.UserKeep;

var baggage = new KeyValuePair<string, string>[]
{
new KeyValuePair<string, string>("Potato", "Cannon"),
new KeyValuePair<string, string>("Foo", "Bar")
};

var ddSpanContext = new SpanContext(traceId, spanId, samplingPriority);
var spanContext = new OpenTracingSpanContext(ddSpanContext);
var spanContext = new OpenTracingSpanContext(ddSpanContext, baggage);
var headers = new MockTextMap();

_codec.Inject(spanContext, headers);

Assert.Equal(spanId.ToString(), headers.Get(HttpHeaderParentId));
Assert.Equal(traceId.ToString(), headers.Get(HttpHeaderTraceId));
Assert.Equal(((int)samplingPriority).ToString(), headers.Get(HttpHeaderSamplingPriority));

foreach (var kv in baggage)
{
Assert.Equal(kv.Value, headers.Get($"{HttpHeaderBaggagePrefix}{kv.Key}"));
}
}
}
}
Loading