From 1605acea9c2b37e0ab14863d8a0abc36e4d9ceea Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 9 Aug 2020 10:12:34 -0700 Subject: [PATCH 01/33] Support W3C Baggage spec. --- .../Propagation/CompositePropagator.cs | 14 +- .../Context/Propagation/ITextFormat.cs | 23 +- .../Context/Propagation/TraceContextFormat.cs | 601 +++++++++++++++++- .../Implementation/HttpInListener.cs | 15 +- .../Implementation/HttpInListener.cs | 13 +- .../HttpWebRequestActivitySource.netfx.cs | 23 +- .../Context/Propagation/B3Format.cs | 54 +- .../Trace/Propagation/TestPropagator.cs | 18 +- 8 files changed, 675 insertions(+), 86 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs index ef69bca4490..23aa597ddc3 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs @@ -42,26 +42,26 @@ public CompositePropagator(List textFormats) public ISet Fields => EmptyFields; /// - public ActivityContext Extract(ActivityContext activityContext, T carrier, Func> getter) + public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) { foreach (var textFormat in this.textFormats) { - activityContext = textFormat.Extract(activityContext, carrier, getter); - if (activityContext.IsValid()) + context = textFormat.Extract(context, carrier, getter); + if (context.ActivityContext.IsValid()) { - return activityContext; + return context; } } - return activityContext; + return context; } /// - public void Inject(ActivityContext activityContext, T carrier, Action setter) + public void Inject(Activity activity, T carrier, Action setter) { foreach (var textFormat in this.textFormats) { - textFormat.Inject(activityContext, carrier, setter); + textFormat.Inject(activity, carrier, setter); } } diff --git a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs index 135a246412b..5e701597400 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs @@ -19,6 +19,19 @@ namespace OpenTelemetry.Context.Propagation { + public readonly struct TextFormatContext + { + public ActivityContext ActivityContext { get; } + + public IEnumerable> ActivityBaggage { get; } + + public TextFormatContext(ActivityContext activityContext, IEnumerable> activityBaggage) + { + this.ActivityContext = activityContext; + this.ActivityBaggage = activityBaggage; + } + } + /// /// Text format wire context propagator. Helps to extract and inject context from textual /// representation (typically http headers or metadata collection). @@ -36,20 +49,20 @@ public interface ITextFormat /// Injects textual representation of activity context to transmit over the wire. /// /// Type of an object to set context on. Typically HttpRequest or similar. - /// Activity context to transmit over the wire. + /// Activity to transmit over the wire. /// Object to set context on. Instance of this object will be passed to setter. /// Action that will set name and value pair on the object. - void Inject(ActivityContext activityContext, T carrier, Action setter); + void Inject(Activity activity, T carrier, Action setter); /// /// Extracts activity context from textual representation. /// /// Type of object to extract context from. Typically HttpRequest or similar. - /// The default activity context to be used if Extract fails. + /// The default context to be used if Extract fails. /// Object to extract context from. Instance of this object will be passed to the getter. /// Function that will return string value of a key with the specified name. - /// Activity context from it's text representation. - ActivityContext Extract(ActivityContext activityContext, T carrier, Func> getter); + /// Context from it's text representation. + TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter); /// /// Tests if an activity context has been injected into a carrier. diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index ee1525aa573..9dd7154e9ea 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -15,8 +15,10 @@ // using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.Linq; +using System.Net; using System.Text; using OpenTelemetry.Internal; @@ -29,6 +31,8 @@ public class TraceContextFormat : ITextFormat { private const string TraceParent = "traceparent"; private const string TraceState = "tracestate"; + private const string Baggage = "baggage"; + private const int MaxBaggageLength = 1024; private static readonly int VersionPrefixIdLength = "00-".Length; private static readonly int TraceIdLength = "0af7651916cd43dd8448eb211c80319c".Length; @@ -73,18 +77,18 @@ public bool IsInjected(T carrier, Func> getter } /// - public ActivityContext Extract(ActivityContext activityContext, T carrier, Func> getter) + public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) { if (carrier == null) { OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); - return activityContext; + return context; } if (getter == null) { OpenTelemetryApiEventSource.Log.FailedToExtractContext("null getter"); - return activityContext; + return context; } try @@ -94,7 +98,7 @@ public ActivityContext Extract(ActivityContext activityContext, T carrier, Fu // There must be a single traceparent if (traceparentCollection == null || traceparentCollection.Count() != 1) { - return activityContext; + return context; } var traceparent = traceparentCollection.First(); @@ -102,7 +106,7 @@ public ActivityContext Extract(ActivityContext activityContext, T carrier, Fu if (!traceparentParsed) { - return activityContext; + return context; } string tracestate = string.Empty; @@ -112,7 +116,16 @@ public ActivityContext Extract(ActivityContext activityContext, T carrier, Fu TryExtractTracestate(tracestateCollection.ToArray(), out tracestate); } - return new ActivityContext(traceId, spanId, traceoptions, tracestate, isRemote: true); + IEnumerable> baggage = null; + var baggageCollection = getter(carrier, Baggage); + if (baggageCollection?.Any() ?? false) + { + TryExtractTracestateBaggage(baggageCollection.ToArray(), out baggage); + } + + return new TextFormatContext( + new ActivityContext(traceId, spanId, traceoptions, tracestate, isRemote: true), + baggage); } catch (Exception ex) { @@ -120,13 +133,13 @@ public ActivityContext Extract(ActivityContext activityContext, T carrier, Fu } // in case of exception indicate to upstream that there is no parseable context from the top - return activityContext; + return context; } /// - public void Inject(ActivityContext activityContext, T carrier, Action setter) + public void Inject(Activity activity, T carrier, Action setter) { - if (activityContext == default) + if (activity.TraceId == default || activity.SpanId == default) { OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); return; @@ -144,16 +157,31 @@ public void Inject(ActivityContext activityContext, T carrier, Action 0) { setter(carrier, TraceState, tracestateStr); } + + using IEnumerator> e = activity.Baggage.GetEnumerator(); + + if (e.MoveNext()) + { + StringBuilder baggage = new StringBuilder(); + do + { + KeyValuePair item = e.Current; + baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(','); + } + while (e.MoveNext()); + baggage.Remove(baggage.Length - 1, 1); + setter(carrier, Baggage, baggage.ToString()); + } } internal static bool TryExtractTraceparent(string traceparent, out ActivityTraceId traceId, out ActivitySpanId spanId, out ActivityTraceFlags traceOptions) @@ -284,6 +312,43 @@ internal static bool TryExtractTracestate(string[] tracestateCollection, out str return true; } + internal static bool TryExtractTracestateBaggage(string[] baggageCollection, out IEnumerable> baggage) + { + int baggageLength = -1; + Dictionary baggageDictionary = null; + + foreach (var item in baggageCollection) + { + if (baggageLength >= MaxBaggageLength) + { + break; + } + + foreach (var pair in item.Split(',')) + { + baggageLength += pair.Length + 1; // pair and comma + + if (baggageLength >= MaxBaggageLength) + { + break; + } + + if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem)) + { + if (baggageDictionary == null) + { + baggageDictionary = new Dictionary(); + } + + baggageDictionary[baggageItem.Name] = baggageItem.Value; + } + } + } + + baggage = baggageDictionary; + return baggageDictionary != null; + } + private static byte HexCharToByte(char c) { if (((c >= '0') && (c <= '9')) @@ -296,4 +361,516 @@ private static byte HexCharToByte(char c) throw new ArgumentOutOfRangeException(nameof(c), $"Invalid character: {c}."); } } + + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/NameValueHeaderValue.cs + internal class NameValueHeaderValue + { + private static readonly HttpHeaderParser SingleValueParser + = new GenericHeaderParser(false, GetNameValueLength); + + private string name; + private string value; + + private NameValueHeaderValue() + { + // Used by the parser to create a new instance of this type. + } + + public string Name + { + get { return name; } + } + + public string Value + { + get { return value; } + } + + public static bool TryParse(string input, out NameValueHeaderValue parsedValue) + { + var index = 0; + return SingleValueParser.TryParseValue(input, ref index, out parsedValue); + } + + internal static int GetValueLength(string input, int startIndex) + { + if (startIndex >= input.Length) + { + return 0; + } + + var valueLength = HttpRuleParser.GetTokenLength(input, startIndex); + + if (valueLength == 0) + { + // A value can either be a token or a quoted string. Check if it is a quoted string. + if (HttpRuleParser.GetQuotedStringLength(input, startIndex, out valueLength) != HttpParseResult.Parsed) + { + // We have an invalid value. Reset the name and return. + return 0; + } + } + + return valueLength; + } + + private static int GetNameValueLength(string input, int startIndex, out NameValueHeaderValue parsedValue) + { + parsedValue = null; + + if (string.IsNullOrEmpty(input) || (startIndex >= input.Length)) + { + return 0; + } + + // Parse the name, i.e. in name/value string "=". Caller must remove + // leading whitespaces. + var nameLength = HttpRuleParser.GetTokenLength(input, startIndex); + + if (nameLength == 0) + { + return 0; + } + + var name = input.Substring(startIndex, nameLength); + var current = startIndex + nameLength; + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + + // Parse the separator between name and value + if ((current == input.Length) || (input[current] != '=')) + { + // We only have a name and that's OK. Return. + parsedValue = new NameValueHeaderValue(); + parsedValue.name = name; + current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces + return current - startIndex; + } + + current++; // skip delimiter. + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + + // Parse the value, i.e. in name/value string "=" + int valueLength = GetValueLength(input, current); + + // Value after the '=' may be empty + // Use parameterless ctor to avoid double-parsing of name and value, i.e. skip public ctor validation. + parsedValue = new NameValueHeaderValue(); + parsedValue.name = name; + parsedValue.value = input.Substring(current, valueLength); + current = current + valueLength; + current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces + return current - startIndex; + } + } + + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpHeaderParser.cs + internal abstract class HttpHeaderParser + { + private bool supportsMultipleValues; + + protected HttpHeaderParser(bool supportsMultipleValues) + { + this.supportsMultipleValues = supportsMultipleValues; + } + + public bool SupportsMultipleValues + { + get { return supportsMultipleValues; } + } + + // If a parser supports multiple values, a call to ParseValue/TryParseValue should return a value for 'index' + // pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0 + // for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first + // non-whitespace after the separator ','. + public abstract bool TryParseValue(string value, ref int index, out T parsedValue); + } + + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpParseResult.cs + internal enum HttpParseResult + { + /// + /// Parsed succesfully. + /// + Parsed, + + /// + /// Was not parsed. + /// + NotParsed, + + /// + /// Invalid format. + /// + InvalidFormat, + } + + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpRuleParser.cs + internal static class HttpRuleParser + { + internal const char CR = '\r'; + internal const char LF = '\n'; + internal const char SP = ' '; + internal const char Tab = '\t'; + internal const int MaxInt64Digits = 19; + internal const int MaxInt32Digits = 10; + + private const int MaxNestedCount = 5; + private static readonly bool[] TokenChars = CreateTokenChars(); + + internal static bool IsTokenChar(char character) + { + // Must be between 'space' (32) and 'DEL' (127) + if (character > 127) + { + return false; + } + + return TokenChars[character]; + } + + internal static int GetTokenLength(string input, int startIndex) + { + if (startIndex >= input.Length) + { + return 0; + } + + var current = startIndex; + + while (current < input.Length) + { + if (!IsTokenChar(input[current])) + { + return current - startIndex; + } + + current++; + } + + return input.Length - startIndex; + } + + internal static int GetWhitespaceLength(string input, int startIndex) + { + if (startIndex >= input.Length) + { + return 0; + } + + var current = startIndex; + + char c; + while (current < input.Length) + { + c = input[current]; + + if ((c == SP) || (c == Tab)) + { + current++; + continue; + } + + if (c == CR) + { + // If we have a #13 char, it must be followed by #10 and then at least one SP or HT. + if ((current + 2 < input.Length) && (input[current + 1] == LF)) + { + char spaceOrTab = input[current + 2]; + if ((spaceOrTab == SP) || (spaceOrTab == Tab)) + { + current += 3; + continue; + } + } + } + + return current - startIndex; + } + + // All characters between startIndex and the end of the string are LWS characters. + return input.Length - startIndex; + } + + internal static HttpParseResult GetQuotedStringLength(string input, int startIndex, out int length) + { + var nestedCount = 0; + return GetExpressionLength(input, startIndex, '"', '"', false, ref nestedCount, out length); + } + + // quoted-pair = "\" CHAR + // CHAR = + internal static HttpParseResult GetQuotedPairLength(string input, int startIndex, out int length) + { + length = 0; + + if (input[startIndex] != '\\') + { + return HttpParseResult.NotParsed; + } + + // Quoted-char has 2 characters. Check wheter there are 2 chars left ('\' + char) + // If so, check whether the character is in the range 0-127. If not, it's an invalid value. + if ((startIndex + 2 > input.Length) || (input[startIndex + 1] > 127)) + { + return HttpParseResult.InvalidFormat; + } + + // We don't care what the char next to '\' is. + length = 2; + return HttpParseResult.Parsed; + } + + private static bool[] CreateTokenChars() + { + // token = 1* + // CTL = + var tokenChars = new bool[128]; // everything is false + + for (int i = 33; i < 127; i++) + { + // skip Space (32) & DEL (127) + tokenChars[i] = true; + } + + // remove separators: these are not valid token characters + tokenChars[(byte)'('] = false; + tokenChars[(byte)')'] = false; + tokenChars[(byte)'<'] = false; + tokenChars[(byte)'>'] = false; + tokenChars[(byte)'@'] = false; + tokenChars[(byte)','] = false; + tokenChars[(byte)';'] = false; + tokenChars[(byte)':'] = false; + tokenChars[(byte)'\\'] = false; + tokenChars[(byte)'"'] = false; + tokenChars[(byte)'/'] = false; + tokenChars[(byte)'['] = false; + tokenChars[(byte)']'] = false; + tokenChars[(byte)'?'] = false; + tokenChars[(byte)'='] = false; + tokenChars[(byte)'{'] = false; + tokenChars[(byte)'}'] = false; + + return tokenChars; + } + + // TEXT = + // LWS = [CRLF] 1*( SP | HT ) + // CTL = + // + // Since we don't really care about the content of a quoted string or comment, we're more tolerant and + // allow these characters. We only want to find the delimiters ('"' for quoted string and '(', ')' for comment). + // + // 'nestedCount': Comments can be nested. We allow a depth of up to 5 nested comments, i.e. something like + // "(((((comment)))))". If we wouldn't define a limit an attacker could send a comment with hundreds of nested + // comments, resulting in a stack overflow exception. In addition having more than 1 nested comment (if any) + // is unusual. + private static HttpParseResult GetExpressionLength( + string input, + int startIndex, + char openChar, + char closeChar, + bool supportsNesting, + ref int nestedCount, + out int length) + { + length = 0; + + if (input[startIndex] != openChar) + { + return HttpParseResult.NotParsed; + } + + var current = startIndex + 1; // Start parsing with the character next to the first open-char + while (current < input.Length) + { + // Only check whether we have a quoted char, if we have at least 3 characters left to read (i.e. + // quoted char + closing char). Otherwise the closing char may be considered part of the quoted char. + var quotedPairLength = 0; + if ((current + 2 < input.Length) && + (GetQuotedPairLength(input, current, out quotedPairLength) == HttpParseResult.Parsed)) + { + // We ignore invalid quoted-pairs. Invalid quoted-pairs may mean that it looked like a quoted pair, + // but we actually have a quoted-string: e.g. "\ü" ('\' followed by a char >127 - quoted-pair only + // allows ASCII chars after '\'; qdtext allows both '\' and >127 chars). + current = current + quotedPairLength; + continue; + } + + // If we support nested expressions and we find an open-char, then parse the nested expressions. + if (supportsNesting && (input[current] == openChar)) + { + nestedCount++; + try + { + // Check if we exceeded the number of nested calls. + if (nestedCount > MaxNestedCount) + { + return HttpParseResult.InvalidFormat; + } + + var nestedLength = 0; + HttpParseResult nestedResult = GetExpressionLength(input, current, openChar, closeChar, supportsNesting, ref nestedCount, out nestedLength); + + switch (nestedResult) + { + case HttpParseResult.Parsed: + current += nestedLength; // add the length of the nested expression and continue. + break; + + case HttpParseResult.NotParsed: + break; + + case HttpParseResult.InvalidFormat: + // If the nested expression is invalid, we can't continue, so we fail with invalid format. + return HttpParseResult.InvalidFormat; + + default: + break; + } + } + finally + { + nestedCount--; + } + } + + if (input[current] == closeChar) + { + length = current - startIndex + 1; + return HttpParseResult.Parsed; + } + + current++; + } + + // We didn't see the final quote, therefore we have an invalid expression string. + return HttpParseResult.InvalidFormat; + } + } + + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/BaseHeaderParser.cs + internal abstract class BaseHeaderParser : HttpHeaderParser + { + protected BaseHeaderParser(bool supportsMultipleValues) + : base(supportsMultipleValues) + { + } + + public sealed override bool TryParseValue(string value, ref int index, out T parsedValue) + { + parsedValue = default(T); + + // If multiple values are supported (i.e. list of values), then accept an empty string: The header may + // be added multiple times to the request/response message. E.g. + // Accept: text/xml; q=1 + // Accept: + // Accept: text/plain; q=0.2 + if (string.IsNullOrEmpty(value) || (index == value.Length)) + { + return SupportsMultipleValues; + } + + var separatorFound = false; + var current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, index, SupportsMultipleValues, out separatorFound); + + if (separatorFound && !SupportsMultipleValues) + { + return false; // leading separators not allowed if we don't support multiple values. + } + + if (current == value.Length) + { + if (SupportsMultipleValues) + { + index = current; + } + + return SupportsMultipleValues; + } + + T result; + var length = GetParsedValueLength(value, current, out result); + + if (length == 0) + { + return false; + } + + current = current + length; + current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, current, SupportsMultipleValues, out separatorFound); + + // If we support multiple values and we've not reached the end of the string, then we must have a separator. + if ((separatorFound && !SupportsMultipleValues) || (!separatorFound && (current < value.Length))) + { + return false; + } + + index = current; + parsedValue = result; + return true; + } + + protected abstract int GetParsedValueLength(string value, int startIndex, out T parsedValue); + } + + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/GenericHeaderParser.cs + internal sealed class GenericHeaderParser : BaseHeaderParser + { + private GetParsedValueLengthDelegate getParsedValueLength; + + internal GenericHeaderParser(bool supportsMultipleValues, GetParsedValueLengthDelegate getParsedValueLength) + : base(supportsMultipleValues) + { + if (getParsedValueLength == null) + { + throw new ArgumentNullException(nameof(getParsedValueLength)); + } + + this.getParsedValueLength = getParsedValueLength; + } + + internal delegate int GetParsedValueLengthDelegate(string value, int startIndex, out T parsedValue); + + protected override int GetParsedValueLength(string value, int startIndex, out T parsedValue) + { + return getParsedValueLength(value, startIndex, out parsedValue); + } + } + + // Adoption of the code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HeaderUtilities.cs + internal static class HeaderUtilities + { + internal static int GetNextNonEmptyOrWhitespaceIndex( + string input, + int startIndex, + bool skipEmptyValues, + out bool separatorFound) + { + separatorFound = false; + var current = startIndex + HttpRuleParser.GetWhitespaceLength(input, startIndex); + + if ((current == input.Length) || (input[current] != ',')) + { + return current; + } + + // If we have a separator, skip the separator and all following whitespaces. If we support + // empty values, continue until the current character is neither a separator nor a whitespace. + separatorFound = true; + current++; // skip delimiter. + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + + if (skipEmptyValues) + { + while ((current < input.Length) && (input[current] == ',')) + { + current++; // skip delimiter. + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + } + } + + return current; + } + } } diff --git a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs index 90f6dd0dbb7..a6343d3383c 100644 --- a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs @@ -64,14 +64,21 @@ public override void OnStartActivity(Activity activity, object payload) // This requires to ignore the current activity and create a new one // using the context extracted using the format TextFormat supports. var ctx = this.options.TextFormat.Extract(default, request, HttpRequestHeaderValuesGetter); - if (ctx != default) + if (ctx.ActivityContext != default) { // Create a new activity with its parent set from the extracted context. // This makes the new activity as a "sibling" of the activity created by - // Asp.Net. + // ASP.NET. Activity newOne = new Activity(ActivityNameByHttpInListener); - newOne.SetParentId(ctx.TraceId, ctx.SpanId, ctx.TraceFlags); - newOne.TraceStateString = ctx.TraceState; + newOne.SetParentId(ctx.ActivityContext.TraceId, ctx.ActivityContext.SpanId, ctx.ActivityContext.TraceFlags); + newOne.TraceStateString = ctx.ActivityContext.TraceState; + if (ctx.ActivityBaggage != null) + { + foreach (var baggageItem in ctx.ActivityBaggage) + { + newOne.AddBaggage(baggageItem.Key, baggageItem.Value); + } + } // Starting the new activity make it the Activity.Current one. newOne.Start(); diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 9b37bbc05be..2a5ac421f97 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -72,14 +72,21 @@ public override void OnStartActivity(Activity activity, object payload) // using the format TextFormat supports. var ctx = this.options.TextFormat.Extract(default, request, HttpRequestHeaderValuesGetter); - if (ctx != default) + if (ctx.ActivityContext != default) { // Create a new activity with its parent set from the extracted context. // This makes the new activity as a "sibling" of the activity created by // Asp.Net Core. Activity newOne = new Activity(ActivityNameByHttpInListener); - newOne.SetParentId(ctx.TraceId, ctx.SpanId, ctx.TraceFlags); - newOne.TraceStateString = ctx.TraceState; + newOne.SetParentId(ctx.ActivityContext.TraceId, ctx.ActivityContext.SpanId, ctx.ActivityContext.TraceFlags); + newOne.TraceStateString = ctx.ActivityContext.TraceState; + if (ctx.ActivityBaggage != null) + { + foreach (var baggageItem in ctx.ActivityBaggage) + { + newOne.AddBaggage(baggageItem.Key, baggageItem.Value); + } + } // Starting the new activity make it the Activity.Current one. newOne.Start(); diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 9e910157f82..56782039576 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -44,8 +44,6 @@ internal static class HttpWebRequestActivitySource internal static HttpWebRequestInstrumentationOptions Options = new HttpWebRequestInstrumentationOptions(); - private const string CorrelationContextHeaderName = "Correlation-Context"; - private static readonly Version Version = typeof(HttpWebRequestActivitySource).Assembly.GetName().Version; private static readonly ActivitySource WebRequestActivitySource = new ActivitySource(ActivitySourceName, Version.ToString()); @@ -189,26 +187,7 @@ private static void AddExceptionTags(Exception exception, Activity activity) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void InstrumentRequest(HttpWebRequest request, Activity activity) { - Options.TextFormat.Inject(activity.Context, request, HttpWebRequestHeaderValuesSetter); - - if (request.Headers.Get(CorrelationContextHeaderName) == null) - { - // we expect baggage to be empty or contain a few items - using IEnumerator> e = activity.Baggage.GetEnumerator(); - - if (e.MoveNext()) - { - StringBuilder baggage = new StringBuilder(); - do - { - KeyValuePair item = e.Current; - baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(','); - } - while (e.MoveNext()); - baggage.Remove(baggage.Length - 1, 1); - request.Headers.Add(CorrelationContextHeaderName, baggage.ToString()); - } - } + Options.TextFormat.Inject(activity, request, HttpWebRequestHeaderValuesSetter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/OpenTelemetry/Context/Propagation/B3Format.cs b/src/OpenTelemetry/Context/Propagation/B3Format.cs index 9c2d16ef413..9dc8a6daa4b 100644 --- a/src/OpenTelemetry/Context/Propagation/B3Format.cs +++ b/src/OpenTelemetry/Context/Propagation/B3Format.cs @@ -107,34 +107,34 @@ public bool IsInjected(T carrier, Func> getter } /// - public ActivityContext Extract(ActivityContext activityContext, T carrier, Func> getter) + public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) { if (carrier == null) { OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null carrier"); - return activityContext; + return context; } if (getter == null) { OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null getter"); - return activityContext; + return context; } if (this.singleHeader) { - return ExtractFromSingleHeader(activityContext, carrier, getter); + return ExtractFromSingleHeader(context, carrier, getter); } else { - return ExtractFromMultipleHeaders(activityContext, carrier, getter); + return ExtractFromMultipleHeaders(context, carrier, getter); } } /// - public void Inject(ActivityContext activityContext, T carrier, Action setter) + public void Inject(Activity activity, T carrier, Action setter) { - if (!activityContext.IsValid()) + if (activity.TraceId == default || activity.SpanId == default) { OpenTelemetrySdkEventSource.Log.FailedToInjectContext("invalid context"); return; @@ -155,10 +155,10 @@ public void Inject(ActivityContext activityContext, T carrier, Action(ActivityContext activityContext, T carrier, Action(ActivityContext activityContext, T carrier, Func> getter) + private static TextFormatContext ExtractFromMultipleHeaders(TextFormatContext context, T carrier, Func> getter) { try { @@ -195,7 +195,7 @@ private static ActivityContext ExtractFromMultipleHeaders(ActivityContext act } else { - return activityContext; + return context; } ActivitySpanId spanId; @@ -206,7 +206,7 @@ private static ActivityContext ExtractFromMultipleHeaders(ActivityContext act } else { - return activityContext; + return context; } var traceOptions = ActivityTraceFlags.None; @@ -216,35 +216,37 @@ private static ActivityContext ExtractFromMultipleHeaders(ActivityContext act traceOptions |= ActivityTraceFlags.Recorded; } - return new ActivityContext(traceId, spanId, traceOptions, isRemote: true); + return new TextFormatContext( + new ActivityContext(traceId, spanId, traceOptions, isRemote: true), + null); } catch (Exception e) { OpenTelemetrySdkEventSource.Log.ContextExtractException(e); - return activityContext; + return context; } } - private static ActivityContext ExtractFromSingleHeader(ActivityContext activityContext, T carrier, Func> getter) + private static TextFormatContext ExtractFromSingleHeader(TextFormatContext context, T carrier, Func> getter) { try { var header = getter(carrier, XB3Combined)?.FirstOrDefault(); if (string.IsNullOrWhiteSpace(header)) { - return activityContext; + return context; } var parts = header.Split(XB3CombinedDelimiter); if (parts.Length < 2 || parts.Length > 4) { - return activityContext; + return context; } var traceIdStr = parts[0]; if (string.IsNullOrWhiteSpace(traceIdStr)) { - return activityContext; + return context; } if (traceIdStr.Length == 16) @@ -258,7 +260,7 @@ private static ActivityContext ExtractFromSingleHeader(ActivityContext activi var spanIdStr = parts[1]; if (string.IsNullOrWhiteSpace(spanIdStr)) { - return activityContext; + return context; } var spanId = ActivitySpanId.CreateFromString(spanIdStr.AsSpan()); @@ -274,12 +276,14 @@ private static ActivityContext ExtractFromSingleHeader(ActivityContext activi } } - return new ActivityContext(traceId, spanId, traceOptions, isRemote: true); + return new TextFormatContext( + new ActivityContext(traceId, spanId, traceOptions, isRemote: true), + null); } catch (Exception e) { OpenTelemetrySdkEventSource.Log.ContextExtractException(e); - return activityContext; + return context; } } } diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs index de206634c28..a81814338bd 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs @@ -37,23 +37,23 @@ public TestPropagator(string idHeaderName, string stateHeaderName, bool defaultC public ISet Fields => new HashSet() { this.idHeaderName, this.stateHeaderName }; - public ActivityContext Extract(ActivityContext activityContext, T carrier, Func> getter) + public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) { if (this.defaultContext) { - return activityContext; + return context; } IEnumerable id = getter(carrier, this.idHeaderName); if (id.Count() <= 0) { - return activityContext; + return context; } var traceparentParsed = TraceContextFormat.TryExtractTraceparent(id.First(), out var traceId, out var spanId, out var traceoptions); if (!traceparentParsed) { - return activityContext; + return context; } string tracestate = string.Empty; @@ -63,19 +63,21 @@ public ActivityContext Extract(ActivityContext activityContext, T carrier, Fu TraceContextFormat.TryExtractTracestate(tracestateCollection.ToArray(), out tracestate); } - return new ActivityContext(traceId, spanId, traceoptions, tracestate); + return new TextFormatContext( + new ActivityContext(traceId, spanId, traceoptions, tracestate), + null); } - public void Inject(ActivityContext activityContext, T carrier, Action setter) + public void Inject(Activity activity, T carrier, Action setter) { string headerNumber = this.stateHeaderName.Split('-').Last(); - var traceparent = string.Concat("00-", activityContext.TraceId.ToHexString(), "-", activityContext.SpanId.ToHexString()); + var traceparent = string.Concat("00-", activity.TraceId.ToHexString(), "-", activity.SpanId.ToHexString()); traceparent = string.Concat(traceparent, "-", headerNumber); setter(carrier, this.idHeaderName, traceparent); - string tracestateStr = activityContext.TraceState; + string tracestateStr = activity.TraceStateString; if (tracestateStr?.Length > 0) { setter(carrier, this.stateHeaderName, tracestateStr); From 37f6df521d30c84738a7594914d59480d782865e Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 9 Aug 2020 11:13:40 -0700 Subject: [PATCH 02/33] Moved baggage propagation to its own ITextFormat. Removed IsInjected. --- .../Propagation/CompositePropagator.cs | 10 - .../Context/Propagation/ITextFormat.cs | 51 ++++- .../Context/Propagation/TraceContextFormat.cs | 196 ++++++++++-------- .../HttpHandlerDiagnosticListener.cs | 4 +- .../HttpWebRequestActivitySource.netfx.cs | 6 +- .../Context/Propagation/B3Format.cs | 43 +--- .../Trace/Propagation/TestPropagator.cs | 10 +- 7 files changed, 161 insertions(+), 159 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs index 23aa597ddc3..34d49ba389f 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs @@ -47,10 +47,6 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(Activity activity, T carrier, Action se textFormat.Inject(activity, carrier, setter); } } - - /// - public bool IsInjected(T carrier, Func> getter) - { - return this.textFormats.All(textFormat => textFormat.IsInjected(carrier, getter)); - } } } diff --git a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs index 5e701597400..6cb0fcdd580 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs @@ -16,10 +16,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; namespace OpenTelemetry.Context.Propagation { - public readonly struct TextFormatContext + public readonly struct TextFormatContext : IEquatable { public ActivityContext ActivityContext { get; } @@ -30,6 +31,45 @@ public TextFormatContext(ActivityContext activityContext, IEnumerable left.Equals(right); + + public static bool operator !=(TextFormatContext left, TextFormatContext right) => !(left == right); + + public bool Equals(TextFormatContext value) + { + if (this.ActivityContext != value.ActivityContext + || this.ActivityBaggage is null != value.ActivityBaggage is null) + { + return false; + } + + if (this.ActivityBaggage is null) + { + return true; + } + + if (this.ActivityBaggage.Count() != value.ActivityBaggage.Count()) + { + return false; + } + + var thisEnumerator = this.ActivityBaggage.GetEnumerator(); + var valueEnumerator = value.ActivityBaggage.GetEnumerator(); + + while (thisEnumerator.MoveNext() && valueEnumerator.MoveNext()) + { + if (thisEnumerator.Current.Key != valueEnumerator.Current.Key + || thisEnumerator.Current.Value != valueEnumerator.Current.Value) + { + return false; + } + } + + return true; + } + + public override bool Equals(object? obj) => (obj is TextFormatContext context) ? Equals(context) : false; } /// @@ -63,14 +103,5 @@ public interface ITextFormat /// Function that will return string value of a key with the specified name. /// Context from it's text representation. TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter); - - /// - /// Tests if an activity context has been injected into a carrier. - /// - /// Type of object to extract context from. Typically HttpRequest or similar. - /// Object to extract context from. Instance of this object will be passed to the getter. - /// Function that will return string value of a key with the specified name. - /// if the carrier has been injected with an activity context. - bool IsInjected(T carrier, Func> getter); } } diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index 9dd7154e9ea..4568173072d 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -24,58 +24,145 @@ namespace OpenTelemetry.Context.Propagation { - /// - /// W3C trace context text wire protocol formatter. See https://github.com/w3c/distributed-tracing/. - /// - public class TraceContextFormat : ITextFormat + // W3C baggage: https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md + public class BaggageFormat : ITextFormat { - private const string TraceParent = "traceparent"; - private const string TraceState = "tracestate"; private const string Baggage = "baggage"; private const int MaxBaggageLength = 1024; - private static readonly int VersionPrefixIdLength = "00-".Length; - private static readonly int TraceIdLength = "0af7651916cd43dd8448eb211c80319c".Length; - private static readonly int VersionAndTraceIdLength = "00-0af7651916cd43dd8448eb211c80319c-".Length; - private static readonly int SpanIdLength = "00f067aa0ba902b7".Length; - private static readonly int VersionAndTraceIdAndSpanIdLength = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-".Length; - private static readonly int OptionsLength = "00".Length; - private static readonly int TraceparentLengthV0 = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-00".Length; - /// - public ISet Fields => new HashSet { TraceState, TraceParent }; + public ISet Fields => new HashSet { Baggage }; /// - public bool IsInjected(T carrier, Func> getter) + public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) { if (carrier == null) { OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); - return false; + return context; } if (getter == null) { OpenTelemetryApiEventSource.Log.FailedToExtractContext("null getter"); - return false; + return context; } try { - var traceparentCollection = getter(carrier, TraceParent); + IEnumerable> baggage = null; + var baggageCollection = getter(carrier, Baggage); + if (baggageCollection?.Any() ?? false) + { + TryExtractTracestateBaggage(baggageCollection.ToArray(), out baggage); + } - // There must be a single traceparent - return traceparentCollection != null && traceparentCollection.Count() == 1; + return new TextFormatContext( + context.ActivityContext, + baggage ?? context.ActivityBaggage); } catch (Exception ex) { OpenTelemetryApiEventSource.Log.ActivityContextExtractException(ex); } - // in case of exception indicate to upstream that there is no parseable context from the top - return false; + return context; } + /// + public void Inject(Activity activity, T carrier, Action setter) + { + if (activity == null) + { + OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); + return; + } + + if (carrier == null) + { + OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); + return; + } + + if (setter == null) + { + OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null setter"); + return; + } + + using IEnumerator> e = activity.Baggage.GetEnumerator(); + + if (e.MoveNext()) + { + StringBuilder baggage = new StringBuilder(); + do + { + KeyValuePair item = e.Current; + baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(','); + } + while (e.MoveNext()); + baggage.Remove(baggage.Length - 1, 1); + setter(carrier, Baggage, baggage.ToString()); + } + } + + internal static bool TryExtractTracestateBaggage(string[] baggageCollection, out IEnumerable> baggage) + { + int baggageLength = -1; + Dictionary baggageDictionary = null; + + foreach (var item in baggageCollection) + { + if (baggageLength >= MaxBaggageLength) + { + break; + } + + foreach (var pair in item.Split(',')) + { + baggageLength += pair.Length + 1; // pair and comma + + if (baggageLength >= MaxBaggageLength) + { + break; + } + + if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem)) + { + if (baggageDictionary == null) + { + baggageDictionary = new Dictionary(); + } + + baggageDictionary[baggageItem.Name] = baggageItem.Value; + } + } + } + + baggage = baggageDictionary; + return baggageDictionary != null; + } + } + + /// + /// W3C trace context text wire protocol formatter. See https://github.com/w3c/distributed-tracing/. + /// + public class TraceContextFormat : ITextFormat + { + private const string TraceParent = "traceparent"; + private const string TraceState = "tracestate"; + + private static readonly int VersionPrefixIdLength = "00-".Length; + private static readonly int TraceIdLength = "0af7651916cd43dd8448eb211c80319c".Length; + private static readonly int VersionAndTraceIdLength = "00-0af7651916cd43dd8448eb211c80319c-".Length; + private static readonly int SpanIdLength = "00f067aa0ba902b7".Length; + private static readonly int VersionAndTraceIdAndSpanIdLength = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-".Length; + private static readonly int OptionsLength = "00".Length; + private static readonly int TraceparentLengthV0 = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-00".Length; + + /// + public ISet Fields => new HashSet { TraceState, TraceParent }; + /// public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) { @@ -116,16 +203,9 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func> baggage = null; - var baggageCollection = getter(carrier, Baggage); - if (baggageCollection?.Any() ?? false) - { - TryExtractTracestateBaggage(baggageCollection.ToArray(), out baggage); - } - return new TextFormatContext( new ActivityContext(traceId, spanId, traceoptions, tracestate, isRemote: true), - baggage); + context.ActivityBaggage); } catch (Exception ex) { @@ -139,7 +219,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func public void Inject(Activity activity, T carrier, Action setter) { - if (activity.TraceId == default || activity.SpanId == default) + if (activity == null || activity.TraceId == default || activity.SpanId == default) { OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); return; @@ -167,21 +247,6 @@ public void Inject(Activity activity, T carrier, Action se { setter(carrier, TraceState, tracestateStr); } - - using IEnumerator> e = activity.Baggage.GetEnumerator(); - - if (e.MoveNext()) - { - StringBuilder baggage = new StringBuilder(); - do - { - KeyValuePair item = e.Current; - baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(','); - } - while (e.MoveNext()); - baggage.Remove(baggage.Length - 1, 1); - setter(carrier, Baggage, baggage.ToString()); - } } internal static bool TryExtractTraceparent(string traceparent, out ActivityTraceId traceId, out ActivitySpanId spanId, out ActivityTraceFlags traceOptions) @@ -312,43 +377,6 @@ internal static bool TryExtractTracestate(string[] tracestateCollection, out str return true; } - internal static bool TryExtractTracestateBaggage(string[] baggageCollection, out IEnumerable> baggage) - { - int baggageLength = -1; - Dictionary baggageDictionary = null; - - foreach (var item in baggageCollection) - { - if (baggageLength >= MaxBaggageLength) - { - break; - } - - foreach (var pair in item.Split(',')) - { - baggageLength += pair.Length + 1; // pair and comma - - if (baggageLength >= MaxBaggageLength) - { - break; - } - - if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem)) - { - if (baggageDictionary == null) - { - baggageDictionary = new Dictionary(); - } - - baggageDictionary[baggageItem.Name] = baggageItem.Value; - } - } - } - - baggage = baggageDictionary; - return baggageDictionary != null; - } - private static byte HexCharToByte(char c) { if (((c >= '0') && (c <= '9')) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs index 05c2c5efcf4..6203c18d652 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs @@ -81,7 +81,7 @@ public override void OnStartActivity(Activity activity, object payload) return; } - if (this.options.TextFormat.IsInjected(request, HttpRequestMessageHeaderValuesGetter)) + if (this.options.TextFormat.Extract(default, request, HttpRequestMessageHeaderValuesGetter) != default) { // this request is already instrumented, we should back off activity.IsAllDataRequested = false; @@ -107,7 +107,7 @@ public override void OnStartActivity(Activity activity, object payload) if (!(this.httpClientSupportsW3C && this.options.TextFormat is TraceContextFormat)) { - this.options.TextFormat.Inject(activity.Context, request, HttpRequestMessageHeaderValueSetter); + this.options.TextFormat.Inject(activity, request, HttpRequestMessageHeaderValueSetter); } } diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 56782039576..a7e50ed345d 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -186,13 +186,11 @@ private static void AddExceptionTags(Exception exception, Activity activity) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void InstrumentRequest(HttpWebRequest request, Activity activity) - { - Options.TextFormat.Inject(activity, request, HttpWebRequestHeaderValuesSetter); - } + => Options.TextFormat.Inject(activity, request, HttpWebRequestHeaderValuesSetter); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsRequestInstrumented(HttpWebRequest request) - => Options.TextFormat.IsInjected(request, HttpWebRequestHeaderValuesGetter); + => Options.TextFormat.Extract(default, request, HttpWebRequestHeaderValuesGetter) != default; private static void ProcessRequest(HttpWebRequest request) { diff --git a/src/OpenTelemetry/Context/Propagation/B3Format.cs b/src/OpenTelemetry/Context/Propagation/B3Format.cs index 9dc8a6daa4b..f4114533664 100644 --- a/src/OpenTelemetry/Context/Propagation/B3Format.cs +++ b/src/OpenTelemetry/Context/Propagation/B3Format.cs @@ -69,43 +69,6 @@ public B3Format(bool singleHeader) /// public ISet Fields => AllFields; - /// - public bool IsInjected(T carrier, Func> getter) - { - if (carrier == null) - { - OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null carrier"); - return false; - } - - if (getter == null) - { - OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null getter"); - return false; - } - - try - { - if (this.singleHeader) - { - var header = getter(carrier, XB3Combined)?.FirstOrDefault(); - return !string.IsNullOrWhiteSpace(header); - } - else - { - var traceIdStr = getter(carrier, XB3TraceId)?.FirstOrDefault(); - var spanIdStr = getter(carrier, XB3SpanId)?.FirstOrDefault(); - - return traceIdStr != null && spanIdStr != null; - } - } - catch (Exception e) - { - OpenTelemetrySdkEventSource.Log.ContextExtractException(e); - return false; - } - } - /// public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) { @@ -134,7 +97,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func public void Inject(Activity activity, T carrier, Action setter) { - if (activity.TraceId == default || activity.SpanId == default) + if (activity == null || activity.TraceId == default || activity.SpanId == default) { OpenTelemetrySdkEventSource.Log.FailedToInjectContext("invalid context"); return; @@ -218,7 +181,7 @@ private static TextFormatContext ExtractFromMultipleHeaders(TextFormatContext return new TextFormatContext( new ActivityContext(traceId, spanId, traceOptions, isRemote: true), - null); + context.ActivityBaggage); } catch (Exception e) { @@ -278,7 +241,7 @@ private static TextFormatContext ExtractFromSingleHeader(TextFormatContext co return new TextFormatContext( new ActivityContext(traceId, spanId, traceOptions, isRemote: true), - null); + context.ActivityBaggage); } catch (Exception e) { diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs index a81814338bd..03a7cc018f4 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs @@ -65,7 +65,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(Activity activity, T carrier, Action setter) @@ -83,13 +83,5 @@ public void Inject(Activity activity, T carrier, Action se setter(carrier, this.stateHeaderName, tracestateStr); } } - - public bool IsInjected(T carrier, Func> getter) - { - var traceparentCollection = getter(carrier, this.idHeaderName); - - // There must be a single traceparent - return traceparentCollection != null && traceparentCollection.Count() == 1; - } } } From a34a80c0cf06d2b093f7d514bef57bcc104058c7 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Sun, 9 Aug 2020 18:01:16 -0300 Subject: [PATCH 03/33] updating some tests --- .../TracerShim.cs | 6 ++-- .../BasicTests.cs | 5 +-- .../Trace/Propagation/B3FormatTest.cs | 10 ++++-- .../Propagation/CompositePropagatorTest.cs | 31 +++++++++++----- .../Trace/Propagation/TraceContextTest.cs | 36 ++++++++++--------- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs index a75ed3a5f1c..e9e2f6c9b4e 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs +++ b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs @@ -60,7 +60,7 @@ public TracerShim(Trace.Tracer tracer, ITextFormat textFormat) throw new ArgumentNullException(nameof(carrier)); } - ActivityContext activityContext = default; + TextFormatContext textFormatContext = default; if ((format == BuiltinFormats.TextMap || format == BuiltinFormats.HttpHeaders) && carrier is ITextMap textMapCarrier) { @@ -81,10 +81,10 @@ IEnumerable GetCarrierKeyValue(Dictionary> s return value; } - activityContext = this.textFormat.Extract(default, carrierMap, GetCarrierKeyValue); + textFormatContext = this.textFormat.Extract(textFormatContext, carrierMap, GetCarrierKeyValue); } - return !activityContext.IsValid() ? null : new SpanContextShim(new Trace.SpanContext(activityContext)); + return !textFormatContext.ActivityContext.IsValid() ? null : new SpanContextShim(new Trace.SpanContext(textFormatContext.ActivityContext)); } /// diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 2e2c03f5391..5d5f8da6f91 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -144,10 +144,11 @@ public async Task CustomTextFormat() var expectedSpanId = ActivitySpanId.CreateRandom(); var textFormat = new Mock(); - textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new ActivityContext( + textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new TextFormatContext( + new ActivityContext( expectedTraceId, expectedSpanId, - ActivityTraceFlags.Recorded)); + ActivityTraceFlags.Recorded), null)); // Arrange using (var testFactory = this.factory diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs index 0769b4596b2..f26fcd84f91 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs @@ -234,7 +234,10 @@ public void ParseSampled_SingleHeader() { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-1" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, TraceOptions), this.b3FormatSingleHeader.Extract(default, headersSampled, Getter)); + + Assert.Equal( + new TextFormatContext(new ActivityContext(TraceId, SpanId, TraceOptions), null), + this.b3FormatSingleHeader.Extract(default, headersSampled, Getter)); } [Fact] @@ -244,7 +247,10 @@ public void ParseZeroSampled_SingleHeader() { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-0" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter)); + + Assert.Equal( + new TextFormatContext(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), null), + this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter)); } [Fact] diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs index 8e9d26131f0..614bfdcb59a 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs @@ -48,6 +48,21 @@ public class CompositePropagatorTest private readonly ActivityTraceId traceId = ActivityTraceId.CreateRandom(); private readonly ActivitySpanId spanId = ActivitySpanId.CreateRandom(); + static CompositePropagatorTest() + { + Activity.DefaultIdFormat = ActivityIdFormat.W3C; + Activity.ForceDefaultIdFormat = true; + + var listener = new ActivityListener + { + ShouldListenTo = _ => true, + GetRequestedDataUsingParentId = (ref ActivityCreationOptions options) => ActivityDataRequest.AllData, + GetRequestedDataUsingContext = (ref ActivityCreationOptions options) => ActivityDataRequest.AllData, + }; + + ActivitySource.AddActivityListener(listener); + } + [Fact] public void CompositePropagator_NullTextFormatList() { @@ -65,13 +80,11 @@ public void CompositePropagator_TestPropagator() var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null); var carrier = new Dictionary(); + var activity = new Activity("test"); - compositePropagator.Inject(activityContext, carrier, Setter); + compositePropagator.Inject(activity, carrier, Setter); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent-1"); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent-2"); - - bool isInjected = compositePropagator.IsInjected(carrier, Getter); - Assert.True(isInjected); } [Fact] @@ -87,20 +100,20 @@ public void CompositePropagator_UsingSameTag() }); var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null); + TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); + + var activity = new Activity("test"); var carrier = new Dictionary(); - compositePropagator.Inject(activityContext, carrier, Setter); + compositePropagator.Inject(activity, carrier, Setter); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent"); // checking if the latest propagator is the one with the data. So, it will replace the previous one. Assert.Equal($"00-{this.traceId}-{this.spanId}-{header02.Split('-').Last()}", carrier["custom-traceparent"]); - bool isInjected = compositePropagator.IsInjected(carrier, Getter); - Assert.True(isInjected); - // resetting counter count = 0; - ActivityContext newContext = compositePropagator.Extract(default, carrier, Getter); + TextFormatContext newTextFormatContext = compositePropagator.Extract(default, carrier, Getter); // checking if we accessed only two times: header/headerstate options // if that's true, we skipped the first one since we have a logic to for the default result diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs index c5b757f32cb..10c080e3aeb 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs @@ -56,14 +56,14 @@ public void TraceContextFormatCanParseExampleFromSpec() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.TraceId); - Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.SpanId); + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.ActivityContext.SpanId); - Assert.True(ctx.IsRemote); - Assert.True(ctx.IsValid()); - Assert.True((ctx.TraceFlags & ActivityTraceFlags.Recorded) != 0); + Assert.True(ctx.ActivityContext.IsRemote); + Assert.True(ctx.ActivityContext.IsValid()); + Assert.True((ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0); - Assert.Equal($"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01", ctx.TraceState); + Assert.Equal($"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01", ctx.ActivityContext.TraceState); } [Fact] @@ -77,12 +77,12 @@ public void TraceContextFormatNotSampled() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.TraceId); - Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.SpanId); - Assert.True((ctx.TraceFlags & ActivityTraceFlags.Recorded) == 0); + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.ActivityContext.SpanId); + Assert.True((ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) == 0); - Assert.True(ctx.IsRemote); - Assert.True(ctx.IsValid()); + Assert.True(ctx.ActivityContext.IsRemote); + Assert.True(ctx.ActivityContext.IsValid()); } [Fact] @@ -93,7 +93,7 @@ public void TraceContextFormat_IsBlankIfNoHeader() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.False(ctx.IsValid()); + Assert.False(ctx.ActivityContext.IsValid()); } [Fact] @@ -107,7 +107,7 @@ public void TraceContextFormat_IsBlankIfInvalid() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.False(ctx.IsValid()); + Assert.False(ctx.ActivityContext.IsValid()); // TODO: when ActivityContext supports IsRemote // Assert.True(ctx.IsRemote); @@ -124,7 +124,7 @@ public void TraceContextFormat_TracestateToStringEmpty() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.Empty(ctx.TraceState); + Assert.Empty(ctx.ActivityContext.TraceState); } [Fact] @@ -139,7 +139,7 @@ public void TraceContextFormat_TracestateToString() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.Equal("k1=v1,k2=v2,k3=v3", ctx.TraceState); + Assert.Equal("k1=v1,k2=v2,k3=v3", ctx.ActivityContext.TraceState); } [Fact] @@ -153,9 +153,10 @@ public void TraceContextFormat_Inject_NoTracestate() }; var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, traceState: null); + var activity = new Activity("test"); var carrier = new Dictionary(); var f = new TraceContextFormat(); - f.Inject(activityContext, carrier, Setter); + f.Inject(activity, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } @@ -172,9 +173,10 @@ public void TraceContextFormat_Inject_WithTracestate() }; var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, expectedHeaders[TraceState]); + var activity = new Activity("test"); var carrier = new Dictionary(); var f = new TraceContextFormat(); - f.Inject(activityContext, carrier, Setter); + f.Inject(activity, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } From 7483e90b1bf0a110817f8402235456ec3caf89b5 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Sun, 9 Aug 2020 18:18:16 -0300 Subject: [PATCH 04/33] creating nw files --- .../Context/Propagation/BaggageFormat.cs | 144 ++++ .../Context/Propagation/BaseHeaderParser.cs | 83 +++ .../Propagation/CompositePropagator.cs | 6 +- .../Propagation/GenericHeaderParser.cs | 43 ++ .../Context/Propagation/HeaderUtilities.cs | 54 ++ .../Context/Propagation/HttpHeaderParser.cs | 40 ++ .../Context/Propagation/HttpParseResult.cs | 37 + .../Context/Propagation/HttpRuleParser.cs | 263 +++++++ .../Context/Propagation/ITextFormat.cs | 4 +- .../Propagation/NameValueHeaderValue.cs | 120 ++++ .../Context/Propagation/TraceContextFormat.cs | 643 +----------------- 11 files changed, 794 insertions(+), 643 deletions(-) create mode 100644 src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs create mode 100644 src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs create mode 100644 src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs create mode 100644 src/OpenTelemetry.Api/Context/Propagation/HeaderUtilities.cs create mode 100644 src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs create mode 100644 src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs create mode 100644 src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs create mode 100644 src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs new file mode 100644 index 00000000000..299b4f6f4ee --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -0,0 +1,144 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Context.Propagation +{ + // W3C baggage: https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md + public class BaggageFormat : ITextFormat + { + private const string Baggage = "baggage"; + private const int MaxBaggageLength = 1024; + + /// + public ISet Fields => new HashSet { Baggage }; + + /// + public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) + { + if (carrier == null) + { + OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); + return context; + } + + if (getter == null) + { + OpenTelemetryApiEventSource.Log.FailedToExtractContext("null getter"); + return context; + } + + try + { + IEnumerable> baggage = null; + var baggageCollection = getter(carrier, Baggage); + if (baggageCollection?.Any() ?? false) + { + TryExtractTracestateBaggage(baggageCollection.ToArray(), out baggage); + } + + return new TextFormatContext( + context.ActivityContext, + baggage ?? context.ActivityBaggage); + } + catch (Exception ex) + { + OpenTelemetryApiEventSource.Log.ActivityContextExtractException(ex); + } + + return context; + } + + /// + public void Inject(TextFormatContext context, T carrier, Action setter) + { + if (context == null) + { + OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); + return; + } + + if (carrier == null) + { + OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); + return; + } + + if (setter == null) + { + OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null setter"); + return; + } + + using IEnumerator> e = context.ActivityBaggage.GetEnumerator(); + + if (e.MoveNext()) + { + StringBuilder baggage = new StringBuilder(); + do + { + KeyValuePair item = e.Current; + baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(','); + } + while (e.MoveNext()); + baggage.Remove(baggage.Length - 1, 1); + setter(carrier, Baggage, baggage.ToString()); + } + } + + internal static bool TryExtractTracestateBaggage(string[] baggageCollection, out IEnumerable> baggage) + { + int baggageLength = -1; + Dictionary baggageDictionary = null; + + foreach (var item in baggageCollection) + { + if (baggageLength >= MaxBaggageLength) + { + break; + } + + foreach (var pair in item.Split(',')) + { + baggageLength += pair.Length + 1; // pair and comma + + if (baggageLength >= MaxBaggageLength) + { + break; + } + + if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem)) + { + if (baggageDictionary == null) + { + baggageDictionary = new Dictionary(); + } + + baggageDictionary[baggageItem.Name] = baggageItem.Value; + } + } + } + + baggage = baggageDictionary; + return baggageDictionary != null; + } + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs b/src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs new file mode 100644 index 00000000000..8366489d994 --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs @@ -0,0 +1,83 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace OpenTelemetry.Context.Propagation +{ + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/BaseHeaderParser.cs + internal abstract class BaseHeaderParser : HttpHeaderParser + { + protected BaseHeaderParser(bool supportsMultipleValues) + : base(supportsMultipleValues) + { + } + + public sealed override bool TryParseValue(string value, ref int index, out T parsedValue) + { + parsedValue = default(T); + + // If multiple values are supported (i.e. list of values), then accept an empty string: The header may + // be added multiple times to the request/response message. E.g. + // Accept: text/xml; q=1 + // Accept: + // Accept: text/plain; q=0.2 + if (string.IsNullOrEmpty(value) || (index == value.Length)) + { + return SupportsMultipleValues; + } + + var separatorFound = false; + var current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, index, SupportsMultipleValues, out separatorFound); + + if (separatorFound && !SupportsMultipleValues) + { + return false; // leading separators not allowed if we don't support multiple values. + } + + if (current == value.Length) + { + if (SupportsMultipleValues) + { + index = current; + } + + return SupportsMultipleValues; + } + + T result; + var length = GetParsedValueLength(value, current, out result); + + if (length == 0) + { + return false; + } + + current = current + length; + current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, current, SupportsMultipleValues, out separatorFound); + + // If we support multiple values and we've not reached the end of the string, then we must have a separator. + if ((separatorFound && !SupportsMultipleValues) || (!separatorFound && (current < value.Length))) + { + return false; + } + + index = current; + parsedValue = result; + return true; + } + + protected abstract int GetParsedValueLength(string value, int startIndex, out T parsedValue); + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs index 34d49ba389f..752db848a99 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs @@ -16,8 +16,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; namespace OpenTelemetry.Context.Propagation { @@ -53,11 +51,11 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func - public void Inject(Activity activity, T carrier, Action setter) + public void Inject(TextFormatContext context, T carrier, Action setter) { foreach (var textFormat in this.textFormats) { - textFormat.Inject(activity, carrier, setter); + textFormat.Inject(context, carrier, setter); } } } diff --git a/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs b/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs new file mode 100644 index 00000000000..14d78f974d3 --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs @@ -0,0 +1,43 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; + +namespace OpenTelemetry.Context.Propagation +{ + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/GenericHeaderParser.cs + internal sealed class GenericHeaderParser : BaseHeaderParser + { + private GetParsedValueLengthDelegate getParsedValueLength; + + internal GenericHeaderParser(bool supportsMultipleValues, GetParsedValueLengthDelegate getParsedValueLength) + : base(supportsMultipleValues) + { + if (getParsedValueLength == null) + { + throw new ArgumentNullException(nameof(getParsedValueLength)); + } + + this.getParsedValueLength = getParsedValueLength; + } + + internal delegate int GetParsedValueLengthDelegate(string value, int startIndex, out T parsedValue); + + protected override int GetParsedValueLength(string value, int startIndex, out T parsedValue) + { + return getParsedValueLength(value, startIndex, out parsedValue); + } + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/HeaderUtilities.cs b/src/OpenTelemetry.Api/Context/Propagation/HeaderUtilities.cs new file mode 100644 index 00000000000..6c876eeae12 --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/HeaderUtilities.cs @@ -0,0 +1,54 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace OpenTelemetry.Context.Propagation +{ + // Adoption of the code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HeaderUtilities.cs + internal static class HeaderUtilities + { + internal static int GetNextNonEmptyOrWhitespaceIndex( + string input, + int startIndex, + bool skipEmptyValues, + out bool separatorFound) + { + separatorFound = false; + var current = startIndex + HttpRuleParser.GetWhitespaceLength(input, startIndex); + + if ((current == input.Length) || (input[current] != ',')) + { + return current; + } + + // If we have a separator, skip the separator and all following whitespaces. If we support + // empty values, continue until the current character is neither a separator nor a whitespace. + separatorFound = true; + current++; // skip delimiter. + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + + if (skipEmptyValues) + { + while ((current < input.Length) && (input[current] == ',')) + { + current++; // skip delimiter. + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + } + } + + return current; + } + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs b/src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs new file mode 100644 index 00000000000..8b48a663091 --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs @@ -0,0 +1,40 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace OpenTelemetry.Context.Propagation +{ + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpHeaderParser.cs + internal abstract class HttpHeaderParser + { + private bool supportsMultipleValues; + + protected HttpHeaderParser(bool supportsMultipleValues) + { + this.supportsMultipleValues = supportsMultipleValues; + } + + public bool SupportsMultipleValues + { + get { return supportsMultipleValues; } + } + + // If a parser supports multiple values, a call to ParseValue/TryParseValue should return a value for 'index' + // pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0 + // for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first + // non-whitespace after the separator ','. + public abstract bool TryParseValue(string value, ref int index, out T parsedValue); + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs b/src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs new file mode 100644 index 00000000000..8eb174569fc --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs @@ -0,0 +1,37 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace OpenTelemetry.Context.Propagation +{ + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpParseResult.cs + internal enum HttpParseResult + { + /// + /// Parsed succesfully. + /// + Parsed, + + /// + /// Was not parsed. + /// + NotParsed, + + /// + /// Invalid format. + /// + InvalidFormat, + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs b/src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs new file mode 100644 index 00000000000..5f7e11442cc --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs @@ -0,0 +1,263 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace OpenTelemetry.Context.Propagation +{ + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpRuleParser.cs + internal static class HttpRuleParser + { + internal const char CR = '\r'; + internal const char LF = '\n'; + internal const char SP = ' '; + internal const char Tab = '\t'; + internal const int MaxInt64Digits = 19; + internal const int MaxInt32Digits = 10; + + private const int MaxNestedCount = 5; + private static readonly bool[] TokenChars = CreateTokenChars(); + + internal static bool IsTokenChar(char character) + { + // Must be between 'space' (32) and 'DEL' (127) + if (character > 127) + { + return false; + } + + return TokenChars[character]; + } + + internal static int GetTokenLength(string input, int startIndex) + { + if (startIndex >= input.Length) + { + return 0; + } + + var current = startIndex; + + while (current < input.Length) + { + if (!IsTokenChar(input[current])) + { + return current - startIndex; + } + + current++; + } + + return input.Length - startIndex; + } + + internal static int GetWhitespaceLength(string input, int startIndex) + { + if (startIndex >= input.Length) + { + return 0; + } + + var current = startIndex; + + char c; + while (current < input.Length) + { + c = input[current]; + + if ((c == SP) || (c == Tab)) + { + current++; + continue; + } + + if (c == CR) + { + // If we have a #13 char, it must be followed by #10 and then at least one SP or HT. + if ((current + 2 < input.Length) && (input[current + 1] == LF)) + { + char spaceOrTab = input[current + 2]; + if ((spaceOrTab == SP) || (spaceOrTab == Tab)) + { + current += 3; + continue; + } + } + } + + return current - startIndex; + } + + // All characters between startIndex and the end of the string are LWS characters. + return input.Length - startIndex; + } + + internal static HttpParseResult GetQuotedStringLength(string input, int startIndex, out int length) + { + var nestedCount = 0; + return GetExpressionLength(input, startIndex, '"', '"', false, ref nestedCount, out length); + } + + // quoted-pair = "\" CHAR + // CHAR = + internal static HttpParseResult GetQuotedPairLength(string input, int startIndex, out int length) + { + length = 0; + + if (input[startIndex] != '\\') + { + return HttpParseResult.NotParsed; + } + + // Quoted-char has 2 characters. Check wheter there are 2 chars left ('\' + char) + // If so, check whether the character is in the range 0-127. If not, it's an invalid value. + if ((startIndex + 2 > input.Length) || (input[startIndex + 1] > 127)) + { + return HttpParseResult.InvalidFormat; + } + + // We don't care what the char next to '\' is. + length = 2; + return HttpParseResult.Parsed; + } + + private static bool[] CreateTokenChars() + { + // token = 1* + // CTL = + var tokenChars = new bool[128]; // everything is false + + for (int i = 33; i < 127; i++) + { + // skip Space (32) & DEL (127) + tokenChars[i] = true; + } + + // remove separators: these are not valid token characters + tokenChars[(byte)'('] = false; + tokenChars[(byte)')'] = false; + tokenChars[(byte)'<'] = false; + tokenChars[(byte)'>'] = false; + tokenChars[(byte)'@'] = false; + tokenChars[(byte)','] = false; + tokenChars[(byte)';'] = false; + tokenChars[(byte)':'] = false; + tokenChars[(byte)'\\'] = false; + tokenChars[(byte)'"'] = false; + tokenChars[(byte)'/'] = false; + tokenChars[(byte)'['] = false; + tokenChars[(byte)']'] = false; + tokenChars[(byte)'?'] = false; + tokenChars[(byte)'='] = false; + tokenChars[(byte)'{'] = false; + tokenChars[(byte)'}'] = false; + + return tokenChars; + } + + // TEXT = + // LWS = [CRLF] 1*( SP | HT ) + // CTL = + // + // Since we don't really care about the content of a quoted string or comment, we're more tolerant and + // allow these characters. We only want to find the delimiters ('"' for quoted string and '(', ')' for comment). + // + // 'nestedCount': Comments can be nested. We allow a depth of up to 5 nested comments, i.e. something like + // "(((((comment)))))". If we wouldn't define a limit an attacker could send a comment with hundreds of nested + // comments, resulting in a stack overflow exception. In addition having more than 1 nested comment (if any) + // is unusual. + private static HttpParseResult GetExpressionLength( + string input, + int startIndex, + char openChar, + char closeChar, + bool supportsNesting, + ref int nestedCount, + out int length) + { + length = 0; + + if (input[startIndex] != openChar) + { + return HttpParseResult.NotParsed; + } + + var current = startIndex + 1; // Start parsing with the character next to the first open-char + while (current < input.Length) + { + // Only check whether we have a quoted char, if we have at least 3 characters left to read (i.e. + // quoted char + closing char). Otherwise the closing char may be considered part of the quoted char. + var quotedPairLength = 0; + if ((current + 2 < input.Length) && + (GetQuotedPairLength(input, current, out quotedPairLength) == HttpParseResult.Parsed)) + { + // We ignore invalid quoted-pairs. Invalid quoted-pairs may mean that it looked like a quoted pair, + // but we actually have a quoted-string: e.g. "\ü" ('\' followed by a char >127 - quoted-pair only + // allows ASCII chars after '\'; qdtext allows both '\' and >127 chars). + current = current + quotedPairLength; + continue; + } + + // If we support nested expressions and we find an open-char, then parse the nested expressions. + if (supportsNesting && (input[current] == openChar)) + { + nestedCount++; + try + { + // Check if we exceeded the number of nested calls. + if (nestedCount > MaxNestedCount) + { + return HttpParseResult.InvalidFormat; + } + + var nestedLength = 0; + HttpParseResult nestedResult = GetExpressionLength(input, current, openChar, closeChar, supportsNesting, ref nestedCount, out nestedLength); + + switch (nestedResult) + { + case HttpParseResult.Parsed: + current += nestedLength; // add the length of the nested expression and continue. + break; + + case HttpParseResult.NotParsed: + break; + + case HttpParseResult.InvalidFormat: + // If the nested expression is invalid, we can't continue, so we fail with invalid format. + return HttpParseResult.InvalidFormat; + + default: + break; + } + } + finally + { + nestedCount--; + } + } + + if (input[current] == closeChar) + { + length = current - startIndex + 1; + return HttpParseResult.Parsed; + } + + current++; + } + + // We didn't see the final quote, therefore we have an invalid expression string. + return HttpParseResult.InvalidFormat; + } + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs index 6cb0fcdd580..1a0e602b372 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs @@ -36,6 +36,7 @@ public TextFormatContext(ActivityContext activityContext, IEnumerable !(left == right); + /// public bool Equals(TextFormatContext value) { if (this.ActivityContext != value.ActivityContext @@ -69,6 +70,7 @@ public bool Equals(TextFormatContext value) return true; } + /// public override bool Equals(object? obj) => (obj is TextFormatContext context) ? Equals(context) : false; } @@ -92,7 +94,7 @@ public interface ITextFormat /// Activity to transmit over the wire. /// Object to set context on. Instance of this object will be passed to setter. /// Action that will set name and value pair on the object. - void Inject(Activity activity, T carrier, Action setter); + void Inject(TextFormatContext context, T carrier, Action setter); /// /// Extracts activity context from textual representation. diff --git a/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs b/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs new file mode 100644 index 00000000000..2276ae2014f --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs @@ -0,0 +1,120 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace OpenTelemetry.Context.Propagation +{ + + // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/NameValueHeaderValue.cs + internal class NameValueHeaderValue + { + private static readonly HttpHeaderParser SingleValueParser + = new GenericHeaderParser(false, GetNameValueLength); + + private string name; + private string value; + + private NameValueHeaderValue() + { + // Used by the parser to create a new instance of this type. + } + + public string Name + { + get { return name; } + } + + public string Value + { + get { return value; } + } + + public static bool TryParse(string input, out NameValueHeaderValue parsedValue) + { + var index = 0; + return SingleValueParser.TryParseValue(input, ref index, out parsedValue); + } + + internal static int GetValueLength(string input, int startIndex) + { + if (startIndex >= input.Length) + { + return 0; + } + + var valueLength = HttpRuleParser.GetTokenLength(input, startIndex); + + if (valueLength == 0) + { + // A value can either be a token or a quoted string. Check if it is a quoted string. + if (HttpRuleParser.GetQuotedStringLength(input, startIndex, out valueLength) != HttpParseResult.Parsed) + { + // We have an invalid value. Reset the name and return. + return 0; + } + } + + return valueLength; + } + + private static int GetNameValueLength(string input, int startIndex, out NameValueHeaderValue parsedValue) + { + parsedValue = null; + + if (string.IsNullOrEmpty(input) || (startIndex >= input.Length)) + { + return 0; + } + + // Parse the name, i.e. in name/value string "=". Caller must remove + // leading whitespaces. + var nameLength = HttpRuleParser.GetTokenLength(input, startIndex); + + if (nameLength == 0) + { + return 0; + } + + var name = input.Substring(startIndex, nameLength); + var current = startIndex + nameLength; + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + + // Parse the separator between name and value + if ((current == input.Length) || (input[current] != '=')) + { + // We only have a name and that's OK. Return. + parsedValue = new NameValueHeaderValue(); + parsedValue.name = name; + current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces + return current - startIndex; + } + + current++; // skip delimiter. + current = current + HttpRuleParser.GetWhitespaceLength(input, current); + + // Parse the value, i.e. in name/value string "=" + int valueLength = GetValueLength(input, current); + + // Value after the '=' may be empty + // Use parameterless ctor to avoid double-parsing of name and value, i.e. skip public ctor validation. + parsedValue = new NameValueHeaderValue(); + parsedValue.name = name; + parsedValue.value = input.Substring(current, valueLength); + current = current + valueLength; + current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces + return current - startIndex; + } + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index 4568173072d..9230d0f1f51 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -15,134 +15,13 @@ // using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Diagnostics; using System.Linq; -using System.Net; using System.Text; using OpenTelemetry.Internal; namespace OpenTelemetry.Context.Propagation { - // W3C baggage: https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md - public class BaggageFormat : ITextFormat - { - private const string Baggage = "baggage"; - private const int MaxBaggageLength = 1024; - - /// - public ISet Fields => new HashSet { Baggage }; - - /// - public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) - { - if (carrier == null) - { - OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); - return context; - } - - if (getter == null) - { - OpenTelemetryApiEventSource.Log.FailedToExtractContext("null getter"); - return context; - } - - try - { - IEnumerable> baggage = null; - var baggageCollection = getter(carrier, Baggage); - if (baggageCollection?.Any() ?? false) - { - TryExtractTracestateBaggage(baggageCollection.ToArray(), out baggage); - } - - return new TextFormatContext( - context.ActivityContext, - baggage ?? context.ActivityBaggage); - } - catch (Exception ex) - { - OpenTelemetryApiEventSource.Log.ActivityContextExtractException(ex); - } - - return context; - } - - /// - public void Inject(Activity activity, T carrier, Action setter) - { - if (activity == null) - { - OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); - return; - } - - if (carrier == null) - { - OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); - return; - } - - if (setter == null) - { - OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null setter"); - return; - } - - using IEnumerator> e = activity.Baggage.GetEnumerator(); - - if (e.MoveNext()) - { - StringBuilder baggage = new StringBuilder(); - do - { - KeyValuePair item = e.Current; - baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(','); - } - while (e.MoveNext()); - baggage.Remove(baggage.Length - 1, 1); - setter(carrier, Baggage, baggage.ToString()); - } - } - - internal static bool TryExtractTracestateBaggage(string[] baggageCollection, out IEnumerable> baggage) - { - int baggageLength = -1; - Dictionary baggageDictionary = null; - - foreach (var item in baggageCollection) - { - if (baggageLength >= MaxBaggageLength) - { - break; - } - - foreach (var pair in item.Split(',')) - { - baggageLength += pair.Length + 1; // pair and comma - - if (baggageLength >= MaxBaggageLength) - { - break; - } - - if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem)) - { - if (baggageDictionary == null) - { - baggageDictionary = new Dictionary(); - } - - baggageDictionary[baggageItem.Name] = baggageItem.Value; - } - } - } - - baggage = baggageDictionary; - return baggageDictionary != null; - } - } /// /// W3C trace context text wire protocol formatter. See https://github.com/w3c/distributed-tracing/. @@ -217,9 +96,9 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func - public void Inject(Activity activity, T carrier, Action setter) + public void Inject(TextFormatContext context, T carrier, Action setter) { - if (activity == null || activity.TraceId == default || activity.SpanId == default) + if (context == null || context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) { OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); return; @@ -237,12 +116,12 @@ public void Inject(Activity activity, T carrier, Action se return; } - var traceparent = string.Concat("00-", activity.TraceId.ToHexString(), "-", activity.SpanId.ToHexString()); - traceparent = string.Concat(traceparent, (activity.ActivityTraceFlags & ActivityTraceFlags.Recorded) != 0 ? "-01" : "-00"); + var traceparent = string.Concat("00-", context.ActivityContext.TraceId.ToHexString(), "-", context.ActivityContext.SpanId.ToHexString()); + traceparent = string.Concat(traceparent, (context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0 ? "-01" : "-00"); setter(carrier, TraceParent, traceparent); - string tracestateStr = activity.TraceStateString; + string tracestateStr = context.ActivityContext.TraceState; if (tracestateStr?.Length > 0) { setter(carrier, TraceState, tracestateStr); @@ -389,516 +268,4 @@ private static byte HexCharToByte(char c) throw new ArgumentOutOfRangeException(nameof(c), $"Invalid character: {c}."); } } - - // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/NameValueHeaderValue.cs - internal class NameValueHeaderValue - { - private static readonly HttpHeaderParser SingleValueParser - = new GenericHeaderParser(false, GetNameValueLength); - - private string name; - private string value; - - private NameValueHeaderValue() - { - // Used by the parser to create a new instance of this type. - } - - public string Name - { - get { return name; } - } - - public string Value - { - get { return value; } - } - - public static bool TryParse(string input, out NameValueHeaderValue parsedValue) - { - var index = 0; - return SingleValueParser.TryParseValue(input, ref index, out parsedValue); - } - - internal static int GetValueLength(string input, int startIndex) - { - if (startIndex >= input.Length) - { - return 0; - } - - var valueLength = HttpRuleParser.GetTokenLength(input, startIndex); - - if (valueLength == 0) - { - // A value can either be a token or a quoted string. Check if it is a quoted string. - if (HttpRuleParser.GetQuotedStringLength(input, startIndex, out valueLength) != HttpParseResult.Parsed) - { - // We have an invalid value. Reset the name and return. - return 0; - } - } - - return valueLength; - } - - private static int GetNameValueLength(string input, int startIndex, out NameValueHeaderValue parsedValue) - { - parsedValue = null; - - if (string.IsNullOrEmpty(input) || (startIndex >= input.Length)) - { - return 0; - } - - // Parse the name, i.e. in name/value string "=". Caller must remove - // leading whitespaces. - var nameLength = HttpRuleParser.GetTokenLength(input, startIndex); - - if (nameLength == 0) - { - return 0; - } - - var name = input.Substring(startIndex, nameLength); - var current = startIndex + nameLength; - current = current + HttpRuleParser.GetWhitespaceLength(input, current); - - // Parse the separator between name and value - if ((current == input.Length) || (input[current] != '=')) - { - // We only have a name and that's OK. Return. - parsedValue = new NameValueHeaderValue(); - parsedValue.name = name; - current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces - return current - startIndex; - } - - current++; // skip delimiter. - current = current + HttpRuleParser.GetWhitespaceLength(input, current); - - // Parse the value, i.e. in name/value string "=" - int valueLength = GetValueLength(input, current); - - // Value after the '=' may be empty - // Use parameterless ctor to avoid double-parsing of name and value, i.e. skip public ctor validation. - parsedValue = new NameValueHeaderValue(); - parsedValue.name = name; - parsedValue.value = input.Substring(current, valueLength); - current = current + valueLength; - current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces - return current - startIndex; - } - } - - // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpHeaderParser.cs - internal abstract class HttpHeaderParser - { - private bool supportsMultipleValues; - - protected HttpHeaderParser(bool supportsMultipleValues) - { - this.supportsMultipleValues = supportsMultipleValues; - } - - public bool SupportsMultipleValues - { - get { return supportsMultipleValues; } - } - - // If a parser supports multiple values, a call to ParseValue/TryParseValue should return a value for 'index' - // pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0 - // for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first - // non-whitespace after the separator ','. - public abstract bool TryParseValue(string value, ref int index, out T parsedValue); - } - - // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpParseResult.cs - internal enum HttpParseResult - { - /// - /// Parsed succesfully. - /// - Parsed, - - /// - /// Was not parsed. - /// - NotParsed, - - /// - /// Invalid format. - /// - InvalidFormat, - } - - // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpRuleParser.cs - internal static class HttpRuleParser - { - internal const char CR = '\r'; - internal const char LF = '\n'; - internal const char SP = ' '; - internal const char Tab = '\t'; - internal const int MaxInt64Digits = 19; - internal const int MaxInt32Digits = 10; - - private const int MaxNestedCount = 5; - private static readonly bool[] TokenChars = CreateTokenChars(); - - internal static bool IsTokenChar(char character) - { - // Must be between 'space' (32) and 'DEL' (127) - if (character > 127) - { - return false; - } - - return TokenChars[character]; - } - - internal static int GetTokenLength(string input, int startIndex) - { - if (startIndex >= input.Length) - { - return 0; - } - - var current = startIndex; - - while (current < input.Length) - { - if (!IsTokenChar(input[current])) - { - return current - startIndex; - } - - current++; - } - - return input.Length - startIndex; - } - - internal static int GetWhitespaceLength(string input, int startIndex) - { - if (startIndex >= input.Length) - { - return 0; - } - - var current = startIndex; - - char c; - while (current < input.Length) - { - c = input[current]; - - if ((c == SP) || (c == Tab)) - { - current++; - continue; - } - - if (c == CR) - { - // If we have a #13 char, it must be followed by #10 and then at least one SP or HT. - if ((current + 2 < input.Length) && (input[current + 1] == LF)) - { - char spaceOrTab = input[current + 2]; - if ((spaceOrTab == SP) || (spaceOrTab == Tab)) - { - current += 3; - continue; - } - } - } - - return current - startIndex; - } - - // All characters between startIndex and the end of the string are LWS characters. - return input.Length - startIndex; - } - - internal static HttpParseResult GetQuotedStringLength(string input, int startIndex, out int length) - { - var nestedCount = 0; - return GetExpressionLength(input, startIndex, '"', '"', false, ref nestedCount, out length); - } - - // quoted-pair = "\" CHAR - // CHAR = - internal static HttpParseResult GetQuotedPairLength(string input, int startIndex, out int length) - { - length = 0; - - if (input[startIndex] != '\\') - { - return HttpParseResult.NotParsed; - } - - // Quoted-char has 2 characters. Check wheter there are 2 chars left ('\' + char) - // If so, check whether the character is in the range 0-127. If not, it's an invalid value. - if ((startIndex + 2 > input.Length) || (input[startIndex + 1] > 127)) - { - return HttpParseResult.InvalidFormat; - } - - // We don't care what the char next to '\' is. - length = 2; - return HttpParseResult.Parsed; - } - - private static bool[] CreateTokenChars() - { - // token = 1* - // CTL = - var tokenChars = new bool[128]; // everything is false - - for (int i = 33; i < 127; i++) - { - // skip Space (32) & DEL (127) - tokenChars[i] = true; - } - - // remove separators: these are not valid token characters - tokenChars[(byte)'('] = false; - tokenChars[(byte)')'] = false; - tokenChars[(byte)'<'] = false; - tokenChars[(byte)'>'] = false; - tokenChars[(byte)'@'] = false; - tokenChars[(byte)','] = false; - tokenChars[(byte)';'] = false; - tokenChars[(byte)':'] = false; - tokenChars[(byte)'\\'] = false; - tokenChars[(byte)'"'] = false; - tokenChars[(byte)'/'] = false; - tokenChars[(byte)'['] = false; - tokenChars[(byte)']'] = false; - tokenChars[(byte)'?'] = false; - tokenChars[(byte)'='] = false; - tokenChars[(byte)'{'] = false; - tokenChars[(byte)'}'] = false; - - return tokenChars; - } - - // TEXT = - // LWS = [CRLF] 1*( SP | HT ) - // CTL = - // - // Since we don't really care about the content of a quoted string or comment, we're more tolerant and - // allow these characters. We only want to find the delimiters ('"' for quoted string and '(', ')' for comment). - // - // 'nestedCount': Comments can be nested. We allow a depth of up to 5 nested comments, i.e. something like - // "(((((comment)))))". If we wouldn't define a limit an attacker could send a comment with hundreds of nested - // comments, resulting in a stack overflow exception. In addition having more than 1 nested comment (if any) - // is unusual. - private static HttpParseResult GetExpressionLength( - string input, - int startIndex, - char openChar, - char closeChar, - bool supportsNesting, - ref int nestedCount, - out int length) - { - length = 0; - - if (input[startIndex] != openChar) - { - return HttpParseResult.NotParsed; - } - - var current = startIndex + 1; // Start parsing with the character next to the first open-char - while (current < input.Length) - { - // Only check whether we have a quoted char, if we have at least 3 characters left to read (i.e. - // quoted char + closing char). Otherwise the closing char may be considered part of the quoted char. - var quotedPairLength = 0; - if ((current + 2 < input.Length) && - (GetQuotedPairLength(input, current, out quotedPairLength) == HttpParseResult.Parsed)) - { - // We ignore invalid quoted-pairs. Invalid quoted-pairs may mean that it looked like a quoted pair, - // but we actually have a quoted-string: e.g. "\ü" ('\' followed by a char >127 - quoted-pair only - // allows ASCII chars after '\'; qdtext allows both '\' and >127 chars). - current = current + quotedPairLength; - continue; - } - - // If we support nested expressions and we find an open-char, then parse the nested expressions. - if (supportsNesting && (input[current] == openChar)) - { - nestedCount++; - try - { - // Check if we exceeded the number of nested calls. - if (nestedCount > MaxNestedCount) - { - return HttpParseResult.InvalidFormat; - } - - var nestedLength = 0; - HttpParseResult nestedResult = GetExpressionLength(input, current, openChar, closeChar, supportsNesting, ref nestedCount, out nestedLength); - - switch (nestedResult) - { - case HttpParseResult.Parsed: - current += nestedLength; // add the length of the nested expression and continue. - break; - - case HttpParseResult.NotParsed: - break; - - case HttpParseResult.InvalidFormat: - // If the nested expression is invalid, we can't continue, so we fail with invalid format. - return HttpParseResult.InvalidFormat; - - default: - break; - } - } - finally - { - nestedCount--; - } - } - - if (input[current] == closeChar) - { - length = current - startIndex + 1; - return HttpParseResult.Parsed; - } - - current++; - } - - // We didn't see the final quote, therefore we have an invalid expression string. - return HttpParseResult.InvalidFormat; - } - } - - // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/BaseHeaderParser.cs - internal abstract class BaseHeaderParser : HttpHeaderParser - { - protected BaseHeaderParser(bool supportsMultipleValues) - : base(supportsMultipleValues) - { - } - - public sealed override bool TryParseValue(string value, ref int index, out T parsedValue) - { - parsedValue = default(T); - - // If multiple values are supported (i.e. list of values), then accept an empty string: The header may - // be added multiple times to the request/response message. E.g. - // Accept: text/xml; q=1 - // Accept: - // Accept: text/plain; q=0.2 - if (string.IsNullOrEmpty(value) || (index == value.Length)) - { - return SupportsMultipleValues; - } - - var separatorFound = false; - var current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, index, SupportsMultipleValues, out separatorFound); - - if (separatorFound && !SupportsMultipleValues) - { - return false; // leading separators not allowed if we don't support multiple values. - } - - if (current == value.Length) - { - if (SupportsMultipleValues) - { - index = current; - } - - return SupportsMultipleValues; - } - - T result; - var length = GetParsedValueLength(value, current, out result); - - if (length == 0) - { - return false; - } - - current = current + length; - current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, current, SupportsMultipleValues, out separatorFound); - - // If we support multiple values and we've not reached the end of the string, then we must have a separator. - if ((separatorFound && !SupportsMultipleValues) || (!separatorFound && (current < value.Length))) - { - return false; - } - - index = current; - parsedValue = result; - return true; - } - - protected abstract int GetParsedValueLength(string value, int startIndex, out T parsedValue); - } - - // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/GenericHeaderParser.cs - internal sealed class GenericHeaderParser : BaseHeaderParser - { - private GetParsedValueLengthDelegate getParsedValueLength; - - internal GenericHeaderParser(bool supportsMultipleValues, GetParsedValueLengthDelegate getParsedValueLength) - : base(supportsMultipleValues) - { - if (getParsedValueLength == null) - { - throw new ArgumentNullException(nameof(getParsedValueLength)); - } - - this.getParsedValueLength = getParsedValueLength; - } - - internal delegate int GetParsedValueLengthDelegate(string value, int startIndex, out T parsedValue); - - protected override int GetParsedValueLength(string value, int startIndex, out T parsedValue) - { - return getParsedValueLength(value, startIndex, out parsedValue); - } - } - - // Adoption of the code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HeaderUtilities.cs - internal static class HeaderUtilities - { - internal static int GetNextNonEmptyOrWhitespaceIndex( - string input, - int startIndex, - bool skipEmptyValues, - out bool separatorFound) - { - separatorFound = false; - var current = startIndex + HttpRuleParser.GetWhitespaceLength(input, startIndex); - - if ((current == input.Length) || (input[current] != ',')) - { - return current; - } - - // If we have a separator, skip the separator and all following whitespaces. If we support - // empty values, continue until the current character is neither a separator nor a whitespace. - separatorFound = true; - current++; // skip delimiter. - current = current + HttpRuleParser.GetWhitespaceLength(input, current); - - if (skipEmptyValues) - { - while ((current < input.Length) && (input[current] == ',')) - { - current++; // skip delimiter. - current = current + HttpRuleParser.GetWhitespaceLength(input, current); - } - } - - return current; - } - } } From 4d7fd7df0a087e6f6bfa7a28822266151142828d Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Sun, 9 Aug 2020 18:43:44 -0300 Subject: [PATCH 05/33] updating files --- .../Context/Propagation/BaseHeaderParser.cs | 16 +++--- .../Propagation/GenericHeaderParser.cs | 2 +- .../Context/Propagation/HttpHeaderParser.cs | 2 +- .../Context/Propagation/ITextFormat.cs | 2 +- .../Propagation/NameValueHeaderValue.cs | 4 +- .../HttpHandlerDiagnosticListener.cs | 2 +- .../HttpWebRequestActivitySource.netfx.cs | 3 +- .../TracerShim.cs | 2 +- .../Context/Propagation/B3Format.cs | 16 +++--- .../HttpInListenerTests.cs | 10 ++-- .../HttpClientTests.Basic.netcore31.cs | 38 +++++++------- .../HttpWebRequestTests.Basic.netfx.cs | 6 +-- .../Trace/Propagation/B3FormatTest.cs | 49 ++++++++++++------- .../Propagation/CompositePropagatorTest.cs | 6 +-- .../Trace/Propagation/TestPropagator.cs | 6 +-- .../Trace/Propagation/TraceContextTest.cs | 8 +-- 16 files changed, 92 insertions(+), 80 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs b/src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs index 8366489d994..d1b0df2ba38 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaseHeaderParser.cs @@ -35,29 +35,29 @@ public sealed override bool TryParseValue(string value, ref int index, out T par // Accept: text/plain; q=0.2 if (string.IsNullOrEmpty(value) || (index == value.Length)) { - return SupportsMultipleValues; + return this.SupportsMultipleValues; } var separatorFound = false; - var current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, index, SupportsMultipleValues, out separatorFound); + var current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, index, this.SupportsMultipleValues, out separatorFound); - if (separatorFound && !SupportsMultipleValues) + if (separatorFound && !this.SupportsMultipleValues) { return false; // leading separators not allowed if we don't support multiple values. } if (current == value.Length) { - if (SupportsMultipleValues) + if (this.SupportsMultipleValues) { index = current; } - return SupportsMultipleValues; + return this.SupportsMultipleValues; } T result; - var length = GetParsedValueLength(value, current, out result); + var length = this.GetParsedValueLength(value, current, out result); if (length == 0) { @@ -65,10 +65,10 @@ public sealed override bool TryParseValue(string value, ref int index, out T par } current = current + length; - current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, current, SupportsMultipleValues, out separatorFound); + current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, current, this.SupportsMultipleValues, out separatorFound); // If we support multiple values and we've not reached the end of the string, then we must have a separator. - if ((separatorFound && !SupportsMultipleValues) || (!separatorFound && (current < value.Length))) + if ((separatorFound && !this.SupportsMultipleValues) || (!separatorFound && (current < value.Length))) { return false; } diff --git a/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs b/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs index 14d78f974d3..cf029b22e82 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs @@ -37,7 +37,7 @@ internal GenericHeaderParser(bool supportsMultipleValues, GetParsedValueLengthDe protected override int GetParsedValueLength(string value, int startIndex, out T parsedValue) { - return getParsedValueLength(value, startIndex, out parsedValue); + return this.getParsedValueLength(value, startIndex, out parsedValue); } } } diff --git a/src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs b/src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs index 8b48a663091..95e5a09c498 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/HttpHeaderParser.cs @@ -28,7 +28,7 @@ protected HttpHeaderParser(bool supportsMultipleValues) public bool SupportsMultipleValues { - get { return supportsMultipleValues; } + get { return this.supportsMultipleValues; } } // If a parser supports multiple values, a call to ParseValue/TryParseValue should return a value for 'index' diff --git a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs index 1a0e602b372..da635904c92 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs @@ -71,7 +71,7 @@ public bool Equals(TextFormatContext value) } /// - public override bool Equals(object? obj) => (obj is TextFormatContext context) ? Equals(context) : false; + public override bool Equals(object? obj) => (obj is TextFormatContext context) ? this.Equals(context) : false; } /// diff --git a/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs b/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs index 2276ae2014f..105b1bb400a 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs @@ -33,12 +33,12 @@ private NameValueHeaderValue() public string Name { - get { return name; } + get { return this.name; } } public string Value { - get { return value; } + get { return this.value; } } public static bool TryParse(string input, out NameValueHeaderValue parsedValue) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs index 6203c18d652..83cf32dc7e7 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs @@ -107,7 +107,7 @@ public override void OnStartActivity(Activity activity, object payload) if (!(this.httpClientSupportsW3C && this.options.TextFormat is TraceContextFormat)) { - this.options.TextFormat.Inject(activity, request, HttpRequestMessageHeaderValueSetter); + this.options.TextFormat.Inject(new TextFormatContext(activity.Context, activity.Baggage), request, HttpRequestMessageHeaderValueSetter); } } diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index a7e50ed345d..4732ddf7c45 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -23,6 +23,7 @@ using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Text; +using OpenTelemetry.Context.Propagation; using OpenTelemetry.Trace; namespace OpenTelemetry.Instrumentation.Http.Implementation @@ -186,7 +187,7 @@ private static void AddExceptionTags(Exception exception, Activity activity) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void InstrumentRequest(HttpWebRequest request, Activity activity) - => Options.TextFormat.Inject(activity, request, HttpWebRequestHeaderValuesSetter); + => Options.TextFormat.Inject(new TextFormatContext(activity.Context, activity.Baggage), request, HttpWebRequestHeaderValuesSetter); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsRequestInstrumented(HttpWebRequest request) diff --git a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs index e9e2f6c9b4e..73e3af039d7 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs +++ b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs @@ -115,7 +115,7 @@ public void Inject( if ((format == BuiltinFormats.TextMap || format == BuiltinFormats.HttpHeaders) && carrier is ITextMap textMapCarrier) { - this.textFormat.Inject(shim.SpanContext, textMapCarrier, (instrumentation, key, value) => instrumentation.Set(key, value)); + this.textFormat.Inject(new TextFormatContext(shim.SpanContext, null), textMapCarrier, (instrumentation, key, value) => instrumentation.Set(key, value)); } } } diff --git a/src/OpenTelemetry/Context/Propagation/B3Format.cs b/src/OpenTelemetry/Context/Propagation/B3Format.cs index f4114533664..10d4e57e92d 100644 --- a/src/OpenTelemetry/Context/Propagation/B3Format.cs +++ b/src/OpenTelemetry/Context/Propagation/B3Format.cs @@ -95,9 +95,9 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func - public void Inject(Activity activity, T carrier, Action setter) + public void Inject(TextFormatContext context, T carrier, Action setter) { - if (activity == null || activity.TraceId == default || activity.SpanId == default) + if (context == null || context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) { OpenTelemetrySdkEventSource.Log.FailedToInjectContext("invalid context"); return; @@ -118,10 +118,10 @@ public void Inject(Activity activity, T carrier, Action se if (this.singleHeader) { var sb = new StringBuilder(); - sb.Append(activity.TraceId.ToHexString()); + sb.Append(context.ActivityContext.TraceId.ToHexString()); sb.Append(XB3CombinedDelimiter); - sb.Append(activity.SpanId.ToHexString()); - if ((activity.ActivityTraceFlags & ActivityTraceFlags.Recorded) != 0) + sb.Append(context.ActivityContext.SpanId.ToHexString()); + if ((context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0) { sb.Append(XB3CombinedDelimiter); sb.Append(SampledValue); @@ -131,9 +131,9 @@ public void Inject(Activity activity, T carrier, Action se } else { - setter(carrier, XB3TraceId, activity.TraceId.ToHexString()); - setter(carrier, XB3SpanId, activity.SpanId.ToHexString()); - if ((activity.ActivityTraceFlags & ActivityTraceFlags.Recorded) != 0) + setter(carrier, XB3TraceId, context.ActivityContext.TraceId.ToHexString()); + setter(carrier, XB3SpanId, context.ActivityContext.SpanId.ToHexString()); + if ((context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0) { setter(carrier, XB3Sampled, SampledValue); } diff --git a/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs b/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs index 9bd6a196024..6deefdc5791 100644 --- a/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs @@ -129,10 +129,12 @@ public void AspNetRequestsAreCollectedSuccessfully( var expectedTraceId = ActivityTraceId.CreateRandom(); var expectedSpanId = ActivitySpanId.CreateRandom(); var textFormat = new Mock(); - textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new ActivityContext( - expectedTraceId, - expectedSpanId, - ActivityTraceFlags.Recorded)); + textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new TextFormatContext( + new ActivityContext( + expectedTraceId, + expectedSpanId, + ActivityTraceFlags.Recorded), + null)); var activity = new Activity(ActivityNameAspNet).AddBaggage("Stuff", "123"); activity.SetParentId(expectedTraceId, expectedSpanId, ActivityTraceFlags.Recorded); diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs index fc54c3ff1ec..785c78eb88f 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs @@ -73,22 +73,23 @@ public async Task HttpClientInstrumentationInjectsHeadersAsync() // Ensure that the header value func does not throw if the header key can't be found var mockTextFormat = new Mock(); - var isInjectedHeaderValueGetterThrows = false; - mockTextFormat - .Setup(x => x.IsInjected(It.IsAny(), It.IsAny>>())) - .Callback>>( - (carrier, getter) => - { - try - { - // traceparent doesn't exist - getter(carrier, "traceparent"); - } - catch - { - isInjectedHeaderValueGetterThrows = true; - } - }); + + // var isInjectedHeaderValueGetterThrows = false; + // mockTextFormat + // .Setup(x => x.IsInjected(It.IsAny(), It.IsAny>>())) + // .Callback>>( + // (carrier, getter) => + // { + // try + // { + // // traceparent doesn't exist + // getter(carrier, "traceparent"); + // } + // catch + // { + // isInjectedHeaderValueGetterThrows = true; + // } + // }); using (Sdk.CreateTracerProviderBuilder() .AddHttpClientInstrumentation(o => o.TextFormat = mockTextFormat.Object) @@ -114,16 +115,13 @@ public async Task HttpClientInstrumentationInjectsHeadersAsync() Assert.Equal($"00-{span.Context.TraceId}-{span.Context.SpanId}-01", traceparents.Single()); Assert.Equal("k1=v1,k2=v2", tracestates.Single()); - - mockTextFormat.Verify(x => x.IsInjected(It.IsAny(), It.IsAny>>()), Times.Once); - Assert.False(isInjectedHeaderValueGetterThrows); } [Fact] public async Task HttpClientInstrumentationInjectsHeadersAsync_CustomFormat() { var textFormat = new Mock(); - textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) + textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) .Callback>((context, message, action) => { action(message, "custom_traceparent", $"00/{context.TraceId}/{context.SpanId}/01"); diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs index 5f64525fbc0..dbb9fc9bad4 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs @@ -97,10 +97,10 @@ public async Task HttpWebRequestInstrumentationInjectsHeadersAsync() public async Task HttpWebRequestInstrumentationInjectsHeadersAsync_CustomFormat() { var textFormat = new Mock(); - textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((context, message, action) => + textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) + .Callback>((context, message, action) => { - action(message, "custom_traceparent", $"00/{context.TraceId}/{context.SpanId}/01"); + action(message, "custom_traceparent", $"00/{context.ActivityContext.TraceId}/{context.ActivityContext.SpanId}/01"); action(message, "custom_tracestate", Activity.Current.TraceStateString); }); diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs index f26fcd84f91..212869d0948 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/B3FormatTest.cs @@ -56,7 +56,7 @@ public B3FormatTest(ITestOutputHelper output) public void Serialize_SampledContext() { var carrier = new Dictionary(); - this.b3Format.Inject(new ActivityContext(TraceId, SpanId, TraceOptions), carrier, Setter); + this.b3Format.Inject(new TextFormatContext(new ActivityContext(TraceId, SpanId, TraceOptions), null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "1" } }); } @@ -66,7 +66,7 @@ public void Serialize_NotSampledContext() var carrier = new Dictionary(); var context = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); this.output.WriteLine(context.ToString()); - this.b3Format.Inject(context, carrier, Setter); + this.b3Format.Inject(new TextFormatContext(context, null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 } }); } @@ -78,7 +78,7 @@ public void ParseMissingSampledAndMissingFlag() { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, }; var spanContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - Assert.Equal(spanContext, this.b3Format.Extract(default, headersNotSampled, Getter)); + Assert.Equal(new TextFormatContext(spanContext, null), this.b3Format.Extract(default, headersNotSampled, Getter)); } [Fact] @@ -88,7 +88,8 @@ public void ParseSampled() { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "1" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, TraceOptions), this.b3Format.Extract(default, headersSampled, Getter)); + var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersSampled, Getter)); } [Fact] @@ -98,7 +99,8 @@ public void ParseZeroSampled() { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "0" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), this.b3Format.Extract(default, headersNotSampled, Getter)); + var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersNotSampled, Getter)); } [Fact] @@ -108,7 +110,8 @@ public void ParseFlag() { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Flags, "1" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, TraceOptions), this.b3Format.Extract(default, headersFlagSampled, Getter)); + var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersFlagSampled, Getter)); } [Fact] @@ -118,7 +121,8 @@ public void ParseZeroFlag() { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Flags, "0" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), this.b3Format.Extract(default, headersFlagNotSampled, Getter)); + var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersFlagNotSampled, Getter)); } [Fact] @@ -130,7 +134,8 @@ public void ParseEightBytesTraceId() { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "1" }, }; - Assert.Equal(new ActivityContext(TraceIdEightBytes, SpanId, TraceOptions), this.b3Format.Extract(default, headersEightBytes, Getter)); + var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, TraceOptions); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter)); } [Fact] @@ -140,7 +145,8 @@ public void ParseEightBytesTraceId_NotSampledSpanContext() { { B3Format.XB3TraceId, TraceIdBase16EightBytes }, { B3Format.XB3SpanId, SpanIdBase16 }, }; - Assert.Equal(new ActivityContext(TraceIdEightBytes, SpanId, ActivityTraceFlags.None), this.b3Format.Extract(default, headersEightBytes, Getter)); + var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, ActivityTraceFlags.None); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter)); } [Fact] @@ -202,7 +208,8 @@ public void ParseMissingSpanId() public void Serialize_SampledContext_SingleHeader() { var carrier = new Dictionary(); - this.b3FormatSingleHeader.Inject(new ActivityContext(TraceId, SpanId, TraceOptions), carrier, Setter); + var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); + this.b3FormatSingleHeader.Inject(new TextFormatContext(activityContext, null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-1" } }); } @@ -210,9 +217,9 @@ public void Serialize_SampledContext_SingleHeader() public void Serialize_NotSampledContext_SingleHeader() { var carrier = new Dictionary(); - var context = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - this.output.WriteLine(context.ToString()); - this.b3FormatSingleHeader.Inject(context, carrier, Setter); + var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); + this.output.WriteLine(activityContext.ToString()); + this.b3FormatSingleHeader.Inject(new TextFormatContext(activityContext, null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}" } }); } @@ -223,8 +230,8 @@ public void ParseMissingSampledAndMissingFlag_SingleHeader() { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}" }, }; - var spanContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - Assert.Equal(spanContext, this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter)); + var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter)); } [Fact] @@ -260,7 +267,8 @@ public void ParseFlag_SingleHeader() { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-1" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, TraceOptions), this.b3FormatSingleHeader.Extract(default, headersFlagSampled, Getter)); + var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagSampled, Getter)); } [Fact] @@ -270,7 +278,8 @@ public void ParseZeroFlag_SingleHeader() { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-0" }, }; - Assert.Equal(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), this.b3FormatSingleHeader.Extract(default, headersFlagNotSampled, Getter)); + var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagNotSampled, Getter)); } [Fact] @@ -280,7 +289,8 @@ public void ParseEightBytesTraceId_SingleHeader() { { B3Format.XB3Combined, $"{TraceIdBase16EightBytes}-{SpanIdBase16}-1" }, }; - Assert.Equal(new ActivityContext(TraceIdEightBytes, SpanId, TraceOptions), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); + var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, TraceOptions); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); } [Fact] @@ -290,7 +300,8 @@ public void ParseEightBytesTraceId_NotSampledSpanContext_SingleHeader() { { B3Format.XB3Combined, $"{TraceIdBase16EightBytes}-{SpanIdBase16}" }, }; - Assert.Equal(new ActivityContext(TraceIdEightBytes, SpanId, ActivityTraceFlags.None), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); + var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, ActivityTraceFlags.None); + Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); } [Fact] diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs index 614bfdcb59a..cbda40110e0 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/CompositePropagatorTest.cs @@ -79,10 +79,11 @@ public void CompositePropagator_TestPropagator() }); var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null); + TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); var carrier = new Dictionary(); var activity = new Activity("test"); - compositePropagator.Inject(activity, carrier, Setter); + compositePropagator.Inject(textFormatContext, carrier, Setter); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent-1"); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent-2"); } @@ -102,10 +103,9 @@ public void CompositePropagator_UsingSameTag() var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null); TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); - var activity = new Activity("test"); var carrier = new Dictionary(); - compositePropagator.Inject(activity, carrier, Setter); + compositePropagator.Inject(textFormatContext, carrier, Setter); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent"); // checking if the latest propagator is the one with the data. So, it will replace the previous one. diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs index 03a7cc018f4..6a5bcfdc842 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TestPropagator.cs @@ -68,16 +68,16 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(Activity activity, T carrier, Action setter) + public void Inject(TextFormatContext context, T carrier, Action setter) { string headerNumber = this.stateHeaderName.Split('-').Last(); - var traceparent = string.Concat("00-", activity.TraceId.ToHexString(), "-", activity.SpanId.ToHexString()); + var traceparent = string.Concat("00-", context.ActivityContext.TraceId.ToHexString(), "-", context.ActivityContext.SpanId.ToHexString()); traceparent = string.Concat(traceparent, "-", headerNumber); setter(carrier, this.idHeaderName, traceparent); - string tracestateStr = activity.TraceStateString; + string tracestateStr = context.ActivityContext.TraceState; if (tracestateStr?.Length > 0) { setter(carrier, this.stateHeaderName, tracestateStr); diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs index 10c080e3aeb..2921c09204f 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs @@ -153,10 +153,10 @@ public void TraceContextFormat_Inject_NoTracestate() }; var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, traceState: null); - var activity = new Activity("test"); + TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); var carrier = new Dictionary(); var f = new TraceContextFormat(); - f.Inject(activity, carrier, Setter); + f.Inject(textFormatContext, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } @@ -173,10 +173,10 @@ public void TraceContextFormat_Inject_WithTracestate() }; var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, expectedHeaders[TraceState]); - var activity = new Activity("test"); + TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); var carrier = new Dictionary(); var f = new TraceContextFormat(); - f.Inject(activity, carrier, Setter); + f.Inject(textFormatContext, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } From ec65bc0180c8fc4d8e188ac8031a4ef72d7f8236 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Sun, 9 Aug 2020 20:24:00 -0300 Subject: [PATCH 06/33] buildable in release --- .../Context/Propagation/BaggageFormat.cs | 4 +- .../Context/Propagation/ITextFormat.cs | 58 +-------- .../Propagation/NameValueHeaderValue.cs | 25 ++-- .../Context/Propagation/TextFormatContext.cs | 110 ++++++++++++++++++ .../Context/Propagation/TraceContextFormat.cs | 1 - 5 files changed, 128 insertions(+), 70 deletions(-) create mode 100644 src/OpenTelemetry.Api/Context/Propagation/TextFormatContext.cs diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index 299b4f6f4ee..b0e4abd5ff5 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -22,7 +22,9 @@ namespace OpenTelemetry.Context.Propagation { - // W3C baggage: https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md + /// + /// W3C baggage: https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md. + /// public class BaggageFormat : ITextFormat { private const string Baggage = "baggage"; diff --git a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs index da635904c92..4c5cf7e6c51 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs @@ -15,65 +15,9 @@ // using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; namespace OpenTelemetry.Context.Propagation { - public readonly struct TextFormatContext : IEquatable - { - public ActivityContext ActivityContext { get; } - - public IEnumerable> ActivityBaggage { get; } - - public TextFormatContext(ActivityContext activityContext, IEnumerable> activityBaggage) - { - this.ActivityContext = activityContext; - this.ActivityBaggage = activityBaggage; - } - - public static bool operator ==(TextFormatContext left, TextFormatContext right) => left.Equals(right); - - public static bool operator !=(TextFormatContext left, TextFormatContext right) => !(left == right); - - /// - public bool Equals(TextFormatContext value) - { - if (this.ActivityContext != value.ActivityContext - || this.ActivityBaggage is null != value.ActivityBaggage is null) - { - return false; - } - - if (this.ActivityBaggage is null) - { - return true; - } - - if (this.ActivityBaggage.Count() != value.ActivityBaggage.Count()) - { - return false; - } - - var thisEnumerator = this.ActivityBaggage.GetEnumerator(); - var valueEnumerator = value.ActivityBaggage.GetEnumerator(); - - while (thisEnumerator.MoveNext() && valueEnumerator.MoveNext()) - { - if (thisEnumerator.Current.Key != valueEnumerator.Current.Key - || thisEnumerator.Current.Value != valueEnumerator.Current.Value) - { - return false; - } - } - - return true; - } - - /// - public override bool Equals(object? obj) => (obj is TextFormatContext context) ? this.Equals(context) : false; - } - /// /// Text format wire context propagator. Helps to extract and inject context from textual /// representation (typically http headers or metadata collection). @@ -91,7 +35,7 @@ public interface ITextFormat /// Injects textual representation of activity context to transmit over the wire. /// /// Type of an object to set context on. Typically HttpRequest or similar. - /// Activity to transmit over the wire. + /// The default context to transmit over the wire. /// Object to set context on. Instance of this object will be passed to setter. /// Action that will set name and value pair on the object. void Inject(TextFormatContext context, T carrier, Action setter); diff --git a/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs b/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs index 105b1bb400a..8b12553ecf4 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/NameValueHeaderValue.cs @@ -16,7 +16,6 @@ namespace OpenTelemetry.Context.Propagation { - // Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/NameValueHeaderValue.cs internal class NameValueHeaderValue { @@ -89,31 +88,35 @@ private static int GetNameValueLength(string input, int startIndex, out NameValu var name = input.Substring(startIndex, nameLength); var current = startIndex + nameLength; - current = current + HttpRuleParser.GetWhitespaceLength(input, current); + current += HttpRuleParser.GetWhitespaceLength(input, current); // Parse the separator between name and value if ((current == input.Length) || (input[current] != '=')) { // We only have a name and that's OK. Return. - parsedValue = new NameValueHeaderValue(); - parsedValue.name = name; - current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces + parsedValue = new NameValueHeaderValue + { + name = name, + }; + current += HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces return current - startIndex; } current++; // skip delimiter. - current = current + HttpRuleParser.GetWhitespaceLength(input, current); + current += HttpRuleParser.GetWhitespaceLength(input, current); // Parse the value, i.e. in name/value string "=" int valueLength = GetValueLength(input, current); // Value after the '=' may be empty // Use parameterless ctor to avoid double-parsing of name and value, i.e. skip public ctor validation. - parsedValue = new NameValueHeaderValue(); - parsedValue.name = name; - parsedValue.value = input.Substring(current, valueLength); - current = current + valueLength; - current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces + parsedValue = new NameValueHeaderValue + { + name = name, + value = input.Substring(current, valueLength), + }; + current += valueLength; + current += HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces return current - startIndex; } } diff --git a/src/OpenTelemetry.Api/Context/Propagation/TextFormatContext.cs b/src/OpenTelemetry.Api/Context/Propagation/TextFormatContext.cs new file mode 100644 index 00000000000..e3ade2caa9f --- /dev/null +++ b/src/OpenTelemetry.Api/Context/Propagation/TextFormatContext.cs @@ -0,0 +1,110 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace OpenTelemetry.Context.Propagation +{ + /// + /// TextFormat context. + /// + public readonly struct TextFormatContext : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + /// Entries for activity context. + /// Entries for activity baggage. + public TextFormatContext(ActivityContext activityContext, IEnumerable> activityBaggage) + { + this.ActivityContext = activityContext; + this.ActivityBaggage = activityBaggage; + } + + /// + /// Gets . + /// + public ActivityContext ActivityContext { get; } + + /// + /// Gets ActivityBaggage. + /// + public IEnumerable> ActivityBaggage { get; } + + /// + /// Compare two entries of for equality. + /// + /// First Entry to compare. + /// Second Entry to compare. + public static bool operator ==(TextFormatContext left, TextFormatContext right) => left.Equals(right); + + /// + /// Compare two entries of for not equality. + /// + /// First Entry to compare. + /// Second Entry to compare. + public static bool operator !=(TextFormatContext left, TextFormatContext right) => !(left == right); + + /// + public bool Equals(TextFormatContext value) + { + if (this.ActivityContext != value.ActivityContext + || this.ActivityBaggage is null != value.ActivityBaggage is null) + { + return false; + } + + if (this.ActivityBaggage is null) + { + return true; + } + + if (this.ActivityBaggage.Count() != value.ActivityBaggage.Count()) + { + return false; + } + + var thisEnumerator = this.ActivityBaggage.GetEnumerator(); + var valueEnumerator = value.ActivityBaggage.GetEnumerator(); + + while (thisEnumerator.MoveNext() && valueEnumerator.MoveNext()) + { + if (thisEnumerator.Current.Key != valueEnumerator.Current.Key + || thisEnumerator.Current.Value != valueEnumerator.Current.Value) + { + return false; + } + } + + return true; + } + + /// + public override bool Equals(object obj) => (obj is TextFormatContext context) && this.Equals(context); + + /// + public override int GetHashCode() + { + var hashCode = 323591981; + hashCode = (hashCode * -1521134295) + this.ActivityContext.GetHashCode(); + hashCode = (hashCode * -1521134295) + EqualityComparer>>.Default.GetHashCode(this.ActivityBaggage); + return hashCode; + } + } +} diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index 9230d0f1f51..e95e3f9be50 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -22,7 +22,6 @@ namespace OpenTelemetry.Context.Propagation { - /// /// W3C trace context text wire protocol formatter. See https://github.com/w3c/distributed-tracing/. /// From 1964da6e24fd31b09255365920a57879c16c5e8b Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Mon, 10 Aug 2020 10:43:19 -0300 Subject: [PATCH 07/33] adding baggage tests --- .../Context/Propagation/BaggageFormat.cs | 9 +- .../Trace/Propagation/BaggageFormatTest.cs | 128 ++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 test/OpenTelemetry.Tests/Implementation/Trace/Propagation/BaggageFormatTest.cs diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index b0e4abd5ff5..6e459bfdd24 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -90,9 +90,9 @@ public void Inject(TextFormatContext context, T carrier, Action> e = context.ActivityBaggage.GetEnumerator(); + using IEnumerator> e = context.ActivityBaggage?.GetEnumerator(); - if (e.MoveNext()) + if (e != null && e.MoveNext()) { StringBuilder baggage = new StringBuilder(); do @@ -118,6 +118,11 @@ internal static bool TryExtractTracestateBaggage(string[] baggageCollection, out break; } + if (string.IsNullOrEmpty(item)) + { + continue; + } + foreach (var pair in item.Split(',')) { baggageLength += pair.Length + 1; // pair and comma diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/BaggageFormatTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/BaggageFormatTest.cs new file mode 100644 index 00000000000..808103e5c49 --- /dev/null +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/BaggageFormatTest.cs @@ -0,0 +1,128 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace OpenTelemetry.Context.Propagation.Test +{ + public class BaggageFormatTest + { + private static readonly Func, string, IEnumerable> Getter = + (d, k) => + { + d.TryGetValue(k, out var v); + return new string[] { v }; + }; + + private static readonly Action, string, string> Setter = (carrier, name, value) => + { + carrier[name] = value; + }; + + private readonly BaggageFormat baggage = new BaggageFormat(); + + [Fact] + public void ValidateFieldsProperty() + { + Assert.Equal(new HashSet { "baggage" }, this.baggage.Fields); + Assert.Single(this.baggage.Fields); + } + + [Fact] + public void ValidateDefaultCarrierExtraction() + { + var textFormatContext = this.baggage.Extract(default, null, null); + Assert.Equal(default, textFormatContext); + } + + [Fact] + public void ValidateDefaultGetterExtraction() + { + var carrier = new Dictionary(); + var textFormatContext = this.baggage.Extract(default, carrier, null); + Assert.Equal(default, textFormatContext); + } + + [Fact] + public void ValidateNoBaggageExtraction() + { + var carrier = new Dictionary(); + var textFormatContext = this.baggage.Extract(default, carrier, Getter); + Assert.Equal(default, textFormatContext); + } + + [Fact] + public void ValidateOneBaggageExtraction() + { + var carrier = new Dictionary + { + { "baggage", "name=test" }, + }; + var textFormatContext = this.baggage.Extract(default, carrier, Getter); + Assert.False(textFormatContext == default); + Assert.Single(textFormatContext.ActivityBaggage); + + var array = textFormatContext.ActivityBaggage.ToArray(); + + Assert.Equal("name", array[0].Key); + Assert.Equal("test", array[0].Value); + } + + [Fact] + public void ValidateLongBaggageExtraction() + { + var carrier = new Dictionary + { + { "baggage", $"name={new string('x', 1018)},clientId=1234" }, + }; + var textFormatContext = this.baggage.Extract(default, carrier, Getter); + Assert.False(textFormatContext == default); + Assert.Single(textFormatContext.ActivityBaggage); + + var array = textFormatContext.ActivityBaggage.ToArray(); + + Assert.Equal("name", array[0].Key); + Assert.Equal(new string('x', 1018), array[0].Value); + } + + [Fact] + public void ValidateEmptyBaggageInjection() + { + var carrier = new Dictionary(); + this.baggage.Inject(default, carrier, Setter); + + Assert.Empty(carrier); + } + + [Fact] + public void ValidateBaggageInjection() + { + var carrier = new Dictionary(); + var textFormatContext = new TextFormatContext(default, new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" }, + }); + + this.baggage.Inject(textFormatContext, carrier, Setter); + + Assert.Single(carrier); + Assert.Equal("key1=value1,key2=value2", carrier["baggage"]); + } + } +} From 685c29803eef62b8b32379bdedcf6d545dd53d97 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Mon, 10 Aug 2020 11:28:54 -0300 Subject: [PATCH 08/33] updating tests --- .../HttpWebRequestInstrumentationOptions.netfx.cs | 6 +++++- .../HttpClientTests.Basic.netcore31.cs | 4 ++-- .../HttpWebRequestActivitySourceTests.netfx.cs | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs index 1a2799a5b30..329d3c91de0 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs @@ -34,7 +34,11 @@ public class HttpWebRequestInstrumentationOptions /// /// Gets or sets for context propagation. Default value: . /// - public ITextFormat TextFormat { get; set; } = new TraceContextFormat(); + public ITextFormat TextFormat { get; set; } = new CompositePropagator(new System.Collections.Generic.List + { + new TraceContextFormat(), + new BaggageFormat(), + }); /// /// Gets or sets an optional callback method for filtering requests that are sent through the instrumentation. diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs index 785c78eb88f..c122d7bffd3 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs @@ -122,9 +122,9 @@ public async Task HttpClientInstrumentationInjectsHeadersAsync_CustomFormat() { var textFormat = new Mock(); textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((context, message, action) => + .Callback>((context, message, action) => { - action(message, "custom_traceparent", $"00/{context.TraceId}/{context.SpanId}/01"); + action(message, "custom_traceparent", $"00/{context.ActivityContext.TraceId}/{context.ActivityContext.SpanId}/01"); action(message, "custom_tracestate", Activity.Current.TraceStateString); }); diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs index 0a61bb86760..346041176f3 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs @@ -435,7 +435,7 @@ public async Task TestTraceStateAndCorrelationContext() var traceparent = startRequest.Headers["traceparent"]; var tracestate = startRequest.Headers["tracestate"]; - var correlationContext = startRequest.Headers["Correlation-Context"]; + var correlationContext = startRequest.Headers["baggage"]; Assert.NotNull(traceparent); Assert.Equal("some=state", tracestate); Assert.Equal("k=v", correlationContext); @@ -742,7 +742,7 @@ public async Task TestInvalidBaggage() Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); WebRequest thisRequest = (WebRequest)eventRecords.Records.First().Value.GetCustomProperty("HttpWebRequest.Request"); - string[] correlationContext = thisRequest.Headers["Correlation-Context"].Split(','); + string[] correlationContext = thisRequest.Headers["baggage"].Split(','); Assert.Equal(3, correlationContext.Length); Assert.Contains("key=value", correlationContext); From a26e2ceccff77611dc387ea803ca1209f1046fe8 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Mon, 10 Aug 2020 14:54:26 -0300 Subject: [PATCH 09/33] updating default textformat for http instrumentation --- .../HttpClientInstrumentationOptions.cs | 8 ++++++-- .../HttpWebRequestInstrumentationOptions.netfx.cs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs index 5e29c2d81a1..4dbadccc9c0 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs @@ -31,9 +31,13 @@ public class HttpClientInstrumentationOptions public bool SetHttpFlavor { get; set; } /// - /// Gets or sets for context propagation. Default value: . + /// Gets or sets for context propagation. Default value: . /// - public ITextFormat TextFormat { get; set; } = new TraceContextFormat(); + public ITextFormat TextFormat { get; set; } = new CompositePropagator(new System.Collections.Generic.List + { + new TraceContextFormat(), + new BaggageFormat(), + }); /// /// Gets or sets an optional callback method for filtering requests that are sent through the instrumentation. diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs index 329d3c91de0..09ea6be2409 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs @@ -32,7 +32,7 @@ public class HttpWebRequestInstrumentationOptions public bool SetHttpFlavor { get; set; } /// - /// Gets or sets for context propagation. Default value: . + /// Gets or sets for context propagation. Default value: . /// public ITextFormat TextFormat { get; set; } = new CompositePropagator(new System.Collections.Generic.List { From de2898e82a6eb07fe323e909254d97531a6e9289 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 10 Aug 2020 23:24:48 -0700 Subject: [PATCH 10/33] Removed a few null checks. --- src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs | 6 ------ .../Context/Propagation/TraceContextFormat.cs | 2 +- src/OpenTelemetry/Context/Propagation/B3Format.cs | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index 6e459bfdd24..e53e01e109f 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -72,12 +72,6 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func public void Inject(TextFormatContext context, T carrier, Action setter) { - if (context == null) - { - OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); - return; - } - if (carrier == null) { OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier"); diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index e95e3f9be50..8dcc2160036 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -97,7 +97,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func public void Inject(TextFormatContext context, T carrier, Action setter) { - if (context == null || context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) + if (context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) { OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context"); return; diff --git a/src/OpenTelemetry/Context/Propagation/B3Format.cs b/src/OpenTelemetry/Context/Propagation/B3Format.cs index 10d4e57e92d..e72871fd770 100644 --- a/src/OpenTelemetry/Context/Propagation/B3Format.cs +++ b/src/OpenTelemetry/Context/Propagation/B3Format.cs @@ -97,7 +97,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func public void Inject(TextFormatContext context, T carrier, Action setter) { - if (context == null || context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) + if (context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) { OpenTelemetrySdkEventSource.Log.FailedToInjectContext("invalid context"); return; From 1ab096959b56c61ae2c0ba32542b1f27aa528239 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 11 Aug 2020 12:00:16 -0700 Subject: [PATCH 11/33] Removed DistributedContext. Drive CorrelationContext off of Activity.Baggage. --- .../Context/CorrelationContext.cs | 62 ++-- .../Context/CorrelationContextBuilder.cs | 199 ------------- .../Context/CorrelationContextEntry.cs | 125 -------- .../Context/DistributedContext.cs | 85 ------ .../Context/DistributedContextBuilder.cs | 90 ------ .../Context/DistributedContextCarrier.cs | 38 --- .../Context/NoopDistributedContextCarrier.cs | 59 ---- .../Propagation/EntryPropagationFilter.cs | 89 ------ .../Metrics/BoundCounterMetric.cs | 2 +- .../Metrics/BoundMeasureMetric.cs | 2 +- .../Metrics/CounterMetric.cs | 4 +- .../Metrics/MeasureMetric.cs | 4 +- .../Metrics/NoopBoundCounterMetric.cs | 2 +- .../Metrics/NoopBoundMeasureMetric.cs | 2 +- .../Metrics/NoopCounterMetric.cs | 4 +- src/OpenTelemetry.Api/Trace/TelemetrySpan.cs | 33 ++- .../SpanContextShim.cs | 10 +- .../SpanShim.cs | 2 +- .../TracerShim.cs | 9 +- .../AsyncLocalDistributedContextCarrier.cs | 102 ------- .../Context/DistributedContextState.cs | 36 --- .../NoopDistributedContextBinarySerializer.cs | 56 ---- .../DistributedContextBinarySerializer.cs | 37 --- .../DistributedContextBinarySerializerBase.cs | 38 --- .../Context/Propagation/SerializationUtils.cs | 186 ------------ .../Metrics/DoubleBoundCounterMetricSdk.cs | 2 +- .../Metrics/DoubleBoundMeasureMetricSdk.cs | 2 +- .../Metrics/DoubleCounterMetricSdk.cs | 4 +- .../Metrics/Int64BoundCounterMetricSdk.cs | 2 +- .../Metrics/Int64BoundMeasureMetricSdk.cs | 2 +- .../Metrics/Int64CounterMetricSdk.cs | 4 +- .../Context/CorrelationContextBuilderTest.cs | 132 --------- .../Context/CorrelationContextEntryTest.cs | 67 ----- .../Context/CorrelationContextTest.cs | 51 ++-- .../Context/DistributedContextsScopeTest.cs | 125 -------- .../DistributedContextDeserializationTest.cs | 272 ------------------ .../DistributedContextRoundtripTest.cs | 82 ------ .../DistributedContextSerializationTest.cs | 153 ---------- 38 files changed, 135 insertions(+), 2039 deletions(-) delete mode 100644 src/OpenTelemetry.Api/Context/CorrelationContextBuilder.cs delete mode 100644 src/OpenTelemetry.Api/Context/CorrelationContextEntry.cs delete mode 100644 src/OpenTelemetry.Api/Context/DistributedContext.cs delete mode 100644 src/OpenTelemetry.Api/Context/DistributedContextBuilder.cs delete mode 100644 src/OpenTelemetry.Api/Context/DistributedContextCarrier.cs delete mode 100644 src/OpenTelemetry.Api/Context/NoopDistributedContextCarrier.cs delete mode 100644 src/OpenTelemetry.Api/Context/Propagation/EntryPropagationFilter.cs delete mode 100644 src/OpenTelemetry/Context/AsyncLocalDistributedContextCarrier.cs delete mode 100644 src/OpenTelemetry/Context/DistributedContextState.cs delete mode 100644 src/OpenTelemetry/Context/NoopDistributedContextBinarySerializer.cs delete mode 100644 src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializer.cs delete mode 100644 src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializerBase.cs delete mode 100644 src/OpenTelemetry/Context/Propagation/SerializationUtils.cs delete mode 100644 test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextBuilderTest.cs delete mode 100644 test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextEntryTest.cs delete mode 100644 test/OpenTelemetry.Tests/Implementation/Context/DistributedContextsScopeTest.cs delete mode 100644 test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextDeserializationTest.cs delete mode 100644 test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextRoundtripTest.cs delete mode 100644 test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextSerializationTest.cs diff --git a/src/OpenTelemetry.Api/Context/CorrelationContext.cs b/src/OpenTelemetry.Api/Context/CorrelationContext.cs index 92dc1b4074f..790f3607e82 100644 --- a/src/OpenTelemetry.Api/Context/CorrelationContext.cs +++ b/src/OpenTelemetry.Api/Context/CorrelationContext.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace OpenTelemetry.Context @@ -25,46 +26,73 @@ namespace OpenTelemetry.Context /// public readonly struct CorrelationContext : IEquatable { - private static readonly List EmptyList = new List(); - private readonly List entries; + internal static readonly CorrelationContext Empty = new CorrelationContext(null); + internal static readonly IEnumerable> EmptyBaggage = new KeyValuePair[0]; + private readonly Activity activity; + + internal CorrelationContext(in Activity activity) + { + this.activity = activity; + } /// - /// Initializes a new instance of the struct. + /// Gets the current . /// - /// Entries for correlation context. - internal CorrelationContext(List entries) + public static CorrelationContext Current { - this.entries = entries; + get + { + Activity activity = Activity.Current; + return activity == null + ? Empty + : new CorrelationContext(activity); + } } /// - /// Gets empty object of struct. + /// Gets the correlation values. /// - public static CorrelationContext Empty { get; } = new CorrelationContext(EmptyList); + public IEnumerable> Correlations => this.activity?.Baggage ?? EmptyBaggage; /// - /// Gets all the in this . + /// Retrieves a correlation item. /// - public IEnumerable Entries => this.entries; + /// Correlation item key. + /// Retrieved correlation value or if no match was found. + public string GetCorrelation(string key) + => this.activity?.GetBaggageItem(key); /// - /// Gets the with the specified name. + /// Adds a correlation item. /// - /// Name of the to get. - /// The with the specified name. If not found - null. - public string GetEntryValue(string key) => this.entries.LastOrDefault(x => x.Key == key).Value; + /// Correlation item key. + /// Correlation item value. + /// The instance for chaining. + public CorrelationContext AddCorrelation(string key, string value) + { + this.activity?.AddBaggage(key, value); + + return this; + } /// public bool Equals(CorrelationContext other) { - if (this.entries.Count != other.entries.Count) + var thisCorrelations = this.Correlations; + var otherCorrelations = other.Correlations; + + if (thisCorrelations.Count() != otherCorrelations.Count()) { return false; } - foreach (CorrelationContextEntry entry in this.entries) + var thisEnumerator = thisCorrelations.GetEnumerator(); + var otherEnumerator = otherCorrelations.GetEnumerator(); + + while (thisEnumerator.MoveNext() && otherEnumerator.MoveNext()) { - if (other.GetEntryValue(entry.Key) != entry.Value) + if (thisEnumerator.Current.Key != otherEnumerator.Current.Key + || thisEnumerator.Current.Value != otherEnumerator.Current.Value) { return false; } diff --git a/src/OpenTelemetry.Api/Context/CorrelationContextBuilder.cs b/src/OpenTelemetry.Api/Context/CorrelationContextBuilder.cs deleted file mode 100644 index 240a4a10e19..00000000000 --- a/src/OpenTelemetry.Api/Context/CorrelationContextBuilder.cs +++ /dev/null @@ -1,199 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Collections.Generic; - -namespace OpenTelemetry.Context -{ - /// - /// Correlation context Builder. - /// - public struct CorrelationContextBuilder - { - private List entries; - - /// - /// Initializes a new instance of the struct. - /// - /// Flag to allow inheriting the current context entries. - public CorrelationContextBuilder(bool inheritCurrentContext) - { - this.entries = null; - - if (DistributedContext.Carrier is NoopDistributedContextCarrier) - { - return; - } - - if (inheritCurrentContext) - { - this.entries = new List(DistributedContext.Current.CorrelationContext.Entries); - } - } - - /// - /// Initializes a new instance of the struct using some context. - /// - /// Initial context. - public CorrelationContextBuilder(CorrelationContext context) - { - if (DistributedContext.Carrier is NoopDistributedContextCarrier) - { - this.entries = null; - return; - } - - this.entries = new List(context.Entries); - } - - /// - /// Create instance from key and value entry. - /// - /// Entry key. - /// Entry value. - /// Instance of . - public static CorrelationContext CreateContext(string key, string value) => - new CorrelationContextBuilder(inheritCurrentContext: false).Add(key, value).Build(); - - /// - /// Create instance from entry. - /// - /// Entry to add to the context. - /// Instance of . - public static CorrelationContext CreateContext(CorrelationContextEntry entry) => - new CorrelationContextBuilder(inheritCurrentContext: false).Add(entry).Build(); - - /// - /// Create instance from entry. - /// - /// List of entries to add to the context. - /// Instance of . - public static CorrelationContext CreateContext(IEnumerable entries) => - new CorrelationContextBuilder(inheritCurrentContext: false).Add(entries).Build(); - - /// - /// Add Distributed Context entry to the builder. - /// - /// Entry to add to the context. - /// The current instance. - public CorrelationContextBuilder Add(CorrelationContextEntry entry) - { - if (DistributedContext.Carrier is NoopDistributedContextCarrier || entry == default) - { - return this; - } - - if (this.entries == null) - { - this.entries = new List(); - } - else - { - for (int i = 0; i < this.entries.Count; i++) - { - if (this.entries[i].Key == entry.Key) - { - this.entries[i] = entry; - return this; - } - } - } - - this.entries.Add(entry); - return this; - } - - /// - /// Add Distributed Context entry to the builder. - /// - /// Entry key. - /// Entry value. - /// Entry metadata. - /// The current instance. - public CorrelationContextBuilder Add(string key, string value, EntryMetadata metadata) - { - return this.Add(new CorrelationContextEntry(key, value, metadata)); - } - - /// - /// Add Distributed Context entry to the builder. - /// - /// Entry key. - /// Entry value. - /// The current instance. - public CorrelationContextBuilder Add(string key, string value) - { - return this.Add(new CorrelationContextEntry(key, value)); - } - - /// - /// Add Distributed Context entry to the builder. - /// - /// List of entries to add to the context. - /// The current instance. - public CorrelationContextBuilder Add(IEnumerable entries) - { - if (DistributedContext.Carrier is NoopDistributedContextCarrier || entries == null) - { - return this; - } - - foreach (var entry in entries) - { - this.Add(entry); - } - - return this; - } - - /// - /// Remove Distributed Context entry from the context. - /// - /// Entry key. - /// The current instance. - public CorrelationContextBuilder Remove(string key) - { - if (key == null || DistributedContext.Carrier is NoopDistributedContextCarrier || this.entries == null) - { - return this; - } - - int index = this.entries.FindIndex(entry => entry.Key == key); - if (index >= 0) - { - this.entries.RemoveAt(index); - } - - return this; - } - - /// - /// Build a Correlation Context from current builder. - /// - /// instance. - public CorrelationContext Build() - { - if (DistributedContext.Carrier is NoopDistributedContextCarrier || this.entries == null) - { - return CorrelationContext.Empty; - } - - var context = new CorrelationContext(this.entries); - this.entries = null; // empty current builder entries. - return context; - } - } -} diff --git a/src/OpenTelemetry.Api/Context/CorrelationContextEntry.cs b/src/OpenTelemetry.Api/Context/CorrelationContextEntry.cs deleted file mode 100644 index ee7c35f4d43..00000000000 --- a/src/OpenTelemetry.Api/Context/CorrelationContextEntry.cs +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Context -{ - /// - /// Distributed Context entry with the key, value and metadata. - /// - public readonly struct CorrelationContextEntry - { - /// - /// Initializes a new instance of the struct with the key and value. - /// - /// Key name for the entry. - /// Value associated with the key name. - public CorrelationContextEntry(string key, string value) - : this(key, value, EntryMetadata.NoPropagationEntry) - { - } - - /// - /// Initializes a new instance of the struct with the key, value, and metadata. - /// - /// Key name for the entry. - /// Value associated with the key name. - /// Entry metadata. - public CorrelationContextEntry(string key, string value, in EntryMetadata metadata) - { - if (key == null) - { - this.Key = string.Empty; - OpenTelemetryApiEventSource.Log.InvalidArgument(nameof(CorrelationContextEntry), nameof(key), "is null"); - } - else - { - this.Key = key; - } - - if (value == null) - { - this.Value = string.Empty; - OpenTelemetryApiEventSource.Log.InvalidArgument(nameof(CorrelationContextEntry), nameof(value), "is null"); - } - else - { - this.Value = value; - } - - this.Metadata = metadata; - } - - /// - /// Gets the tag key. - /// - public string Key { get; } - - /// - /// Gets the tag value. - /// - public string Value { get; } - - /// - /// Gets the metadata associated with this entry. - /// - public EntryMetadata Metadata { get; } - - /// - /// Compare two entries of for equality. - /// - /// First Entry to compare. - /// Second Entry to compare. - public static bool operator ==(CorrelationContextEntry entry1, CorrelationContextEntry entry2) => entry1.Equals(entry2); - - /// - /// Compare two entries of for not equality. - /// - /// First Entry to compare. - /// Second Entry to compare. - public static bool operator !=(CorrelationContextEntry entry1, CorrelationContextEntry entry2) => !entry1.Equals(entry2); - - /// - public override bool Equals(object o) - { - return o is CorrelationContextEntry that && this.Key == that.Key && this.Value == that.Value; - } - - /// - public override string ToString() - { - return this.Key is null ? "{}" : $"{nameof(CorrelationContextEntry)}{{{nameof(this.Key)}={this.Key}, {nameof(this.Value)}={this.Value}}}"; - } - - /// - public override int GetHashCode() - { - if (this.Key is null) - { - // Default instance - return 0; - } - - var h = 1; - h *= 1000003; - h ^= this.Key.GetHashCode(); - h *= 1000003; - h ^= this.Value.GetHashCode(); - return h; - } - } -} diff --git a/src/OpenTelemetry.Api/Context/DistributedContext.cs b/src/OpenTelemetry.Api/Context/DistributedContext.cs deleted file mode 100644 index 36aa29fffcc..00000000000 --- a/src/OpenTelemetry.Api/Context/DistributedContext.cs +++ /dev/null @@ -1,85 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Context -{ - /// - /// Distributed context. - /// - public readonly struct DistributedContext : IEquatable - { - private static DistributedContextCarrier carrier = NoopDistributedContextCarrier.Instance; - private readonly CorrelationContext correlationContext; - - /// - /// Initializes a new instance of the struct. - /// - /// The correlation context. - internal DistributedContext(CorrelationContext correlationContext) - { - this.correlationContext = correlationContext; - } - - /// - /// Gets empty object of struct. - /// - public static DistributedContext Empty { get; } = new DistributedContext(CorrelationContext.Empty); - - /// - /// Gets the current . - /// - public static DistributedContext Current => carrier.Current; - - /// - /// Gets or sets the default carrier instance of the class. - /// SDK will need to override the value to AsyncLocalDistributedContextCarrier.Instance. - /// - public static DistributedContextCarrier Carrier - { - get => carrier; - set - { - if (value is null) - { - OpenTelemetryApiEventSource.Log.InvalidArgument("set_Carrier", nameof(value), "is null"); - } - - carrier = value ?? NoopDistributedContextCarrier.Instance; - } - } - - /// - /// Gets the for the current distributed context. - /// - public CorrelationContext CorrelationContext => this.correlationContext; - - /// - /// Sets the current . - /// - /// Context to set as current. - /// Scope object. On disposal - original context will be restored. - public static IDisposable SetCurrent(in DistributedContext context) => carrier.SetCurrent(context); - - /// - public bool Equals(DistributedContext other) - { - return this.CorrelationContext.Equals(other.CorrelationContext); - } - } -} diff --git a/src/OpenTelemetry.Api/Context/DistributedContextBuilder.cs b/src/OpenTelemetry.Api/Context/DistributedContextBuilder.cs deleted file mode 100644 index 20147ab2565..00000000000 --- a/src/OpenTelemetry.Api/Context/DistributedContextBuilder.cs +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System; -using System.Collections.Generic; - -namespace OpenTelemetry.Context -{ - /// - /// Distributed context Builder. - /// - public struct DistributedContextBuilder - { - private CorrelationContextBuilder correlationContextBuilder; - - /// - /// Initializes a new instance of the struct. - /// - /// Flag to allow inheriting the current context entries. - public DistributedContextBuilder(bool inheritCurrentContext) - { - this.correlationContextBuilder = new CorrelationContextBuilder(false); - - if (DistributedContext.Carrier is NoopDistributedContextCarrier) - { - return; - } - - if (inheritCurrentContext) - { - this.correlationContextBuilder.Add(DistributedContext.Current.CorrelationContext.Entries); - } - } - - /// - /// Create context. - /// - /// The correlation key. - /// The correlation value. - /// A instance. - public static DistributedContext CreateContext(string key, string value) => - new DistributedContext(new CorrelationContextBuilder(inheritCurrentContext: false).Add(key, value).Build()); - - /// - /// Create context. - /// - /// A list of correlations to create the context with. - /// A instance. - public static DistributedContext CreateContext(IEnumerable entries) => - new DistributedContext(new CorrelationContextBuilder(inheritCurrentContext: false).Add(entries).Build()); - - /// - /// Configures correlations to be used with the context. - /// - /// An used to configure correlations. - /// The current instance. - public DistributedContextBuilder Correlations(Action configureCorrelations) - { - configureCorrelations?.Invoke(this.correlationContextBuilder); - return this; - } - - /// - /// Build a Distributed Context from current builder. - /// - /// instance. - public DistributedContext Build() - { - if (DistributedContext.Carrier is NoopDistributedContextCarrier) - { - return DistributedContext.Empty; - } - - return new DistributedContext(this.correlationContextBuilder.Build()); - } - } -} diff --git a/src/OpenTelemetry.Api/Context/DistributedContextCarrier.cs b/src/OpenTelemetry.Api/Context/DistributedContextCarrier.cs deleted file mode 100644 index ab27dce58a0..00000000000 --- a/src/OpenTelemetry.Api/Context/DistributedContextCarrier.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System; - -namespace OpenTelemetry.Context -{ - /// - /// Abstraction to the carrier of the DistributedContext.Current object. - /// - public abstract class DistributedContextCarrier - { - /// - /// Gets the current . - /// - public abstract DistributedContext Current { get; } - - /// - /// Sets the current . - /// - /// Context to set as current. - /// Scope object. On disposal - original context will be restored. - public abstract IDisposable SetCurrent(in DistributedContext context); - } -} diff --git a/src/OpenTelemetry.Api/Context/NoopDistributedContextCarrier.cs b/src/OpenTelemetry.Api/Context/NoopDistributedContextCarrier.cs deleted file mode 100644 index b81f0fd40ea..00000000000 --- a/src/OpenTelemetry.Api/Context/NoopDistributedContextCarrier.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System; - -namespace OpenTelemetry.Context -{ - /// - /// No-op Distributed context carrier. - /// - public class NoopDistributedContextCarrier : DistributedContextCarrier - { - /// - /// Initializes a new instance of the class. - /// - private NoopDistributedContextCarrier() - { - } - - /// - /// Gets the instance of . - /// - public static NoopDistributedContextCarrier Instance { get; } = new NoopDistributedContextCarrier(); - - /// - /// Gets the current . - /// - public override DistributedContext Current => DistributedContext.Empty; - - /// - /// Sets the current . - /// - /// Context to set as current. - /// Scope object. On disposal - original context will be restored. - public override IDisposable SetCurrent(in DistributedContext context) => EmptyDisposable.Instance; - - private class EmptyDisposable : IDisposable - { - public static EmptyDisposable Instance { get; } = new EmptyDisposable(); - - public void Dispose() - { - } - } - } -} diff --git a/src/OpenTelemetry.Api/Context/Propagation/EntryPropagationFilter.cs b/src/OpenTelemetry.Api/Context/Propagation/EntryPropagationFilter.cs deleted file mode 100644 index d207ed744a4..00000000000 --- a/src/OpenTelemetry.Api/Context/Propagation/EntryPropagationFilter.cs +++ /dev/null @@ -1,89 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System; - -namespace OpenTelemetry.Context.Propagation -{ - /// - /// Filter defining propagation rules for . - /// - public readonly struct EntryPropagationFilter - { - /// - /// Initializes a new instance of the struct. - /// - /// Operator to apply. - /// String to apply the operator with. - /// Action to execute on entry. - public EntryPropagationFilter(FilterMatchOperator op, string matchString, Action action) - { - this.Operator = op; - this.MatchString = matchString; - this.Action = action; - } - - /// - /// Operator to use in a filter. - /// - public enum FilterMatchOperator - { - /// - /// Equals operator. - /// - Equal, - - /// - /// Not equals operator. - /// - NotEqual, - - /// - /// Operator checking the prefix. - /// - HasPrefix, - } - - internal FilterMatchOperator Operator { get; } - - internal string MatchString { get; } - - internal Action Action { get; } - - /// - /// Check whether matches this filter pattern. - /// - /// Distributed Context entry to check. - /// True if matches this filter, false - otherwise. - public bool IsMatch(CorrelationContextEntry entry) - { - bool result = false; - switch (this.Operator) - { - case FilterMatchOperator.Equal: result = entry.Key.Equals(this.MatchString); break; - case FilterMatchOperator.NotEqual: result = !entry.Key.Equals(this.MatchString); break; - case FilterMatchOperator.HasPrefix: result = entry.Key.StartsWith(this.MatchString, StringComparison.Ordinal); break; - } - - if (result) - { - this.Action(entry); - } - - return result; - } - } -} diff --git a/src/OpenTelemetry.Api/Metrics/BoundCounterMetric.cs b/src/OpenTelemetry.Api/Metrics/BoundCounterMetric.cs index 2cdbdc84b9f..5e04dffe074 100644 --- a/src/OpenTelemetry.Api/Metrics/BoundCounterMetric.cs +++ b/src/OpenTelemetry.Api/Metrics/BoundCounterMetric.cs @@ -38,6 +38,6 @@ public abstract class BoundCounterMetric /// /// the associated distributed context. /// value by which the bound counter metric should be added. - public abstract void Add(in DistributedContext context, T value); + public abstract void Add(in CorrelationContext context, T value); } } diff --git a/src/OpenTelemetry.Api/Metrics/BoundMeasureMetric.cs b/src/OpenTelemetry.Api/Metrics/BoundMeasureMetric.cs index 37576d05eca..d5126e2e010 100644 --- a/src/OpenTelemetry.Api/Metrics/BoundMeasureMetric.cs +++ b/src/OpenTelemetry.Api/Metrics/BoundMeasureMetric.cs @@ -38,6 +38,6 @@ public abstract class BoundMeasureMetric /// /// the associated distributed context. /// the measurement to be recorded. - public abstract void Record(in DistributedContext context, T value); + public abstract void Record(in CorrelationContext context, T value); } } diff --git a/src/OpenTelemetry.Api/Metrics/CounterMetric.cs b/src/OpenTelemetry.Api/Metrics/CounterMetric.cs index f83565dc7e3..9da617fca8c 100644 --- a/src/OpenTelemetry.Api/Metrics/CounterMetric.cs +++ b/src/OpenTelemetry.Api/Metrics/CounterMetric.cs @@ -49,7 +49,7 @@ public abstract class CounterMetric /// the associated distributed context. /// value by which the counter should be incremented. /// The labelset associated with this value. - public abstract void Add(in DistributedContext context, T value, LabelSet labelset); + public abstract void Add(in CorrelationContext context, T value, LabelSet labelset); /// /// Adds or Increments the counter. @@ -57,7 +57,7 @@ public abstract class CounterMetric /// the associated distributed context. /// value by which the counter should be incremented. /// The labels or dimensions associated with this value. - public abstract void Add(in DistributedContext context, T value, IEnumerable> labels); + public abstract void Add(in CorrelationContext context, T value, IEnumerable> labels); /// /// Gets the bound counter metric with given labelset. diff --git a/src/OpenTelemetry.Api/Metrics/MeasureMetric.cs b/src/OpenTelemetry.Api/Metrics/MeasureMetric.cs index 1e23d1e6619..153b2497d40 100644 --- a/src/OpenTelemetry.Api/Metrics/MeasureMetric.cs +++ b/src/OpenTelemetry.Api/Metrics/MeasureMetric.cs @@ -49,7 +49,7 @@ public abstract class MeasureMetric /// the associated distributed context. /// value to record. /// The labelset associated with this value. - public void Record(in DistributedContext context, T value, LabelSet labelset) => this.Bind(labelset).Record(context, value); + public void Record(in CorrelationContext context, T value, LabelSet labelset) => this.Bind(labelset).Record(context, value); /// /// Records a measure. @@ -57,7 +57,7 @@ public abstract class MeasureMetric /// the associated distributed context. /// value to record. /// The labels or dimensions associated with this value. - public void Record(in DistributedContext context, T value, IEnumerable> labels) => this.Bind(labels).Record(context, value); + public void Record(in CorrelationContext context, T value, IEnumerable> labels) => this.Bind(labels).Record(context, value); /// /// Gets the bound measure metric with given labelset. diff --git a/src/OpenTelemetry.Api/Metrics/NoopBoundCounterMetric.cs b/src/OpenTelemetry.Api/Metrics/NoopBoundCounterMetric.cs index d3e3bc453a9..6acfce58d19 100644 --- a/src/OpenTelemetry.Api/Metrics/NoopBoundCounterMetric.cs +++ b/src/OpenTelemetry.Api/Metrics/NoopBoundCounterMetric.cs @@ -37,7 +37,7 @@ public override void Add(in SpanContext context, T value) } /// - public override void Add(in DistributedContext context, T value) + public override void Add(in CorrelationContext context, T value) { } } diff --git a/src/OpenTelemetry.Api/Metrics/NoopBoundMeasureMetric.cs b/src/OpenTelemetry.Api/Metrics/NoopBoundMeasureMetric.cs index 4d8391a466c..66aef213e5c 100644 --- a/src/OpenTelemetry.Api/Metrics/NoopBoundMeasureMetric.cs +++ b/src/OpenTelemetry.Api/Metrics/NoopBoundMeasureMetric.cs @@ -37,7 +37,7 @@ public override void Record(in SpanContext context, T value) } /// - public override void Record(in DistributedContext context, T value) + public override void Record(in CorrelationContext context, T value) { } } diff --git a/src/OpenTelemetry.Api/Metrics/NoopCounterMetric.cs b/src/OpenTelemetry.Api/Metrics/NoopCounterMetric.cs index f5642fda6c8..de95886dd8e 100644 --- a/src/OpenTelemetry.Api/Metrics/NoopCounterMetric.cs +++ b/src/OpenTelemetry.Api/Metrics/NoopCounterMetric.cs @@ -43,12 +43,12 @@ public override void Add(in SpanContext context, T value, IEnumerable - public override void Add(in DistributedContext context, T value, LabelSet labelset) + public override void Add(in CorrelationContext context, T value, LabelSet labelset) { } /// - public override void Add(in DistributedContext context, T value, IEnumerable> labels) + public override void Add(in CorrelationContext context, T value, IEnumerable> labels) { } diff --git a/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs b/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs index d9807191549..e0c8a30a30e 100644 --- a/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs +++ b/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs @@ -30,6 +30,7 @@ public class TelemetrySpan : IDisposable { internal static readonly TelemetrySpan NoopInstance = new TelemetrySpan(null); internal readonly Activity Activity; + private static readonly IEnumerable> EmptyBaggage = new KeyValuePair[0]; internal TelemetrySpan(Activity activity) { @@ -61,7 +62,7 @@ public bool IsRecording { get { - return (this.Activity == null) ? false : this.Activity.IsAllDataRequested; + return this.Activity != null && this.Activity.IsAllDataRequested; } } @@ -76,6 +77,11 @@ public Status Status } } + /// + /// Gets the span baggage. + /// + public IEnumerable> Baggage => this.Activity?.Baggage ?? EmptyBaggage; + /// /// Updates the name. /// @@ -274,6 +280,31 @@ public void End(DateTimeOffset endTimestamp) this.Activity?.Stop(); } + /// + /// Retrieves a baggage item. + /// + /// Baggage item key. + /// Retrieved baggage value or if no match was found. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string GetBaggageItem(string key) + { + return this.Activity?.GetBaggageItem(key); + } + + /// + /// Adds a baggage item to the . + /// + /// Baggage item key. + /// Baggage item value. + /// The instance for chaining. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TelemetrySpan AddBaggage(string key, string value) + { + this.Activity?.AddBaggage(key, value); + + return this; + } + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() diff --git a/src/OpenTelemetry.Shims.OpenTracing/SpanContextShim.cs b/src/OpenTelemetry.Shims.OpenTracing/SpanContextShim.cs index 6215c09ca53..38109683774 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/SpanContextShim.cs +++ b/src/OpenTelemetry.Shims.OpenTracing/SpanContextShim.cs @@ -22,7 +22,9 @@ namespace OpenTelemetry.Shims.OpenTracing { public sealed class SpanContextShim : ISpanContext { - public SpanContextShim(in Trace.SpanContext spanContext) + private readonly IEnumerable> baggage; + + public SpanContextShim(in Trace.SpanContext spanContext, IEnumerable> baggage = null) { if (!spanContext.IsValid) { @@ -30,6 +32,7 @@ public SpanContextShim(in Trace.SpanContext spanContext) } this.SpanContext = spanContext; + this.baggage = baggage; } public Trace.SpanContext SpanContext { get; private set; } @@ -42,9 +45,6 @@ public SpanContextShim(in Trace.SpanContext spanContext) /// public IEnumerable> GetBaggageItems() - { - // TODO - throw new NotImplementedException(); - } + => this.baggage; } } diff --git a/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs b/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs index a352a5bf0e2..7f8b23c71d5 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs +++ b/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs @@ -52,7 +52,7 @@ public SpanShim(TelemetrySpan span) throw new ArgumentException(nameof(this.Span.Context)); } - this.spanContextShim = new SpanContextShim(this.Span.Context); + this.spanContextShim = new SpanContextShim(this.Span.Context, this.Span.Baggage); } public ISpanContext Context => this.spanContextShim; diff --git a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs index 73e3af039d7..fdd32085a83 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs +++ b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs @@ -71,7 +71,7 @@ public TracerShim(Trace.Tracer tracer, ITextFormat textFormat) carrierMap.Add(entry.Key, new[] { entry.Value }); } - IEnumerable GetCarrierKeyValue(Dictionary> source, string key) + static IEnumerable GetCarrierKeyValue(Dictionary> source, string key) { if (key == null || !source.TryGetValue(key, out var value)) { @@ -84,7 +84,7 @@ IEnumerable GetCarrierKeyValue(Dictionary> s textFormatContext = this.textFormat.Extract(textFormatContext, carrierMap, GetCarrierKeyValue); } - return !textFormatContext.ActivityContext.IsValid() ? null : new SpanContextShim(new Trace.SpanContext(textFormatContext.ActivityContext)); + return !textFormatContext.ActivityContext.IsValid() ? null : new SpanContextShim(new Trace.SpanContext(textFormatContext.ActivityContext), textFormatContext.ActivityBaggage); } /// @@ -115,7 +115,10 @@ public void Inject( if ((format == BuiltinFormats.TextMap || format == BuiltinFormats.HttpHeaders) && carrier is ITextMap textMapCarrier) { - this.textFormat.Inject(new TextFormatContext(shim.SpanContext, null), textMapCarrier, (instrumentation, key, value) => instrumentation.Set(key, value)); + this.textFormat.Inject( + new TextFormatContext(shim.SpanContext, shim.GetBaggageItems()), + textMapCarrier, + (instrumentation, key, value) => instrumentation.Set(key, value)); } } } diff --git a/src/OpenTelemetry/Context/AsyncLocalDistributedContextCarrier.cs b/src/OpenTelemetry/Context/AsyncLocalDistributedContextCarrier.cs deleted file mode 100644 index 315dd3f73f3..00000000000 --- a/src/OpenTelemetry/Context/AsyncLocalDistributedContextCarrier.cs +++ /dev/null @@ -1,102 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System; -#if NET452 -using System.Collections; -using System.Reflection; -using System.Runtime.Remoting.Messaging; -#endif -using System.Threading; - -namespace OpenTelemetry.Context -{ - /// - /// Distributed Context carrier using AsyncLocal. - /// - public sealed class AsyncLocalDistributedContextCarrier : DistributedContextCarrier - { -#if NET452 - // A special workaround to suppress context propagation cross AppDomains. - // - // By default the value added to System.Runtime.Remoting.Messaging.CallContext - // will be marshalled/unmarshalled across AppDomain boundary. This will cause - // serious issue if the destination AppDomain doesn't have the corresponding type - // to unmarshal data (which is DistributedContext in this case). - // The worst case is AppDomain crash with ReflectionLoadTypeException. - // - // The workaround is to use a well known type that exists in all AppDomains, and - // put the actual payload (DistributedContext instance) as a non-public field so - // the field is ignored during marshalling. - private const string ContextSlotName = "OpenTelemetry.DistributedContext"; - private static readonly FieldInfo WrapperField = typeof(BitArray).GetField("_syncRoot", BindingFlags.Instance | BindingFlags.NonPublic); -#else - private static AsyncLocal carrier = new AsyncLocal(); -#endif - - private AsyncLocalDistributedContextCarrier() - { - this.OverwriteCurrent(DistributedContext.Empty); - } - - /// - /// Gets the instance of . - /// - public static DistributedContextCarrier Instance { get; } = new AsyncLocalDistributedContextCarrier(); - - /// - /// Gets the current . - /// - public override DistributedContext Current - { - get - { -#if NET452 - var wrapper = CallContext.LogicalGetData(ContextSlotName) as BitArray; - - if (wrapper == null) - { - var context = default(DistributedContext); - this.OverwriteCurrent(context); - return context; - } - - return (DistributedContext)WrapperField.GetValue(wrapper); -#else - return carrier.Value; -#endif - } - } - - /// - /// Sets the current . - /// - /// Context to set as current. - /// Scope object. On disposal - original context will be restored. - public override IDisposable SetCurrent(in DistributedContext context) => new DistributedContextState(in context); - - internal void OverwriteCurrent(in DistributedContext context) - { -#if NET452 - var wrapper = new BitArray(0); - WrapperField.SetValue(wrapper, context); - CallContext.LogicalSetData(ContextSlotName, wrapper); -#else - carrier.Value = context; -#endif - } - } -} diff --git a/src/OpenTelemetry/Context/DistributedContextState.cs b/src/OpenTelemetry/Context/DistributedContextState.cs deleted file mode 100644 index 11a27e0b3e3..00000000000 --- a/src/OpenTelemetry/Context/DistributedContextState.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System; - -namespace OpenTelemetry.Context -{ - internal struct DistributedContextState : IDisposable - { - private DistributedContext context; - - internal DistributedContextState(in DistributedContext context) - { - this.context = AsyncLocalDistributedContextCarrier.Instance.Current; - ((AsyncLocalDistributedContextCarrier)AsyncLocalDistributedContextCarrier.Instance).OverwriteCurrent(in context); - } - - public void Dispose() - { - ((AsyncLocalDistributedContextCarrier)AsyncLocalDistributedContextCarrier.Instance).OverwriteCurrent(in this.context); - } - } -} diff --git a/src/OpenTelemetry/Context/NoopDistributedContextBinarySerializer.cs b/src/OpenTelemetry/Context/NoopDistributedContextBinarySerializer.cs deleted file mode 100644 index ed98e1f68b8..00000000000 --- a/src/OpenTelemetry/Context/NoopDistributedContextBinarySerializer.cs +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#if !NET452 -using System; -#endif -using OpenTelemetry.Context.Propagation; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Context -{ - public class NoopDistributedContextBinarySerializer : DistributedContextBinarySerializerBase - { - internal static readonly DistributedContextBinarySerializerBase Instance = new NoopDistributedContextBinarySerializer(); -#if NET452 - private static readonly byte[] EmptyByteArray = { }; -#else - private static readonly byte[] EmptyByteArray = Array.Empty(); -#endif - - /// - public override byte[] ToByteArray(DistributedContext context) - { - if (context.CorrelationContext.Entries is null) - { - OpenTelemetrySdkEventSource.Log.FailedToInjectContext("entries are null"); - } - - return EmptyByteArray; - } - - /// - public override DistributedContext FromByteArray(byte[] bytes) - { - if (bytes == null) - { - OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null bytes"); - } - - return DistributedContext.Empty; - } - } -} diff --git a/src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializer.cs b/src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializer.cs deleted file mode 100644 index e0044af3ff7..00000000000 --- a/src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializer.cs +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -namespace OpenTelemetry.Context.Propagation -{ - internal sealed class DistributedContextBinarySerializer : DistributedContextBinarySerializerBase - { - internal DistributedContextBinarySerializer() - { - } - - /// - public override byte[] ToByteArray(DistributedContext context) - { - return SerializationUtils.SerializeBinary(context); - } - - /// - public override DistributedContext FromByteArray(byte[] bytes) - { - return SerializationUtils.DeserializeBinary(bytes); - } - } -} diff --git a/src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializerBase.cs b/src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializerBase.cs deleted file mode 100644 index 53d24f94baf..00000000000 --- a/src/OpenTelemetry/Context/Propagation/DistributedContextBinarySerializerBase.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -namespace OpenTelemetry.Context.Propagation -{ - /// - /// DistributedContextBinarySerializerBase base class. - /// - public abstract class DistributedContextBinarySerializerBase - { - /// - /// Deserializes input to based on the binary format standard. - /// - /// Array of bytes in binary format standard. - /// . - public abstract DistributedContext FromByteArray(byte[] bytes); - - /// - /// Serializes a to the on-the-wire format. - /// - /// . - /// Serialized . - public abstract byte[] ToByteArray(DistributedContext tags); - } -} diff --git a/src/OpenTelemetry/Context/Propagation/SerializationUtils.cs b/src/OpenTelemetry/Context/Propagation/SerializationUtils.cs deleted file mode 100644 index 1e8237c338f..00000000000 --- a/src/OpenTelemetry/Context/Propagation/SerializationUtils.cs +++ /dev/null @@ -1,186 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Context.Propagation -{ - internal static class SerializationUtils - { - internal const int VersionId = 0; - internal const int TagFieldId = 0; - - // This size limit only applies to the bytes representing tag keys and values. - internal const int TagContextSerializedSizeLimit = 8192; -#if NET452 - private static readonly byte[] InvalidContext = new byte[0]; -#else - private static readonly byte[] InvalidContext = Array.Empty(); -#endif - - // Serializes a DistributedContext to the on-the-wire format. - // Encoded tags are of the form: - internal static byte[] SerializeBinary(DistributedContext dc) - { - // Use a ByteArrayDataOutput to avoid needing to handle IOExceptions. - // ByteArrayDataOutput byteArrayDataOutput = ByteStreams.newDataOutput(); - var byteArrayDataOutput = new MemoryStream(); - - byteArrayDataOutput.WriteByte(VersionId); - var totalChars = 0; // Here chars are equivalent to bytes, since we're using ascii chars. - foreach (var tag in dc.CorrelationContext.Entries) - { - totalChars += tag.Key.Length; - totalChars += tag.Value.Length; - EncodeTag(tag, byteArrayDataOutput); - } - - // for (Iterator i = InternalUtils.getTags(tags); i.hasNext();) { - // Tag tag = i.next(); - // totalChars += tag.getKey().getName().length(); - // totalChars += tag.getValue().asString().length(); - // encodeTag(tag, byteArrayDataOutput); - // } - if (totalChars > TagContextSerializedSizeLimit) - { - OpenTelemetrySdkEventSource.Log.FailedToInjectContext("Size of DistributedContext exceeds the maximum serialized size " - + TagContextSerializedSizeLimit); - return InvalidContext; - } - - return byteArrayDataOutput.ToArray(); - } - - // Deserializes input to DistributedContext based on the binary format standard. - // The encoded tags are of the form: - internal static DistributedContext DeserializeBinary(byte[] bytes) - { - if (bytes.Length == 0) - { - OpenTelemetrySdkEventSource.Log.FailedToExtractContext("Input byte[] can not be empty."); - return DistributedContext.Empty; - } - - try - { - var buffer = new MemoryStream(bytes); - var versionId = buffer.ReadByte(); - if (versionId > VersionId || versionId < 0) - { - OpenTelemetrySdkEventSource.Log.FailedToExtractContext("Wrong Version ID: " + versionId + ". Currently supports version up to: " + VersionId); - return DistributedContext.Empty; - } - - if (TryParseTags(buffer, out var tags)) - { - return DistributedContextBuilder.CreateContext(tags); - } - } - catch (Exception e) - { - OpenTelemetrySdkEventSource.Log.ContextExtractException(e); - } - - return DistributedContext.Empty; - } - - internal static bool TryParseTags(MemoryStream buffer, out List tags) - { - tags = new List(); - var limit = buffer.Length; - var totalChars = 0; // Here chars are equivalent to bytes, since we're using ascii chars. - while (buffer.Position < limit) - { - var type = buffer.ReadByte(); - if (type == TagFieldId) - { - var key = CreateTagKey(DecodeString(buffer)); - var val = CreateTagValue(key, DecodeString(buffer)); - totalChars += key.Length; - totalChars += val.Length; - tags.Add(new CorrelationContextEntry(key, val)); - } - else - { - // Stop parsing at the first unknown field ID, since there is no way to know its length. - // TODO(sebright): Consider storing the rest of the byte array in the TagContext. - break; - } - } - - if (totalChars > TagContextSerializedSizeLimit) - { - OpenTelemetrySdkEventSource.Log.FailedToExtractContext( - "Size of TagContext exceeds the maximum serialized size " - + TagContextSerializedSizeLimit); - - return false; - } - - return true; - } - - // TODO(sebright): Consider exposing a string name validation method to avoid needing to catch an - // IllegalArgumentException here. - private static string CreateTagKey(string name) - { - return name; - } - - // TODO(sebright): Consider exposing a string validation method to avoid needing to catch - // an IllegalArgumentException here. - private static string CreateTagValue(string key, string value) - { - return value; - } - - private static void EncodeTag(CorrelationContextEntry tag, MemoryStream byteArrayDataOutput) - { - byteArrayDataOutput.WriteByte(TagFieldId); - EncodeString(tag.Key, byteArrayDataOutput); - EncodeString(tag.Value, byteArrayDataOutput); - } - - private static void EncodeString(string input, MemoryStream byteArrayDataOutput) - { - PutVarInt(input.Length, byteArrayDataOutput); - var bytes = Encoding.UTF8.GetBytes(input); - byteArrayDataOutput.Write(bytes, 0, bytes.Length); - } - - private static void PutVarInt(int input, MemoryStream byteArrayDataOutput) - { - var output = new byte[VarInt.VarIntSize(input)]; - VarInt.PutVarInt(input, output, 0); - byteArrayDataOutput.Write(output, 0, output.Length); - } - - private static string DecodeString(MemoryStream buffer) - { - var length = VarInt.GetVarInt(buffer); - var builder = new StringBuilder(); - for (var i = 0; i < length; i++) - { - builder.Append((char)buffer.ReadByte()); - } - - return builder.ToString(); - } - } -} diff --git a/src/OpenTelemetry/Metrics/DoubleBoundCounterMetricSdk.cs b/src/OpenTelemetry/Metrics/DoubleBoundCounterMetricSdk.cs index b8fa1431fac..57bf8dbb821 100644 --- a/src/OpenTelemetry/Metrics/DoubleBoundCounterMetricSdk.cs +++ b/src/OpenTelemetry/Metrics/DoubleBoundCounterMetricSdk.cs @@ -34,7 +34,7 @@ public override void Add(in SpanContext context, double value) this.sumAggregator.Update(value); } - public override void Add(in DistributedContext context, double value) + public override void Add(in CorrelationContext context, double value) { this.sumAggregator.Update(value); } diff --git a/src/OpenTelemetry/Metrics/DoubleBoundMeasureMetricSdk.cs b/src/OpenTelemetry/Metrics/DoubleBoundMeasureMetricSdk.cs index 7b295031e8c..1c457f0ad78 100644 --- a/src/OpenTelemetry/Metrics/DoubleBoundMeasureMetricSdk.cs +++ b/src/OpenTelemetry/Metrics/DoubleBoundMeasureMetricSdk.cs @@ -29,7 +29,7 @@ public override void Record(in SpanContext context, double value) this.measureAggregator.Update(value); } - public override void Record(in DistributedContext context, double value) + public override void Record(in CorrelationContext context, double value) { this.measureAggregator.Update(value); } diff --git a/src/OpenTelemetry/Metrics/DoubleCounterMetricSdk.cs b/src/OpenTelemetry/Metrics/DoubleCounterMetricSdk.cs index 7f16948d606..9832b874888 100644 --- a/src/OpenTelemetry/Metrics/DoubleCounterMetricSdk.cs +++ b/src/OpenTelemetry/Metrics/DoubleCounterMetricSdk.cs @@ -39,13 +39,13 @@ public override void Add(in SpanContext context, double value, IEnumerable> labels) + public override void Add(in CorrelationContext context, double value, IEnumerable> labels) { // user not using bound instrument. Hence create a short-lived bound instrument. this.Bind(new LabelSetSdk(labels), isShortLived: true).Add(context, value); diff --git a/src/OpenTelemetry/Metrics/Int64BoundCounterMetricSdk.cs b/src/OpenTelemetry/Metrics/Int64BoundCounterMetricSdk.cs index f79b54f838e..86b2d7242d7 100644 --- a/src/OpenTelemetry/Metrics/Int64BoundCounterMetricSdk.cs +++ b/src/OpenTelemetry/Metrics/Int64BoundCounterMetricSdk.cs @@ -34,7 +34,7 @@ public override void Add(in SpanContext context, long value) this.sumAggregator.Update(value); } - public override void Add(in DistributedContext context, long value) + public override void Add(in CorrelationContext context, long value) { this.sumAggregator.Update(value); } diff --git a/src/OpenTelemetry/Metrics/Int64BoundMeasureMetricSdk.cs b/src/OpenTelemetry/Metrics/Int64BoundMeasureMetricSdk.cs index a9e1c6de1a8..77c6c16f537 100644 --- a/src/OpenTelemetry/Metrics/Int64BoundMeasureMetricSdk.cs +++ b/src/OpenTelemetry/Metrics/Int64BoundMeasureMetricSdk.cs @@ -29,7 +29,7 @@ public override void Record(in SpanContext context, long value) this.measureAggregator.Update(value); } - public override void Record(in DistributedContext context, long value) + public override void Record(in CorrelationContext context, long value) { this.measureAggregator.Update(value); } diff --git a/src/OpenTelemetry/Metrics/Int64CounterMetricSdk.cs b/src/OpenTelemetry/Metrics/Int64CounterMetricSdk.cs index aec085a174b..ea8e2dd670b 100644 --- a/src/OpenTelemetry/Metrics/Int64CounterMetricSdk.cs +++ b/src/OpenTelemetry/Metrics/Int64CounterMetricSdk.cs @@ -39,13 +39,13 @@ public override void Add(in SpanContext context, long value, IEnumerable> labels) + public override void Add(in CorrelationContext context, long value, IEnumerable> labels) { // user not using bound instrument. Hence create a short-lived bound instrument. this.Bind(new LabelSetSdk(labels), isShortLived: true).Add(context, value); diff --git a/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextBuilderTest.cs b/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextBuilderTest.cs deleted file mode 100644 index 35423f57848..00000000000 --- a/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextBuilderTest.cs +++ /dev/null @@ -1,132 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Collections.Generic; -using Xunit; - -namespace OpenTelemetry.Context.Test -{ - public class CorrelationContextBuilderTest - { - private const string Key1 = "key 1"; - private const string Key2 = "key 2"; - - private const string Value1 = "value 1"; - private const string Value2 = "value 2"; - - private static readonly List List1 = new List(1) - { new CorrelationContextEntry(Key1, Value1) }; - - private static readonly List List2 = new List(2) - { - new CorrelationContextEntry(Key1, Value1), - new CorrelationContextEntry(Key2, Value2), - }; - - public CorrelationContextBuilderTest() - { - DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; - } - - [Fact] - public void ContextCreation() - { - CorrelationContext dc = CorrelationContextBuilder.CreateContext(null); - Assert.Equal(CorrelationContext.Empty, dc); - - dc = CorrelationContextBuilder.CreateContext(CorrelationContext.Empty.Entries); - Assert.Equal(CorrelationContext.Empty, dc); - - dc = CorrelationContextBuilder.CreateContext(Key1, Value1); - Assert.Equal(CorrelationContextBuilder.CreateContext(List1), dc); - - Assert.Equal(dc, new CorrelationContextBuilder(dc).Build()); - } - - [Fact] - public void AddEntries() - { - Assert.Equal(CorrelationContext.Empty, new CorrelationContextBuilder(inheritCurrentContext: false).Build()); - - Assert.Equal( - CorrelationContextBuilder.CreateContext(List1), new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(Key1, Value1) - .Build()); - - Assert.Equal( - CorrelationContextBuilder.CreateContext(List1), new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(new CorrelationContextEntry(Key1, Value1)) - .Build()); - - Assert.Equal( - CorrelationContextBuilder.CreateContext(List2), new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(Key1, Value1) - .Add(Key2, Value2) - .Build()); - - Assert.Equal( - CorrelationContextBuilder.CreateContext(List2), new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(new CorrelationContextEntry(Key1, Value1)) - .Add(new CorrelationContextEntry(Key2, Value2)) - .Build()); - - Assert.Equal( - CorrelationContextBuilder.CreateContext(List1), new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(List1) - .Build()); - - Assert.Equal( - CorrelationContextBuilder.CreateContext(List2), new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(List2) - .Build()); - } - - [Fact] - public void RemoveEntries() - { - Assert.Equal( - CorrelationContextBuilder.CreateContext(List1), new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(List2) - .Remove(Key2) - .Build()); - - Assert.Equal( - CorrelationContext.Empty, new CorrelationContextBuilder(inheritCurrentContext: false) - .Add(List2) - .Remove(Key2) - .Remove(Key1) - .Build()); - } - - [Fact] - public void EnsureEmptyListAfterBuild() - { - var dcb = new CorrelationContextBuilder(inheritCurrentContext: false); - Assert.Equal(CorrelationContext.Empty, dcb.Build()); - - dcb.Add(List2); - Assert.Equal(CorrelationContextBuilder.CreateContext(List2), dcb.Build()); - Assert.Equal(CorrelationContext.Empty, dcb.Build()); - - var dc = dcb.Add(List1).Build(); - Assert.Equal(dc, dcb.Add(List1).Build()); - - dcb = new CorrelationContextBuilder(dc); - Assert.Equal(dc, dcb.Build()); - Assert.Equal(CorrelationContext.Empty, dcb.Build()); - } - } -} diff --git a/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextEntryTest.cs b/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextEntryTest.cs deleted file mode 100644 index 465f5bf72fc..00000000000 --- a/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextEntryTest.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using Xunit; - -namespace OpenTelemetry.Context.Test -{ - public class CorrelationContextEntryTest - { - [Fact] - public void TestGetKey() - { - Assert.Equal("k", new CorrelationContextEntry("k", "v").Key); - } - - [Fact] - public void TestTagEquals() - { - var tag1 = new CorrelationContextEntry("Key", "foo"); - var tag2 = new CorrelationContextEntry("Key", "foo"); - var tag3 = new CorrelationContextEntry("Key", "bar"); - var tag4 = new CorrelationContextEntry("Key2", "foo"); - Assert.Equal(tag1, tag2); - Assert.NotEqual(tag1, tag3); - Assert.NotEqual(tag1, tag4); - Assert.NotEqual(tag2, tag3); - Assert.NotEqual(tag2, tag4); - Assert.NotEqual(tag3, tag4); - } - - [Fact] - public void TestNullKeyNullValue() - { - var entry = new CorrelationContextEntry(null, null); - Assert.Empty(entry.Key); - Assert.Empty(entry.Value); - } - - [Fact] - public void TestNullKey() - { - var entry = new CorrelationContextEntry(null, "foo"); - Assert.Empty(entry.Key); - Assert.Equal("foo", entry.Value); - } - - [Fact] - public void TestNullValue() - { - var entry = new CorrelationContextEntry("foo", null); - Assert.Equal("foo", entry.Key); - Assert.Empty(entry.Value); - } - } -} diff --git a/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextTest.cs b/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextTest.cs index fd41fe146b2..56296958203 100644 --- a/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Context/CorrelationContextTest.cs @@ -14,6 +14,7 @@ // limitations under the License. // using System.Collections.Generic; +using System.Diagnostics; using Xunit; namespace OpenTelemetry.Context.Test @@ -26,36 +27,40 @@ public class CorrelationContextTest private const string V1 = "v1"; private const string V2 = "v2"; - public CorrelationContextTest() - { - DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; - } - [Fact] public void EmptyContext() { - var dc = CorrelationContextBuilder.CreateContext(new List()); - Assert.Empty(dc.Entries); - Assert.Equal(CorrelationContext.Empty, dc); + var cc = CorrelationContext.Current; + Assert.Empty(cc.Correlations); + Assert.Equal(CorrelationContext.Empty, cc); } [Fact] public void NonEmptyContext() { - var list = new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }; - var dc = CorrelationContextBuilder.CreateContext(list); - Assert.Equal(list, dc.Entries); + using Activity activity = new Activity("TestActivity"); + activity.Start(); + + var list = new List>(2) { new KeyValuePair(K1, V1), new KeyValuePair(K2, V2) }; + + var cc = CorrelationContext.Current; + + cc.AddCorrelation(K1, V1); + cc.AddCorrelation(K2, V2); + + Assert.NotEqual(CorrelationContext.Empty, cc); + Assert.Equal(list, cc.Correlations); } - [Fact] + /*[Fact] public void AddExtraKey() { var list = new List(1) { new CorrelationContextEntry(K1, V1) }; - var dc = CorrelationContextBuilder.CreateContext(list); + var cc = CorrelationContextBuilder.CreateContext(list); Assert.Equal(list, dc.Entries); list.Add(new CorrelationContextEntry(K2, V2)); - var dc1 = CorrelationContextBuilder.CreateContext(list); + var cc1 = CorrelationContextBuilder.CreateContext(list); Assert.Equal(list, dc1.Entries); } @@ -63,7 +68,7 @@ public void AddExtraKey() public void AddExistingKey() { var list = new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K1, V2) }; - var dc = CorrelationContextBuilder.CreateContext(list); + var cc = CorrelationContextBuilder.CreateContext(list); Assert.Equal(new List(1) { new CorrelationContextEntry(K1, V2) }, dc.Entries); } @@ -78,7 +83,7 @@ public void UseDefaultEntry() public void RemoveExistingKey() { var list = new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }; - var dc = CorrelationContextBuilder.CreateContext(list); + var cc = CorrelationContextBuilder.CreateContext(list); Assert.Equal(list, dc.Entries); list.RemoveAt(0); @@ -95,7 +100,7 @@ public void RemoveExistingKey() public void TestIterator() { var list = new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }; - var dc = CorrelationContextBuilder.CreateContext(list); + var cc = CorrelationContextBuilder.CreateContext(list); var i = dc.Entries.GetEnumerator(); Assert.True(i.MoveNext()); @@ -109,11 +114,11 @@ public void TestIterator() [Fact] public void TestEquals() { - var dc1 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }); - var dc2 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }); - var dc3 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K2, V2), new CorrelationContextEntry(K1, V1) }); - var dc4 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V1) }); - var dc5 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V2), new CorrelationContextEntry(K2, V1) }); + var cc1 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }); + var cc2 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }); + var cc3 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K2, V2), new CorrelationContextEntry(K1, V1) }); + var cc4 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V1) }); + var cc5 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V2), new CorrelationContextEntry(K2, V1) }); Assert.True(dc1.Equals(dc2)); Assert.True(dc1.Equals(dc3)); @@ -123,6 +128,6 @@ public void TestEquals() Assert.False(dc3.Equals(dc4)); Assert.False(dc5.Equals(dc4)); Assert.False(dc4.Equals(dc5)); - } + }*/ } } diff --git a/test/OpenTelemetry.Tests/Implementation/Context/DistributedContextsScopeTest.cs b/test/OpenTelemetry.Tests/Implementation/Context/DistributedContextsScopeTest.cs deleted file mode 100644 index f3df27d01be..00000000000 --- a/test/OpenTelemetry.Tests/Implementation/Context/DistributedContextsScopeTest.cs +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; - -namespace OpenTelemetry.Context.Test -{ - public class DistributedContextsScopeTest - { - private const string Key1 = "key 1"; - private const string Key2 = "key 2"; - - private const string Value1 = "value 1"; - private const string Value2 = "value 2"; - - [Fact] - public void NoopContextCarrier() - { - DistributedContext.Carrier = NoopDistributedContextCarrier.Instance; - List list = new List(2) - { - new CorrelationContextEntry(Key1, Value1), new CorrelationContextEntry(Key2, Value2), - }; - Assert.Equal(DistributedContext.Empty, DistributedContext.Current); - - using (DistributedContext.SetCurrent(DistributedContextBuilder.CreateContext(Key1, Value1))) - { - Assert.Equal(DistributedContext.Empty, DistributedContext.Current); - using (DistributedContext.SetCurrent(DistributedContextBuilder.CreateContext(list))) - { - Assert.Equal(DistributedContext.Empty, DistributedContext.Current); - } - } - - Assert.Equal(DistributedContext.Empty, DistributedContext.Current); - } - - [Fact] - public async void AsyncContextCarrier() - { - DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; - List list = new List(2) { new CorrelationContextEntry(Key1, Value1), new CorrelationContextEntry(Key2, Value2), }; - - var dc1 = DistributedContextBuilder.CreateContext(Key1, Value1); - var dc2 = DistributedContextBuilder.CreateContext(list); - - DistributedContext.SetCurrent(DistributedContext.Empty); - Assert.Equal(DistributedContext.Empty, DistributedContext.Current); - - using (DistributedContext.SetCurrent(dc1)) - { - Assert.Equal(dc1, DistributedContext.Current); - using (DistributedContext.SetCurrent(dc2)) - { - Assert.Equal(dc2, DistributedContext.Current); - } - - Assert.Equal(dc1, DistributedContext.Current); - - using (DistributedContext.SetCurrent(dc2)) - { - await Task.Run(() => Assert.Equal(dc2, DistributedContext.Current)); - } - - await Task.Run(() => Assert.Equal(dc1, DistributedContext.Current)); - } - - Assert.Equal(DistributedContext.Empty, DistributedContext.Current); - await Task.Run(() => Assert.Equal(DistributedContext.Empty, DistributedContext.Current)); - } - - [Fact] - public async void TestContextInheritance() - { - DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; - var list1 = new List(1) { new CorrelationContextEntry(Key1, Value1) }; - var list2 = new List(2) { new CorrelationContextEntry(Key1, Value1), new CorrelationContextEntry(Key2, Value2) }; - - DistributedContext.SetCurrent(DistributedContext.Empty); - await Task.Run(() => Assert.Equal(DistributedContext.Empty, DistributedContext.Current)); - - using (DistributedContext.SetCurrent(DistributedContextBuilder.CreateContext(list1))) - { - await Task.Run(() => Assert.Equal(DistributedContextBuilder.CreateContext(list1), DistributedContext.Current)); - - using (DistributedContext.SetCurrent(new DistributedContextBuilder(inheritCurrentContext: true).Build())) - { - await Task.Run(() => Assert.Equal(DistributedContextBuilder.CreateContext(list1), DistributedContext.Current)); - - using (DistributedContext.SetCurrent(new DistributedContextBuilder(inheritCurrentContext: true).Correlations(b => b.Add(Key2, Value2)).Build())) - { - await Task.Run(() => Assert.Equal(DistributedContextBuilder.CreateContext(list2), DistributedContext.Current)); - using (DistributedContext.SetCurrent(new DistributedContextBuilder(inheritCurrentContext: true).Correlations(b => b.Remove(Key2)).Build())) - { - await Task.Run(() => Assert.Equal(DistributedContextBuilder.CreateContext(list1), DistributedContext.Current)); - } - } - - await Task.Run(() => Assert.Equal(DistributedContextBuilder.CreateContext(list1), DistributedContext.Current)); - - using (DistributedContext.SetCurrent(new DistributedContextBuilder(inheritCurrentContext: false).Build())) - { - await Task.Run(() => Assert.Equal(DistributedContext.Empty, DistributedContext.Current)); - } - - await Task.Run(() => Assert.Equal(DistributedContextBuilder.CreateContext(list1), DistributedContext.Current)); - } - } - } - } -} diff --git a/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextDeserializationTest.cs b/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextDeserializationTest.cs deleted file mode 100644 index 5cda9b1de98..00000000000 --- a/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextDeserializationTest.cs +++ /dev/null @@ -1,272 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Collections.Generic; -using System.IO; -using System.Text; -using OpenTelemetry.Internal; -using Xunit; - -namespace OpenTelemetry.Context.Propagation.Test -{ - public class DistributedContextDeserializationTest - { - private readonly DistributedContextBinarySerializer serializer; - - public DistributedContextDeserializationTest() - { - DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; - this.serializer = new DistributedContextBinarySerializer(); - } - - [Fact] - public void TestConstants() - { - // Refer to the JavaDoc on SerializationUtils for the definitions on these constants. - Assert.Equal(0, SerializationUtils.VersionId); - Assert.Equal(0, SerializationUtils.TagFieldId); - Assert.Equal(8192, SerializationUtils.TagContextSerializedSizeLimit); - } - - [Fact] - public void TestNoTagsSerialization() - { - var dc = this.serializer.FromByteArray(this.serializer.ToByteArray(DistributedContext.Empty)); - Assert.Empty(dc.CorrelationContext.Entries); - - dc = this.serializer.FromByteArray(new byte[] { SerializationUtils.VersionId }); // One byte that represents Version ID. - Assert.Empty(dc.CorrelationContext.Entries); - } - - [Fact] - public void TestDeserializeEmptyByteArrayThrowException() - { - Assert.Equal(DistributedContext.Empty, this.serializer.FromByteArray(new byte[0])); - } - - [Fact] - public void TestDeserializeTooLargeByteArrayThrowException() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - - for (var i = 0; i < (SerializationUtils.TagContextSerializedSizeLimit / 8) - 1; i++) - { - // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8. - string str = i.ToString("0000"); - EncodeTagToOutPut(str, str, output); - } - - // The last tag will be of size 9, so the total size of the TagContext (8193) will be one byte - // more than limit. - EncodeTagToOutPut("last", "last1", output); - - var bytes = output.ToArray(); - Assert.Equal(DistributedContext.Empty, this.serializer.FromByteArray(bytes)); - } - - // Deserializing this inPut should cause an error, even though it represents a relatively small - // TagContext. - [Fact] - public void TestDeserializeTooLargeByteArrayThrowException_WithDuplicateTagKeys() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - - for (var i = 0; i < (SerializationUtils.TagContextSerializedSizeLimit / 8) - 1; i++) - { - // Each tag will be with format {key : "key_", value : "0123"}, so the length of it is 8. - EncodeTagToOutPut("key_", i.ToString("0000"), output); - } - - // The last tag will be of size 9, so the total size of the TagContext (8193) will be one byte - // more than limit. - EncodeTagToOutPut("key_", "last1", output); - - var bytes = output.ToArray(); - Assert.Equal(DistributedContext.Empty, this.serializer.FromByteArray(bytes)); - } - - [Fact] - public void TestDeserializeOneTag() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - EncodeTagToOutPut("Key", "Value", output); - - var expected = DistributedContextBuilder.CreateContext("Key", "Value"); - Assert.Equal(expected, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void TestDeserializeMultipleTags() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - EncodeTagToOutPut("Key1", "Value1", output); - EncodeTagToOutPut("Key2", "Value2", output); - - var expected = DistributedContextBuilder.CreateContext( - new List(2) { new CorrelationContextEntry("Key1", "Value1"), new CorrelationContextEntry("Key2", "Value2") }); - Assert.Equal(expected, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void TestDeserializeDuplicateKeys() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - EncodeTagToOutPut("Key1", "Value1", output); - EncodeTagToOutPut("Key1", "Value2", output); - - var expected = DistributedContextBuilder.CreateContext("Key1", "Value2"); - Assert.Equal(expected, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void TestDeserializeNonConsecutiveDuplicateKeys() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - EncodeTagToOutPut("Key1", "Value1", output); - EncodeTagToOutPut("Key2", "Value2", output); - EncodeTagToOutPut("Key3", "Value3", output); - EncodeTagToOutPut("Key1", "Value4", output); - EncodeTagToOutPut("Key2", "Value5", output); - - var expected = DistributedContextBuilder.CreateContext( - new List(3) - { - new CorrelationContextEntry("Key1", "Value4"), - new CorrelationContextEntry("Key2", "Value5"), - new CorrelationContextEntry("Key3", "Value3"), - }); - Assert.Equal(expected, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void TestDeserializeDuplicateTags() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - EncodeTagToOutPut("Key1", "Value1", output); - EncodeTagToOutPut("Key1", "Value2", output); - - var expected = DistributedContextBuilder.CreateContext("Key1", "Value2"); - Assert.Equal(expected, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void TestDeserializeNonConsecutiveDuplicateTags() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - EncodeTagToOutPut("Key1", "Value1", output); - EncodeTagToOutPut("Key2", "Value2", output); - EncodeTagToOutPut("Key3", "Value3", output); - EncodeTagToOutPut("Key1", "Value1", output); - EncodeTagToOutPut("Key2", "Value2", output); - - var expected = DistributedContextBuilder.CreateContext( - new List(3) - { - new CorrelationContextEntry("Key1", "Value1"), - new CorrelationContextEntry("Key2", "Value2"), - new CorrelationContextEntry("Key3", "Value3"), - }); - Assert.Equal(expected, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void StopParsingAtUnknownField() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - EncodeTagToOutPut("Key1", "Value1", output); - EncodeTagToOutPut("Key2", "Value2", output); - - // Write unknown field ID 1. - output.WriteByte(1); - output.Write(new byte[] { 1, 2, 3, 4 }, 0, 4); - - EncodeTagToOutPut("Key3", "Value3", output); - - var expected = DistributedContextBuilder.CreateContext( - new List(2) - { - new CorrelationContextEntry("Key1", "Value1"), - new CorrelationContextEntry("Key2", "Value2"), - }); - Assert.Equal(expected, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void StopParsingAtUnknownTagAtStart() - { - var output = new MemoryStream(); - output.WriteByte(SerializationUtils.VersionId); - - // Write unknown field ID 1. - output.WriteByte(1); - output.Write(new byte[] { 1, 2, 3, 4 }, 0, 4); - - EncodeTagToOutPut("Key", "Value", output); - Assert.Equal(DistributedContext.Empty, this.serializer.FromByteArray(output.ToArray())); - } - - [Fact] - public void TestDeserializeWrongFormat() - { - // encoded tags should follow the format ()* - Assert.Equal(DistributedContext.Empty, this.serializer.FromByteArray(new byte[3])); - } - - [Fact] - public void TestDeserializeWrongVersionId() - { - Assert.Equal(DistributedContext.Empty, this.serializer.FromByteArray(new byte[] { SerializationUtils.VersionId + 1 })); - } - - [Fact] - public void TestDeserializeNegativeVersionId() - { - Assert.Equal(DistributedContext.Empty, this.serializer.FromByteArray(new byte[] { 0xff })); - } - - // == - // - // == varint encoded integer - // == tag_key_len bytes comprising tag key name - // == varint encoded integer - // == tag_val_len bytes comprising UTF-8 string - private static void EncodeTagToOutPut(string key, string value, MemoryStream output) - { - output.WriteByte(SerializationUtils.TagFieldId); - EncodeString(key, output); - EncodeString(value, output); - } - - private static void EncodeString(string input, MemoryStream output) - { - var length = input.Length; - var bytes = new byte[VarInt.VarIntSize(length)]; - VarInt.PutVarInt(length, bytes, 0); - output.Write(bytes, 0, bytes.Length); - var inPutBytes = Encoding.UTF8.GetBytes(input); - output.Write(inPutBytes, 0, inPutBytes.Length); - } - } -} diff --git a/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextRoundtripTest.cs b/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextRoundtripTest.cs deleted file mode 100644 index 4eb585efb32..00000000000 --- a/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextRoundtripTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Collections.Generic; -using Xunit; - -namespace OpenTelemetry.Context.Propagation.Test -{ - public class DistributedContextRoundtripTest - { - private const string K1 = "k1"; - private const string K2 = "k2"; - private const string K3 = "k3"; - - private const string V1 = "v1"; - private const string V2 = "v2"; - private const string V3 = "v3"; - - private readonly DistributedContextBinarySerializer serializer; - - public DistributedContextRoundtripTest() - { - DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; - this.serializer = new DistributedContextBinarySerializer(); - } - - [Fact] - public void TestRoundtripSerialization_NormalTagContext() - { - this.TestRoundtripSerialization(DistributedContext.Empty); - this.TestRoundtripSerialization(DistributedContextBuilder.CreateContext(K1, V1)); - - var expected = DistributedContextBuilder.CreateContext( - new List(3) - { - new CorrelationContextEntry(K1, V1), - new CorrelationContextEntry(K2, V2), - new CorrelationContextEntry(K3, V3), - }); - - this.TestRoundtripSerialization(expected); - this.TestRoundtripSerialization(DistributedContextBuilder.CreateContext(K1, string.Empty)); - } - - [Fact] - public void TestRoundtrip_TagContextWithMaximumSize() - { - var list = new List(); - - for (var i = 0; i < SerializationUtils.TagContextSerializedSizeLimit / 8; i++) - { - // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8. - // Add 1024 tags, the total size should just be 8192. - - var str = i.ToString("0000"); - list.Add(new CorrelationContextEntry(str, str)); - } - - this.TestRoundtripSerialization(DistributedContextBuilder.CreateContext(list)); - } - - private void TestRoundtripSerialization(DistributedContext expected) - { - var bytes = this.serializer.ToByteArray(expected); - var actual = this.serializer.FromByteArray(bytes); - Assert.Equal(expected, actual); - } - } -} diff --git a/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextSerializationTest.cs b/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextSerializationTest.cs deleted file mode 100644 index 03f8cc2ff2d..00000000000 --- a/test/OpenTelemetry.Tests/Implementation/Context/Propagation/DistributedContextSerializationTest.cs +++ /dev/null @@ -1,153 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using OpenTelemetry.Internal; -using Xunit; - -namespace OpenTelemetry.Context.Propagation.Test -{ - public class DistributedContextSerializationTest - { - private const string K1 = "k1"; - private const string K2 = "k2"; - private const string K3 = "k3"; - private const string K4 = "k4"; - - private const string V1 = "v1"; - private const string V2 = "v2"; - private const string V3 = "v3"; - private const string V4 = "v4"; - - private static readonly CorrelationContextEntry T1 = new CorrelationContextEntry(K1, V1); - private static readonly CorrelationContextEntry T2 = new CorrelationContextEntry(K2, V2); - private static readonly CorrelationContextEntry T3 = new CorrelationContextEntry(K3, V3); - private static readonly CorrelationContextEntry T4 = new CorrelationContextEntry(K4, V4); - - private readonly DistributedContextBinarySerializer serializer; - - public DistributedContextSerializationTest() - { - DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; - this.serializer = new DistributedContextBinarySerializer(); - } - - [Fact] - public void TestSerializeDefault() - { - this.TestSerialize(); - } - - [Fact] - public void TestSerializeWithOneTag() - { - this.TestSerialize(T1); - } - - [Fact] - public void TestSerializeWithMultipleTags() - { - this.TestSerialize(T1, T2, T3, T4); - } - - [Fact] - public void TestSerializeTooLargeTagContext() - { - var list = new List(); - - for (var i = 0; i < (SerializationUtils.TagContextSerializedSizeLimit / 8) - 1; i++) - { - // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8. - var str = i.ToString("0000"); - list.Add(new CorrelationContextEntry(str, str)); - } - - // The last tag will be of size 9, so the total size of the TagContext (8193) will be one byte - // more than limit. - list.Add(new CorrelationContextEntry("last", "last1")); - - var dc = DistributedContextBuilder.CreateContext(list); - Assert.Empty(this.serializer.ToByteArray(dc)); - } - - private static void EncodeString(string input, MemoryStream byteArrayOutPutStream) - { - VarInt.PutVarInt(input.Length, byteArrayOutPutStream); - var inpBytes = Encoding.UTF8.GetBytes(input); - byteArrayOutPutStream.Write(inpBytes, 0, inpBytes.Length); - } - - private static void RotateRight(IList sequence, int count) - { - var tmp = sequence[count - 1]; - sequence.RemoveAt(count - 1); - sequence.Insert(0, tmp); - } - - private static IEnumerable> Permutate(IList sequence, int count) - { - if (count == 0) - { - yield return sequence; - } - else - { - for (var i = 0; i < count; i++) - { - foreach (var perm in Permutate(sequence, count - 1)) - { - yield return perm; - } - - RotateRight(sequence, count); - } - } - } - - private void TestSerialize(params CorrelationContextEntry[] tags) - { - var list = new List(tags); - - var actual = this.serializer.ToByteArray(DistributedContextBuilder.CreateContext(list)); - var tagsList = tags.ToList(); - var tagPermutation = Permutate(tagsList, tagsList.Count); - ISet possibleOutPuts = new HashSet(); - - foreach (var distributedContextEntries in tagPermutation) - { - var l = (List)distributedContextEntries; - var expected = new MemoryStream(); - expected.WriteByte(SerializationUtils.VersionId); - - foreach (var tag in l) - { - expected.WriteByte(SerializationUtils.TagFieldId); - EncodeString(tag.Key, expected); - EncodeString(tag.Value, expected); - } - - var bytes = expected.ToArray(); - possibleOutPuts.Add(Encoding.UTF8.GetString(bytes)); - } - - var exp = Encoding.UTF8.GetString(actual); - Assert.Contains(exp, possibleOutPuts); - } - } -} From 6454533090d0da16be814aa9443c56a6b6417089 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Tue, 11 Aug 2020 16:25:27 -0300 Subject: [PATCH 12/33] updating issues after merge --- examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs | 2 +- examples/MicroserviceExample/Utils/Messaging/MessageSender.cs | 2 +- .../Implementation/Trace/Propagation/TraceContextTest.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs b/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs index a8448a30b86..31b74d0cbf5 100644 --- a/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs +++ b/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs @@ -63,7 +63,7 @@ public void ReceiveMessage(BasicDeliverEventArgs ea) // https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md#span-name var activityName = $"{ea.RoutingKey} receive"; - using (var activity = ActivitySource.StartActivity(activityName, ActivityKind.Consumer, parentContext)) + using (var activity = ActivitySource.StartActivity(activityName, ActivityKind.Consumer, parentContext.ActivityContext)) { try { diff --git a/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs b/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs index 8eded341382..5ca2b73cbb4 100644 --- a/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs +++ b/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs @@ -61,7 +61,7 @@ public string SendMessage() if (activity != null) { // Inject the ActivityContext into the message headers to propagate trace context to the receiving service. - TextFormat.Inject(activity.Context, props, this.InjectTraceContextIntoBasicProperties); + TextFormat.Inject(new TextFormatContext(activity.Context, activity.Baggage), props, this.InjectTraceContextIntoBasicProperties); // The OpenTelemetry messaging specification defines a number of attributes. These attributes are added here. RabbitMqHelper.AddMessagingTags(activity); diff --git a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs index e69ebecefb7..f441369efb4 100644 --- a/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs +++ b/test/OpenTelemetry.Tests/Implementation/Trace/Propagation/TraceContextTest.cs @@ -107,7 +107,7 @@ public void TraceContextFormat_IsBlankIfInvalid() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.False(ctx.IsValid()); + Assert.False(ctx.ActivityContext.IsValid()); } [Fact] From a07482c7aeead0e750bab929942e7b88303f9383 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Tue, 11 Aug 2020 16:28:28 -0300 Subject: [PATCH 13/33] updating based on sanity check --- src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs | 2 +- src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs b/src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs index 8eb174569fc..3b9831b9e8c 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/HttpParseResult.cs @@ -20,7 +20,7 @@ namespace OpenTelemetry.Context.Propagation internal enum HttpParseResult { /// - /// Parsed succesfully. + /// Parsed successfully. /// Parsed, diff --git a/src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs b/src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs index 5f7e11442cc..3654b87827d 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/HttpRuleParser.cs @@ -120,7 +120,7 @@ internal static HttpParseResult GetQuotedPairLength(string input, int startIndex return HttpParseResult.NotParsed; } - // Quoted-char has 2 characters. Check wheter there are 2 chars left ('\' + char) + // Quoted-char has 2 characters. Check whether there are 2 chars left ('\' + char) // If so, check whether the character is in the range 0-127. If not, it's an invalid value. if ((startIndex + 2 > input.Length) || (input[startIndex + 1] > 127)) { @@ -203,7 +203,7 @@ private static HttpParseResult GetExpressionLength( (GetQuotedPairLength(input, current, out quotedPairLength) == HttpParseResult.Parsed)) { // We ignore invalid quoted-pairs. Invalid quoted-pairs may mean that it looked like a quoted pair, - // but we actually have a quoted-string: e.g. "\ü" ('\' followed by a char >127 - quoted-pair only + // but we actually have a quoted-string: e.g. "\u" ('\' followed by a char >127 - quoted-pair only // allows ASCII chars after '\'; qdtext allows both '\' and >127 chars). current = current + quotedPairLength; continue; From 0fecef051037e6f7c558ea53e38f632038d54445 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Tue, 11 Aug 2020 16:36:19 -0300 Subject: [PATCH 14/33] updating baggage test --- .../SpanContextShimTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs b/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs index 8e0890ba2e5..b870dc1df49 100644 --- a/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs +++ b/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs @@ -50,7 +50,8 @@ public void GetSpanId() public void GetBaggage() { var shim = GetSpanContextShim(); - Assert.Throws(() => shim.GetBaggageItems()); + var baggage = shim.GetBaggageItems(); + Assert.Empty(baggage); } internal static SpanContextShim GetSpanContextShim() From 2a023885c1104d9f9a8d46b9ff4497bd823bbee6 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Tue, 11 Aug 2020 16:52:01 -0300 Subject: [PATCH 15/33] updating tests --- .../SpanContextShimTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs b/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs index b870dc1df49..352b2c1226a 100644 --- a/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs +++ b/test/OpenTelemetry.Shims.OpenTracing.Tests/SpanContextShimTests.cs @@ -51,7 +51,7 @@ public void GetBaggage() { var shim = GetSpanContextShim(); var baggage = shim.GetBaggageItems(); - Assert.Empty(baggage); + Assert.Null(baggage); } internal static SpanContextShim GetSpanContextShim() From 6f5f2f1010e920b673f16964004e3c5614b4b0cc Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Tue, 11 Aug 2020 17:12:09 -0300 Subject: [PATCH 16/33] reiley's comments --- src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index e53e01e109f..3fabd5a322e 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // + using System; using System.Collections.Generic; using System.Linq; @@ -54,7 +55,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Action> baggage) + internal static bool TryExtractBaggage(string[] baggageCollection, out IEnumerable> baggage) { int baggageLength = -1; Dictionary baggageDictionary = null; From 90ca374c1c481189b4a9b470db561699ed3f3e8a Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Tue, 11 Aug 2020 17:39:19 -0300 Subject: [PATCH 17/33] move to using --- .../HttpWebRequestInstrumentationOptions.netfx.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs index 09ea6be2409..128e8b238e5 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs @@ -15,6 +15,7 @@ // #if NETFRAMEWORK using System; +using System.Collections.Generic; using System.Net; using OpenTelemetry.Context.Propagation; using OpenTelemetry.Trace; @@ -34,7 +35,7 @@ public class HttpWebRequestInstrumentationOptions /// /// Gets or sets for context propagation. Default value: . /// - public ITextFormat TextFormat { get; set; } = new CompositePropagator(new System.Collections.Generic.List + public ITextFormat TextFormat { get; set; } = new CompositePropagator(new List { new TraceContextFormat(), new BaggageFormat(), From 9b815e7d5af9bbad0bc3a67f7786353ba6ced7a9 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 11 Aug 2020 21:17:17 -0700 Subject: [PATCH 18/33] Updates for http-in and http-out. Updated CHANGELOGs. --- src/OpenTelemetry.Api/CHANGELOG.md | 7 ++++++ .../Propagation/CompositePropagator.cs | 4 ++-- .../AspNetInstrumentationOptions.cs | 8 +++++-- .../CHANGELOG.md | 7 ++++++ .../Implementation/HttpInListener.cs | 20 ++++++++--------- .../AspNetCoreInstrumentationOptions.cs | 8 +++++-- .../CHANGELOG.md | 12 +++++++--- .../Implementation/HttpInListener.cs | 22 +++++++++---------- .../CHANGELOG.md | 7 ++++++ .../HttpClientInstrumentationOptions.cs | 4 ++-- ...pWebRequestInstrumentationOptions.netfx.cs | 5 ++--- 11 files changed, 68 insertions(+), 36 deletions(-) diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index 096f827bfff..36c68b8cbe0 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +* Added `BaggageFormat` an `ITextFormat` implementation for managing Baggage + propagation via the [W3C + Baggage](https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md) + header + ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) +* Removed `DistributedContext` as it is no longer part of the spec + ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048))) * Renaming from `ot` to `otel` ([#1046](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1046)) * Added `RuntimeContext` API diff --git a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs index 752db848a99..0a06f9eee9c 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs @@ -31,9 +31,9 @@ public class CompositePropagator : ITextFormat /// Initializes a new instance of the class. /// /// List of wire context propagator. - public CompositePropagator(List textFormats) + public CompositePropagator(IEnumerable textFormats) { - this.textFormats = textFormats ?? throw new ArgumentNullException(nameof(textFormats)); + this.textFormats = new List(textFormats ?? throw new ArgumentNullException(nameof(textFormats))); } /// diff --git a/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs index 7538298403d..67d0cddb498 100644 --- a/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs @@ -26,9 +26,13 @@ namespace OpenTelemetry.Instrumentation.AspNet public class AspNetInstrumentationOptions { /// - /// Gets or sets for context propagation. + /// Gets or sets for context propagation. Default value: with & . /// - public ITextFormat TextFormat { get; set; } = new TraceContextFormat(); + public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[] + { + new TraceContextFormat(), + new BaggageFormat(), + }); /// /// Gets or sets a hook to exclude calls based on domain or other per-request criterion. diff --git a/src/OpenTelemetry.Instrumentation.AspNet/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNet/CHANGELOG.md index 2410d0bec7a..3fb173352d6 100644 --- a/src/OpenTelemetry.Instrumentation.AspNet/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNet/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +* Changed the default propagation to support W3C Baggage + ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) + * The default ITextFormat is now `CompositePropagator(TraceContextFormat, + BaggageFormat)`. Baggage sent via the [W3C + Baggage](https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md) + header will now be parsed and set on incoming Http spans. + ## 0.3.0-beta Released 2020-07-23 diff --git a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs index a6343d3383c..2ec2e1a9457 100644 --- a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs @@ -61,10 +61,9 @@ public override void OnStartActivity(Activity activity, object payload) if (!(this.options.TextFormat is TraceContextFormat)) { - // This requires to ignore the current activity and create a new one - // using the context extracted using the format TextFormat supports. var ctx = this.options.TextFormat.Extract(default, request, HttpRequestHeaderValuesGetter); - if (ctx.ActivityContext != default) + + if (ctx.ActivityContext != activity.Context) { // Create a new activity with its parent set from the extracted context. // This makes the new activity as a "sibling" of the activity created by @@ -72,13 +71,6 @@ public override void OnStartActivity(Activity activity, object payload) Activity newOne = new Activity(ActivityNameByHttpInListener); newOne.SetParentId(ctx.ActivityContext.TraceId, ctx.ActivityContext.SpanId, ctx.ActivityContext.TraceFlags); newOne.TraceStateString = ctx.ActivityContext.TraceState; - if (ctx.ActivityBaggage != null) - { - foreach (var baggageItem in ctx.ActivityBaggage) - { - newOne.AddBaggage(baggageItem.Key, baggageItem.Value); - } - } // Starting the new activity make it the Activity.Current one. newOne.Start(); @@ -90,6 +82,14 @@ public override void OnStartActivity(Activity activity, object payload) activity.SetCustomProperty("ActivityByHttpInListener", newOne); activity = newOne; } + + if (ctx.ActivityBaggage != null) + { + foreach (var baggageItem in ctx.ActivityBaggage) + { + activity.AddBaggage(baggageItem.Key, baggageItem.Value); + } + } } // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs index 687774be972..076e20bc5ff 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs @@ -26,9 +26,13 @@ namespace OpenTelemetry.Instrumentation.AspNetCore public class AspNetCoreInstrumentationOptions { /// - /// Gets or sets for context propagation. + /// Gets or sets for context propagation. Default value: with & . /// - public ITextFormat TextFormat { get; set; } = new TraceContextFormat(); + public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[] + { + new TraceContextFormat(), + new BaggageFormat(), + }); /// /// Gets or sets a hook to exclude calls based on domain or other per-request criterion. diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 43be080ab1f..56d3d75984d 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -2,12 +2,18 @@ ## Unreleased +* Changed the default propagation to support W3C Baggage + ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) + * The default ITextFormat is now `CompositePropagator(TraceContextFormat, + BaggageFormat)`. Baggage sent via the [W3C + Baggage](https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md) + header will now be parsed and set on incoming Http spans. * Introduced support for Grpc.AspNetCore (#803). * Attributes are added to gRPC invocations: `rpc.system`, `rpc.service`, `rpc.method`. These attributes are added to an existing span generated by - the instrumentation. This is unlike the instrumentation for client-side - gRPC calls where one span is created for the gRPC call and a separate span - is created for the underlying HTTP call in the event both gRPC and HTTP + the instrumentation. This is unlike the instrumentation for client-side gRPC + calls where one span is created for the gRPC call and a separate span is + created for the underlying HTTP call in the event both gRPC and HTTP instrumentation are enabled. ## 0.3.0-beta diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 2a5ac421f97..cf182734eb0 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -67,12 +67,9 @@ public override void OnStartActivity(Activity activity, object payload) var request = context.Request; if (!this.hostingSupportsW3C || !(this.options.TextFormat is TraceContextFormat)) { - // This requires to ignore the current activity and create a new one - // using the context extracted from w3ctraceparent header or - // using the format TextFormat supports. - var ctx = this.options.TextFormat.Extract(default, request, HttpRequestHeaderValuesGetter); - if (ctx.ActivityContext != default) + + if (ctx.ActivityContext != activity.Context) { // Create a new activity with its parent set from the extracted context. // This makes the new activity as a "sibling" of the activity created by @@ -80,18 +77,19 @@ public override void OnStartActivity(Activity activity, object payload) Activity newOne = new Activity(ActivityNameByHttpInListener); newOne.SetParentId(ctx.ActivityContext.TraceId, ctx.ActivityContext.SpanId, ctx.ActivityContext.TraceFlags); newOne.TraceStateString = ctx.ActivityContext.TraceState; - if (ctx.ActivityBaggage != null) - { - foreach (var baggageItem in ctx.ActivityBaggage) - { - newOne.AddBaggage(baggageItem.Key, baggageItem.Value); - } - } // Starting the new activity make it the Activity.Current one. newOne.Start(); activity = newOne; } + + if (ctx.ActivityBaggage != null) + { + foreach (var baggageItem in ctx.ActivityBaggage) + { + activity.AddBaggage(baggageItem.Key, baggageItem.Value); + } + } } activity.SetKind(ActivityKind.Server); diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index b7781620eed..df3a092b994 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +* Changed the default propagation to support W3C Baggage + ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) + * The default ITextFormat is now `CompositePropagator(TraceContextFormat, + BaggageFormat)`. Outgoing Http request will now send Baggage using the [W3C + Baggage](https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md) + header. Previously Baggage was sent using the `Correlation-Context` header, + which is now outdated. * Removed `AddHttpInstrumentation` and `AddHttpWebRequestInstrumentation` (.NET Framework) `TracerProviderBuilderExtensions`. `AddHttpClientInstrumentation` will now register `HttpClient` instrumentation on .NET Core and `HttpClient` + diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs index 4dbadccc9c0..a7a5e4ba108 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationOptions.cs @@ -31,9 +31,9 @@ public class HttpClientInstrumentationOptions public bool SetHttpFlavor { get; set; } /// - /// Gets or sets for context propagation. Default value: . + /// Gets or sets for context propagation. Default value: with & . /// - public ITextFormat TextFormat { get; set; } = new CompositePropagator(new System.Collections.Generic.List + public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[] { new TraceContextFormat(), new BaggageFormat(), diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs index 128e8b238e5..1530fac156c 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpWebRequestInstrumentationOptions.netfx.cs @@ -15,7 +15,6 @@ // #if NETFRAMEWORK using System; -using System.Collections.Generic; using System.Net; using OpenTelemetry.Context.Propagation; using OpenTelemetry.Trace; @@ -33,9 +32,9 @@ public class HttpWebRequestInstrumentationOptions public bool SetHttpFlavor { get; set; } /// - /// Gets or sets for context propagation. Default value: . + /// Gets or sets for context propagation. Default value: with & . /// - public ITextFormat TextFormat { get; set; } = new CompositePropagator(new List + public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[] { new TraceContextFormat(), new BaggageFormat(), From 172dd1b5cc90b31452b5cdda40aa7bcff8c2e1cb Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 12 Aug 2020 23:00:47 -0700 Subject: [PATCH 19/33] Adding tests. --- .../Context/CorrelationContext.cs | 18 +++ .../Context/CorrelationContextTest.cs | 131 ++++++++++-------- .../Trace/Propagation/BaggageFormatTest.cs | 34 ++++- 3 files changed, 124 insertions(+), 59 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/CorrelationContext.cs b/src/OpenTelemetry.Api/Context/CorrelationContext.cs index 790f3607e82..5b5614a5a35 100644 --- a/src/OpenTelemetry.Api/Context/CorrelationContext.cs +++ b/src/OpenTelemetry.Api/Context/CorrelationContext.cs @@ -75,6 +75,24 @@ public CorrelationContext AddCorrelation(string key, string value) return this; } + /// + /// Adds correlation items. + /// + /// Correlation items. + /// The instance for chaining. + public CorrelationContext AddCorrelation(IEnumerable> correlations) + { + if (correlations != null) + { + foreach (KeyValuePair correlation in correlations) + { + this.activity?.AddBaggage(correlation.Key, correlation.Value); + } + } + + return this; + } + /// public bool Equals(CorrelationContext other) { diff --git a/test/OpenTelemetry.Tests/Context/CorrelationContextTest.cs b/test/OpenTelemetry.Tests/Context/CorrelationContextTest.cs index a5d1e233e0b..56bcc4430e4 100644 --- a/test/OpenTelemetry.Tests/Context/CorrelationContextTest.cs +++ b/test/OpenTelemetry.Tests/Context/CorrelationContextTest.cs @@ -21,11 +21,11 @@ namespace OpenTelemetry.Context.Tests { public class CorrelationContextTest { - private const string K1 = "k1"; - private const string K2 = "k2"; + private const string K1 = "Key1"; + private const string K2 = "Key2"; - private const string V1 = "v1"; - private const string V2 = "v2"; + private const string V1 = "Value1"; + private const string V2 = "Value2"; [Fact] public void EmptyContext() @@ -33,6 +33,11 @@ public void EmptyContext() var cc = CorrelationContext.Current; Assert.Empty(cc.Correlations); Assert.Equal(CorrelationContext.Empty, cc); + + cc.AddCorrelation(K1, V1); + Assert.Empty(cc.Correlations); + + Assert.Null(cc.GetCorrelation(K1)); } [Fact] @@ -41,7 +46,11 @@ public void NonEmptyContext() using Activity activity = new Activity("TestActivity"); activity.Start(); - var list = new List>(2) { new KeyValuePair(K1, V1), new KeyValuePair(K2, V2) }; + var list = new List>(2) + { + new KeyValuePair(K1, V1), + new KeyValuePair(K2, V2), + }; var cc = CorrelationContext.Current; @@ -50,84 +59,90 @@ public void NonEmptyContext() Assert.NotEqual(CorrelationContext.Empty, cc); Assert.Equal(list, cc.Correlations); - } - /*[Fact] - public void AddExtraKey() - { - var list = new List(1) { new CorrelationContextEntry(K1, V1) }; - var cc = CorrelationContextBuilder.CreateContext(list); - Assert.Equal(list, dc.Entries); - - list.Add(new CorrelationContextEntry(K2, V2)); - var cc1 = CorrelationContextBuilder.CreateContext(list); - Assert.Equal(list, dc1.Entries); + Assert.Equal(V1, cc.GetCorrelation(K1)); + Assert.Null(cc.GetCorrelation(K1.ToLower())); + Assert.Null(cc.GetCorrelation(K1.ToUpper())); + Assert.Null(cc.GetCorrelation("NO_KEY")); } [Fact] public void AddExistingKey() { - var list = new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K1, V2) }; - var cc = CorrelationContextBuilder.CreateContext(list); - Assert.Equal(new List(1) { new CorrelationContextEntry(K1, V2) }, dc.Entries); - } - - [Fact] - public void UseDefaultEntry() - { - Assert.Equal(CorrelationContext.Empty, CorrelationContextBuilder.CreateContext(new List(1) { default })); - Assert.Equal(CorrelationContext.Empty, CorrelationContextBuilder.CreateContext(null)); - } + using Activity activity = new Activity("TestActivity"); + activity.Start(); - [Fact] - public void RemoveExistingKey() - { - var list = new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }; - var cc = CorrelationContextBuilder.CreateContext(list); - Assert.Equal(list, dc.Entries); + var list = new List>(2) + { + new KeyValuePair(K1, V1), + new KeyValuePair(K1, V1), + }; - list.RemoveAt(0); + var cc = CorrelationContext.Current; - dc = CorrelationContextBuilder.CreateContext(list); - Assert.Equal(list, dc.Entries); + cc.AddCorrelation(K1, V1); + cc.AddCorrelation(K1, V1); - list.Clear(); - dc = CorrelationContextBuilder.CreateContext(list); - Assert.Equal(CorrelationContext.Empty, dc); + Assert.Equal(list, cc.Correlations); } [Fact] public void TestIterator() { - var list = new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }; - var cc = CorrelationContextBuilder.CreateContext(list); + using Activity activity = new Activity("TestActivity"); + activity.Start(); + + var list = new List>(2) + { + new KeyValuePair(K1, V1), + new KeyValuePair(K2, V2), + }; + + var cc = CorrelationContext.Current; + + cc.AddCorrelation(K1, V1); + cc.AddCorrelation(K2, V2); + + var i = cc.Correlations.GetEnumerator(); - var i = dc.Entries.GetEnumerator(); Assert.True(i.MoveNext()); var tag1 = i.Current; Assert.True(i.MoveNext()); var tag2 = i.Current; Assert.False(i.MoveNext()); - Assert.Equal(new List { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }, new List { tag1, tag2 }); + + Assert.Equal(list, new List> { tag1, tag2 }); } [Fact] public void TestEquals() { - var cc1 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }); - var cc2 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }); - var cc3 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K2, V2), new CorrelationContextEntry(K1, V1) }); - var cc4 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V1) }); - var cc5 = CorrelationContextBuilder.CreateContext(new List(2) { new CorrelationContextEntry(K1, V2), new CorrelationContextEntry(K2, V1) }); - - Assert.True(dc1.Equals(dc2)); - Assert.True(dc1.Equals(dc3)); - - Assert.False(dc1.Equals(dc4)); - Assert.False(dc2.Equals(dc4)); - Assert.False(dc3.Equals(dc4)); - Assert.False(dc5.Equals(dc4)); - Assert.False(dc4.Equals(dc5)); - }*/ + var cc1 = CreateCorrelationContext(new KeyValuePair(K1, V1), new KeyValuePair(K2, V2)); + var cc2 = CreateCorrelationContext(new KeyValuePair(K1, V1), new KeyValuePair(K2, V2)); + var cc3 = CreateCorrelationContext(new KeyValuePair(K2, V2), new KeyValuePair(K1, V1)); + var cc4 = CreateCorrelationContext(new KeyValuePair(K1, V1), new KeyValuePair(K2, V1)); + var cc5 = CreateCorrelationContext(new KeyValuePair(K1, V2), new KeyValuePair(K2, V1)); + + Assert.True(cc1.Equals(cc2)); + + Assert.False(cc1.Equals(cc3)); + Assert.False(cc1.Equals(cc4)); + Assert.False(cc2.Equals(cc4)); + Assert.False(cc3.Equals(cc4)); + Assert.False(cc5.Equals(cc4)); + Assert.False(cc4.Equals(cc5)); + } + + private static CorrelationContext CreateCorrelationContext(params KeyValuePair[] correlations) + { + using Activity activity = new Activity("TestActivity"); + activity.Start(); + + var cc = CorrelationContext.Current; + + cc.AddCorrelation(correlations); + + return cc; + } } } diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs index 808103e5c49..a8787e8c1fe 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs @@ -18,7 +18,7 @@ using System.Linq; using Xunit; -namespace OpenTelemetry.Context.Propagation.Test +namespace OpenTelemetry.Context.Propagation.Tests { public class BaggageFormatTest { @@ -29,6 +29,12 @@ public class BaggageFormatTest return new string[] { v }; }; + private static readonly Func>, string, IEnumerable> GetterList = + (d, k) => + { + return d.Where(i => i.Key == k).Select(i => i.Value); + }; + private static readonly Action, string, string> Setter = (carrier, name, value) => { carrier[name] = value; @@ -83,6 +89,32 @@ public void ValidateOneBaggageExtraction() Assert.Equal("test", array[0].Value); } + [Fact] + public void ValidateMultipleBaggageExtraction() + { + var carrier = new List> + { + new KeyValuePair("baggage", "name1=test1"), + new KeyValuePair("baggage", "name2=test2"), + new KeyValuePair("baggage", "name2=test2"), + }; + + var textFormatContext = this.baggage.Extract(default, carrier, GetterList); + + Assert.False(textFormatContext == default); + Assert.True(textFormatContext.ActivityContext == default); + + Assert.Equal(2, textFormatContext.ActivityBaggage.Count()); + + var array = textFormatContext.ActivityBaggage.ToArray(); + + Assert.Equal("name1", array[0].Key); + Assert.Equal("test1", array[0].Value); + + Assert.Equal("name2", array[1].Key); + Assert.Equal("test2", array[1].Value); + } + [Fact] public void ValidateLongBaggageExtraction() { From f9a3f8b19196aa7d3e73bd53967992e63e2765f9 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Thu, 13 Aug 2020 16:51:51 -0300 Subject: [PATCH 20/33] updating correlation context --- .../Context/CorrelationContext.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/CorrelationContext.cs b/src/OpenTelemetry.Api/Context/CorrelationContext.cs index a7216d4d4d9..19d0f11c3f5 100644 --- a/src/OpenTelemetry.Api/Context/CorrelationContext.cs +++ b/src/OpenTelemetry.Api/Context/CorrelationContext.cs @@ -54,6 +54,20 @@ public static CorrelationContext Current /// public IEnumerable> Correlations => this.activity?.Baggage ?? EmptyBaggage; + /// + /// Compare two entries of for equality. + /// + /// First Entry to compare. + /// Second Entry to compare. + public static bool operator ==(CorrelationContext left, CorrelationContext right) => left.Equals(right); + + /// + /// Compare two entries of for equality. + /// + /// First Entry to compare. + /// Second Entry to compare. + public static bool operator !=(CorrelationContext left, CorrelationContext right) => !(left == right); + /// /// Retrieves a correlation item. /// @@ -76,21 +90,7 @@ public CorrelationContext AddCorrelation(string key, string value) } /// - /// Compare two entries of for equality. - /// - /// First Entry to compare. - /// Second Entry to compare. - public static bool operator ==(CorrelationContext left, CorrelationContext right) => left.Equals(right); - - /// - /// Compare two entries of for equality. - /// - /// First Entry to compare. - /// Second Entry to compare. - public static bool operator !=(CorrelationContext left, CorrelationContext right) => !(left == right); - - /// - /// Gets the with the specified name. + /// Adds correlation items. /// /// Correlation items. /// The instance for chaining. @@ -142,7 +142,7 @@ public override bool Equals(object obj) /// public override int GetHashCode() { - return this.entries.GetHashCode(); + return this.Correlations.GetHashCode(); } } } From 2f6a3ff276c8867c1208e4b7035a4f4b192944e1 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 13 Aug 2020 21:59:08 -0700 Subject: [PATCH 21/33] Added test for TraceContextFormat + BaggageFormat used together. --- .../Context/Propagation/TraceContextFormat.cs | 2 +- .../Propagation/CompositePropagatorTest.cs | 32 +++++++++++++++++++ .../Trace/Propagation/TraceContextTest.cs | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index 8dcc2160036..d88178e47d4 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -74,7 +74,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func + { + new TraceContextFormat(), + new BaggageFormat(), + }); + + var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null, isRemote: true); + var baggage = new Dictionary { ["key1"] = "value1" }; + + TextFormatContext textFormatContextActivityOnly = new TextFormatContext(activityContext, null); + TextFormatContext textFormatContextBaggageOnly = new TextFormatContext(default, baggage); + TextFormatContext textFormatContextBoth = new TextFormatContext(activityContext, baggage); + + var carrier = new Dictionary(); + compositePropagator.Inject(textFormatContextActivityOnly, carrier, Setter); + TextFormatContext extractedContext = compositePropagator.Extract(default, carrier, Getter); + Assert.Equal(textFormatContextActivityOnly, extractedContext); + + carrier = new Dictionary(); + compositePropagator.Inject(textFormatContextBaggageOnly, carrier, Setter); + extractedContext = compositePropagator.Extract(default, carrier, Getter); + Assert.Equal(textFormatContextBaggageOnly, extractedContext); + + carrier = new Dictionary(); + compositePropagator.Inject(textFormatContextBoth, carrier, Setter); + extractedContext = compositePropagator.Extract(default, carrier, Getter); + Assert.Equal(textFormatContextBoth, extractedContext); + } } } diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs index 624497d609b..eafda2c644e 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs @@ -121,7 +121,7 @@ public void TraceContextFormat_TracestateToStringEmpty() var f = new TraceContextFormat(); var ctx = f.Extract(default, headers, Getter); - Assert.Empty(ctx.ActivityContext.TraceState); + Assert.Null(ctx.ActivityContext.TraceState); } [Fact] From 34c6667e1afd614560520c2fc82cb6671efd3dcd Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 13 Aug 2020 22:45:23 -0700 Subject: [PATCH 22/33] Fixed broken tests. --- .../Implementation/HttpInListener.cs | 2 +- .../Implementation/HttpInListener.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs index 2ec2e1a9457..ab9989e82f5 100644 --- a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs @@ -63,7 +63,7 @@ public override void OnStartActivity(Activity activity, object payload) { var ctx = this.options.TextFormat.Extract(default, request, HttpRequestHeaderValuesGetter); - if (ctx.ActivityContext != activity.Context) + if (ctx.ActivityContext.IsValid() && ctx.ActivityContext != activity.Context) { // Create a new activity with its parent set from the extracted context. // This makes the new activity as a "sibling" of the activity created by diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index c730f2f0b76..5ff66456058 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -69,7 +69,7 @@ public override void OnStartActivity(Activity activity, object payload) { var ctx = this.options.TextFormat.Extract(default, request, HttpRequestHeaderValuesGetter); - if (ctx.ActivityContext != activity.Context) + if (ctx.ActivityContext.IsValid() && ctx.ActivityContext != activity.Context) { // Create a new activity with its parent set from the extracted context. // This makes the new activity as a "sibling" of the activity created by From 1a8a92238bc4265f5df322973d65276e271aaab3 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 13 Aug 2020 22:45:28 -0700 Subject: [PATCH 23/33] Code review. --- src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs | 1 + src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs | 1 + src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs b/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs index cf029b22e82..6d37a966465 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/GenericHeaderParser.cs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // + using System; namespace OpenTelemetry.Context.Propagation diff --git a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs index 4c5cf7e6c51..af26eba7875 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // + using System; using System.Collections.Generic; diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index b0328287ea2..0d08369beb0 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // + using System; using System.Collections.Generic; using System.Diagnostics; From 8c95541cd4f847d5a1033cad286885f71f19d423 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 13 Aug 2020 23:43:09 -0700 Subject: [PATCH 24/33] Test fixup. --- .../AspNetInstrumentationOptions.cs | 7 +------ .../Implementation/HttpInListener.cs | 15 +++++++------- .../AspNetCoreInstrumentationOptions.cs | 7 +------ .../Implementation/HttpInListener.cs | 2 +- .../HttpInListenerTests.cs | 20 ++++++++++++++----- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs index 67d0cddb498..80b57e3d655 100644 --- a/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNet/AspNetInstrumentationOptions.cs @@ -37,11 +37,6 @@ public class AspNetInstrumentationOptions /// /// Gets or sets a hook to exclude calls based on domain or other per-request criterion. /// - internal Predicate RequestFilter { get; set; } = DefaultFilter; - - private static bool DefaultFilter(HttpContext httpContext) - { - return true; - } + internal Predicate RequestFilter { get; set; } } } diff --git a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs index ab9989e82f5..bab7424db80 100644 --- a/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNet/Implementation/HttpInListener.cs @@ -49,7 +49,7 @@ public override void OnStartActivity(Activity activity, object payload) return; } - if (this.options.RequestFilter != null && !this.options.RequestFilter(context)) + if (this.options.RequestFilter?.Invoke(context) == false) { AspNetInstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName); activity.IsAllDataRequested = false; @@ -121,6 +121,7 @@ public override void OnStartActivity(Activity activity, object payload) public override void OnStopActivity(Activity activity, object payload) { Activity activityToEnrich = activity; + Activity createdActivity = null; if (!(this.options.TextFormat is TraceContextFormat)) { @@ -132,9 +133,10 @@ public override void OnStopActivity(Activity activity, object payload) if (activity.OperationName.Equals("Microsoft.AspNet.HttpReqIn.Start")) { // This block is hit if Asp.Net did restore Current to its own activity, - // then we need to retrieve the one created by HttpInListener - // and populate tags to it. - activityToEnrich = (Activity)activity.GetCustomProperty("ActivityByHttpInListener"); + // and we need to retrieve the one created by HttpInListener, + // or an additional activity was never created. + createdActivity = (Activity)activity.GetCustomProperty("ActivityByHttpInListener"); + activityToEnrich = createdActivity ?? activity; } } @@ -197,13 +199,12 @@ public override void OnStopActivity(Activity activity, object payload) var activityByAspNet = (Activity)activity.GetCustomProperty("ActivityByAspNet"); Activity.Current = activityByAspNet; } - else if (activity.OperationName.Equals("Microsoft.AspNet.HttpReqIn.Start")) + else if (createdActivity != null) { // This block is hit if Asp.Net did restore Current to its own activity, // then we need to retrieve the one created by HttpInListener // and stop it. - var activityByHttpInListener = (Activity)activity.GetCustomProperty("ActivityByHttpInListener"); - activityByHttpInListener.Stop(); + createdActivity.Stop(); // Restore current back to the one created by Asp.Net Activity.Current = activity; diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs index 076e20bc5ff..08722ac7437 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationOptions.cs @@ -37,11 +37,6 @@ public class AspNetCoreInstrumentationOptions /// /// Gets or sets a hook to exclude calls based on domain or other per-request criterion. /// - internal Predicate RequestFilter { get; set; } = DefaultFilter; - - private static bool DefaultFilter(HttpContext httpContext) - { - return true; - } + internal Predicate RequestFilter { get; set; } } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 5ff66456058..58c07e5f8a3 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -57,7 +57,7 @@ public override void OnStartActivity(Activity activity, object payload) return; } - if (this.options.RequestFilter != null && !this.options.RequestFilter(context)) + if (this.options.RequestFilter?.Invoke(context) == false) { AspNetCoreInstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName); activity.IsAllDataRequested = false; diff --git a/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs b/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs index 6deefdc5791..c0c77bf7537 100644 --- a/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs @@ -53,10 +53,8 @@ public void Dispose() [InlineData("https://localhost:443/about_attr_route/10", 2, "about_attr_route/{customerId}", "TraceContext")] [InlineData("http://localhost:1880/api/weatherforecast", 3, "api/{controller}/{id}", "TraceContext")] [InlineData("https://localhost:1843/subroute/10", 4, "subroute/{customerId}", "TraceContext")] - - // TODO: Reenable this tests once filtering mechanism is designed. - // [InlineData("http://localhost/api/value", 0, null, "/api/value")] // Request will be filtered - // [InlineData("http://localhost/api/value", 0, null, "{ThrowException}")] // Filter user code will throw an exception + [InlineData("http://localhost/api/value", 0, null, "TraceContext", "/api/value")] // Request will be filtered + [InlineData("http://localhost/api/value", 0, null, "TraceContext", "{ThrowException}")] // Filter user code will throw an exception [InlineData("http://localhost/api/value/2", 0, null, "CustomContext", "/api/value")] // Request will not be filtered [InlineData("http://localhost/api/value/2", 0, null, "CustomContext", "/api/value", true)] // Request will not be filtered public void AspNetRequestsAreCollectedSuccessfully( @@ -188,7 +186,19 @@ public void AspNetRequestsAreCollectedSuccessfully( if (HttpContext.Current.Request.Path == filter || filter == "{ThrowException}") { - Assert.Equal(0, activityProcessor.Invocations.Count); // Nothing was called because request was filtered. + if (filter == "{ThrowException}") + { + // This behavior is not good. If filter throws, Stop is called without Start. + // Need to do something here, but user can't currently set the filter + // so it wil always noop. When we support user filter, + // treat this as a todo: define exception behavior. + Assert.Equal(2, activityProcessor.Invocations.Count); // Stop & Disposed called. + } + else + { + Assert.Equal(1, activityProcessor.Invocations.Count); // Only disposed was called because request was filtered. + } + return; } From 302267f8c331b440e63d6270e31eb7bf536a47d7 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Fri, 14 Aug 2020 06:49:01 -0300 Subject: [PATCH 25/33] updating order --- src/OpenTelemetry.Api/Trace/TelemetrySpan.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs b/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs index ed781089252..11e97a8e26a 100644 --- a/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs +++ b/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs @@ -66,6 +66,11 @@ public bool IsRecording } } + /// + /// Gets the span baggage. + /// + public IEnumerable> Baggage => this.Activity?.Baggage ?? EmptyBaggage; + /// /// Sets the status of the span execution. /// @@ -75,11 +80,6 @@ public void SetStatus(Status value) this.Activity?.SetStatus(value); } - /// - /// Gets the span baggage. - /// - public IEnumerable> Baggage => this.Activity?.Baggage ?? EmptyBaggage; - /// /// Updates the name. /// From bb25ad094cbbd969f41148444cae411f8789d58d Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Fri, 14 Aug 2020 07:01:00 -0300 Subject: [PATCH 26/33] updating tests --- test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs | 4 ++-- test/OpenTelemetry.Tests/Trace/TracerTest.cs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs b/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs index 35c37e4022a..03e858d80c3 100644 --- a/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs +++ b/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs @@ -151,7 +151,7 @@ public void TracerSdkSetsActivityDataRequestBasedOnSamplingDecision() public void ProcessorDoesNotReceiveNotRecordDecisionSpan() { var testSampler = new TestSampler(); - TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); + using TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); bool startCalled = false; bool endCalled = false; @@ -189,7 +189,7 @@ public void ProcessorDoesNotReceiveNotRecordDecisionSpan() [Fact] public void TracerProvideSdkCreatesActivitySource() { - TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); + using TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); bool startCalled = false; bool endCalled = false; diff --git a/test/OpenTelemetry.Tests/Trace/TracerTest.cs b/test/OpenTelemetry.Tests/Trace/TracerTest.cs index 29353d9b7bf..21d6e1b4074 100644 --- a/test/OpenTelemetry.Tests/Trace/TracerTest.cs +++ b/test/OpenTelemetry.Tests/Trace/TracerTest.cs @@ -23,11 +23,13 @@ namespace OpenTelemetry.Trace.Tests public class TracerTest : IDisposable { // TODO: This is only a basic test. This must cover the entire shim API scenarios. + private readonly TracerProvider tracerProvider; private readonly Tracer tracer; public TracerTest() { - this.tracer = TracerProvider.Default.GetTracer("tracername", "tracerversion"); + this.tracerProvider = TracerProvider.Default; + this.tracer = this.tracerProvider.GetTracer("tracername", "tracerversion"); } [Fact] @@ -248,6 +250,8 @@ public void CreateSpan_NotSampled() public void Dispose() { Activity.Current = null; + this.tracerProvider.Dispose(); + GC.SuppressFinalize(this); } private static bool IsNoopSpan(TelemetrySpan span) From 374b4b0036b336c29dfe618690a0d196b11821f0 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Fri, 14 Aug 2020 07:27:41 -0300 Subject: [PATCH 27/33] updating tests, adding dispose, clearing objects --- .../Trace/ActivitySourceAdapterTest.cs | 2 ++ .../Trace/CompositeActivityProcessorTests.cs | 14 +++++++------- .../Propagation/CompositePropagatorTest.cs | 17 ----------------- .../Trace/TracerProvideSdkTest.cs | 7 ++++++- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/test/OpenTelemetry.Tests/Trace/ActivitySourceAdapterTest.cs b/test/OpenTelemetry.Tests/Trace/ActivitySourceAdapterTest.cs index ae31ae2d752..e7267bdf9da 100644 --- a/test/OpenTelemetry.Tests/Trace/ActivitySourceAdapterTest.cs +++ b/test/OpenTelemetry.Tests/Trace/ActivitySourceAdapterTest.cs @@ -221,6 +221,8 @@ public void ActivitySourceAdapterPopulatesSamplingParamsCorrectlyForActivityWith public void Dispose() { Activity.Current = null; + this.testProcessor.Dispose(); + GC.SuppressFinalize(this); } } } diff --git a/test/OpenTelemetry.Tests/Trace/CompositeActivityProcessorTests.cs b/test/OpenTelemetry.Tests/Trace/CompositeActivityProcessorTests.cs index 06502bbeef3..762db1bd518 100644 --- a/test/OpenTelemetry.Tests/Trace/CompositeActivityProcessorTests.cs +++ b/test/OpenTelemetry.Tests/Trace/CompositeActivityProcessorTests.cs @@ -35,10 +35,10 @@ public void CompositeActivityProcessor_CallsAllProcessorSequentially() { var result = string.Empty; - var p1 = new TestActivityProcessor( + using var p1 = new TestActivityProcessor( activity => { result += "1"; }, activity => { result += "3"; }); - var p2 = new TestActivityProcessor( + using var p2 = new TestActivityProcessor( activity => { result += "2"; }, activity => { result += "4"; }); @@ -56,7 +56,7 @@ public void CompositeActivityProcessor_CallsAllProcessorSequentially() [Fact] public void CompositeActivityProcessor_ProcessorThrows() { - var p1 = new TestActivityProcessor( + using var p1 = new TestActivityProcessor( activity => { throw new Exception("Start exception"); }, activity => { throw new Exception("End exception"); }); @@ -72,8 +72,8 @@ public void CompositeActivityProcessor_ProcessorThrows() [Fact] public void CompositeActivityProcessor_ShutsDownAll() { - var p1 = new TestActivityProcessor(null, null); - var p2 = new TestActivityProcessor(null, null); + using var p1 = new TestActivityProcessor(null, null); + using var p2 = new TestActivityProcessor(null, null); using (var processor = new CompositeActivityProcessor(new[] { p1, p2 })) { @@ -86,8 +86,8 @@ public void CompositeActivityProcessor_ShutsDownAll() [Fact] public void CompositeActivityProcessor_ForceFlush() { - var p1 = new TestActivityProcessor(null, null); - var p2 = new TestActivityProcessor(null, null); + using var p1 = new TestActivityProcessor(null, null); + using var p2 = new TestActivityProcessor(null, null); using (var processor = new CompositeActivityProcessor(new[] { p1, p2 })) { diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs index 647c4e3e4aa..4efb9941a34 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs @@ -18,14 +18,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using OpenTelemetry.Context.Propagation; using Xunit; namespace OpenTelemetry.Context.Propagation.Tests { public class CompositePropagatorTest { - private const string TraceParent = "traceparent"; private static readonly string[] Empty = new string[0]; private static readonly Func, string, IEnumerable> Getter = (headers, name) => { @@ -48,21 +46,6 @@ public class CompositePropagatorTest private readonly ActivityTraceId traceId = ActivityTraceId.CreateRandom(); private readonly ActivitySpanId spanId = ActivitySpanId.CreateRandom(); - static CompositePropagatorTest() - { - Activity.DefaultIdFormat = ActivityIdFormat.W3C; - Activity.ForceDefaultIdFormat = true; - - var listener = new ActivityListener - { - ShouldListenTo = _ => true, - GetRequestedDataUsingParentId = (ref ActivityCreationOptions options) => ActivityDataRequest.AllData, - GetRequestedDataUsingContext = (ref ActivityCreationOptions options) => ActivityDataRequest.AllData, - }; - - ActivitySource.AddActivityListener(listener); - } - [Fact] public void CompositePropagator_NullTextFormatList() { diff --git a/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs b/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs index 03e858d80c3..c83e86b58c5 100644 --- a/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs +++ b/test/OpenTelemetry.Tests/Trace/TracerProvideSdkTest.cs @@ -21,7 +21,7 @@ namespace OpenTelemetry.Trace.Tests { - public class TracerProvideSdkTest + public class TracerProvideSdkTest : IDisposable { private const string ActivitySourceName = "TraceSdkTest"; @@ -277,6 +277,11 @@ public void TracerProvideSdkCreatesAndDiposesInstrumentation() Assert.True(testInstrumentation.IsDisposed); } + public void Dispose() + { + GC.SuppressFinalize(this); + } + private class TestSampler : Sampler { public SamplingResult DesiredSamplingResult { get; set; } = new SamplingResult(SamplingDecision.RecordAndSampled); From 4fb391ae6790ea5a9b9664d6cbc5f4fda319c710 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Tue, 18 Aug 2020 06:32:06 -0300 Subject: [PATCH 28/33] updating changelog --- src/OpenTelemetry.Api/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index 36c68b8cbe0..6a6cd52c944 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* `TextFormatContext` is now used instead of `ActivityContext` in the + `ITextFormat` + ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) * Added `BaggageFormat` an `ITextFormat` implementation for managing Baggage propagation via the [W3C Baggage](https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md) From eb8599196a95015d2afae3b224737dc370231449 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 18 Aug 2020 08:20:12 -0700 Subject: [PATCH 29/33] Use "Baggage" instead of "baggage" as the header name. --- src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index 3fabd5a322e..550974347a6 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -28,7 +28,7 @@ namespace OpenTelemetry.Context.Propagation /// public class BaggageFormat : ITextFormat { - private const string Baggage = "baggage"; + private const string Baggage = "Baggage"; private const int MaxBaggageLength = 1024; /// From 1b3b49ffd83e352d23c4b4d2dcb4a49c24f47bb0 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 18 Aug 2020 08:43:58 -0700 Subject: [PATCH 30/33] Added some basic support for the Baggage limits specified in the spec. --- .../Context/Propagation/BaggageFormat.cs | 23 +++++++++++-------- .../Trace/Propagation/BaggageFormatTest.cs | 16 ++++++------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index 550974347a6..302a69cec15 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -28,11 +28,13 @@ namespace OpenTelemetry.Context.Propagation /// public class BaggageFormat : ITextFormat { - private const string Baggage = "Baggage"; - private const int MaxBaggageLength = 1024; + internal const string BaggageHeaderName = "Baggage"; + + private const int MaxBaggageLength = 8192; + private const int MaxBaggageItems = 180; /// - public ISet Fields => new HashSet { Baggage }; + public ISet Fields => new HashSet { BaggageHeaderName }; /// public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) @@ -52,7 +54,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func> baggage = null; - var baggageCollection = getter(carrier, Baggage); + var baggageCollection = getter(carrier, BaggageHeaderName); if (baggageCollection?.Any() ?? false) { TryExtractBaggage(baggageCollection.ToArray(), out baggage); @@ -87,28 +89,30 @@ public void Inject(TextFormatContext context, T carrier, Action> e = context.ActivityBaggage?.GetEnumerator(); - if (e != null && e.MoveNext()) + if (e?.MoveNext() == true) { + int itemCount = 1; StringBuilder baggage = new StringBuilder(); do { KeyValuePair item = e.Current; baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(','); } - while (e.MoveNext()); + while (e.MoveNext() && itemCount++ < MaxBaggageItems && baggage.Length < MaxBaggageLength); baggage.Remove(baggage.Length - 1, 1); - setter(carrier, Baggage, baggage.ToString()); + setter(carrier, BaggageHeaderName, baggage.ToString()); } } internal static bool TryExtractBaggage(string[] baggageCollection, out IEnumerable> baggage) { int baggageLength = -1; + bool done = false; Dictionary baggageDictionary = null; foreach (var item in baggageCollection) { - if (baggageLength >= MaxBaggageLength) + if (done) { break; } @@ -122,8 +126,9 @@ internal static bool TryExtractBaggage(string[] baggageCollection, out IEnumerab { baggageLength += pair.Length + 1; // pair and comma - if (baggageLength >= MaxBaggageLength) + if (baggageLength >= MaxBaggageLength || baggageDictionary?.Count >= MaxBaggageItems) { + done = true; break; } diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs index a8787e8c1fe..e52e28dcea3 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs @@ -45,7 +45,7 @@ public class BaggageFormatTest [Fact] public void ValidateFieldsProperty() { - Assert.Equal(new HashSet { "baggage" }, this.baggage.Fields); + Assert.Equal(new HashSet { BaggageFormat.BaggageHeaderName }, this.baggage.Fields); Assert.Single(this.baggage.Fields); } @@ -77,7 +77,7 @@ public void ValidateOneBaggageExtraction() { var carrier = new Dictionary { - { "baggage", "name=test" }, + { BaggageFormat.BaggageHeaderName, "name=test" }, }; var textFormatContext = this.baggage.Extract(default, carrier, Getter); Assert.False(textFormatContext == default); @@ -94,9 +94,9 @@ public void ValidateMultipleBaggageExtraction() { var carrier = new List> { - new KeyValuePair("baggage", "name1=test1"), - new KeyValuePair("baggage", "name2=test2"), - new KeyValuePair("baggage", "name2=test2"), + new KeyValuePair(BaggageFormat.BaggageHeaderName, "name1=test1"), + new KeyValuePair(BaggageFormat.BaggageHeaderName, "name2=test2"), + new KeyValuePair(BaggageFormat.BaggageHeaderName, "name2=test2"), }; var textFormatContext = this.baggage.Extract(default, carrier, GetterList); @@ -120,7 +120,7 @@ public void ValidateLongBaggageExtraction() { var carrier = new Dictionary { - { "baggage", $"name={new string('x', 1018)},clientId=1234" }, + { BaggageFormat.BaggageHeaderName, $"name={new string('x', 8186)},clientId=1234" }, }; var textFormatContext = this.baggage.Extract(default, carrier, Getter); Assert.False(textFormatContext == default); @@ -129,7 +129,7 @@ public void ValidateLongBaggageExtraction() var array = textFormatContext.ActivityBaggage.ToArray(); Assert.Equal("name", array[0].Key); - Assert.Equal(new string('x', 1018), array[0].Value); + Assert.Equal(new string('x', 8186), array[0].Value); } [Fact] @@ -154,7 +154,7 @@ public void ValidateBaggageInjection() this.baggage.Inject(textFormatContext, carrier, Setter); Assert.Single(carrier); - Assert.Equal("key1=value1,key2=value2", carrier["baggage"]); + Assert.Equal("key1=value1,key2=value2", carrier[BaggageFormat.BaggageHeaderName]); } } } From 0a283bff4f8e42f14680027135aaafe70cd9ce00 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 18 Aug 2020 09:08:56 -0700 Subject: [PATCH 31/33] Fixed and improved ITextFormat log messages. --- .../Context/Propagation/BaggageFormat.cs | 10 ++--- .../Context/Propagation/TraceContextFormat.cs | 12 +++--- .../Internal/OpenTelemetryApiEventSource.cs | 37 +++++++++++++------ .../Context/Propagation/B3Format.cs | 14 +++---- .../Internal/OpenTelemetrySdkEventSource.cs | 16 ++++---- 5 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index 302a69cec15..a48548aabcd 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -41,13 +41,13 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Action(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Action(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Action(TextFormatContext } catch (Exception e) { - OpenTelemetrySdkEventSource.Log.ContextExtractException(e); + OpenTelemetrySdkEventSource.Log.ActivityContextExtractException(nameof(B3Format), e); return context; } } @@ -245,7 +245,7 @@ private static TextFormatContext ExtractFromSingleHeader(TextFormatContext co } catch (Exception e) { - OpenTelemetrySdkEventSource.Log.ContextExtractException(e); + OpenTelemetrySdkEventSource.Log.ActivityContextExtractException(nameof(B3Format), e); return context; } } diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index e89120f8ced..9ef0e721267 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -38,11 +38,11 @@ public void SpanProcessorException(string evnt, Exception ex) } [NonEvent] - public void ContextExtractException(Exception ex) + public void ActivityContextExtractException(string format, Exception ex) { if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1))) { - this.FailedToExtractContext(ex.ToInvariantString()); + this.FailedToExtractActivityContext(format, ex.ToInvariantString()); } } @@ -139,16 +139,16 @@ public void InvalidArgument(string methodName, string argumentName, string issue this.WriteEvent(8, methodName, argumentName, issue); } - [Event(9, Message = "Failed to extract span context: '{0}'", Level = EventLevel.Warning)] - public void FailedToExtractContext(string error) + [Event(9, Message = "Failed to extract activity context in format: '{0}', context: '{1}'.", Level = EventLevel.Warning)] + public void FailedToExtractActivityContext(string format, string error) { - this.WriteEvent(9, error); + this.WriteEvent(9, format, error); } - [Event(10, Message = "Failed to inject span context: '{0}'", Level = EventLevel.Warning)] - public void FailedToInjectContext(string error) + [Event(10, Message = "Failed to inject activity context in format: '{0}', context: '{1}'.", Level = EventLevel.Warning)] + public void FailedToInjectActivityContext(string format, string error) { - this.WriteEvent(10, error); + this.WriteEvent(10, format, error); } [Event(11, Message = "Failed to parse tracestate: too many items", Level = EventLevel.Warning)] From 3c2e9fdebda26591fedf6ac33b7241af16516425 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 18 Aug 2020 09:28:30 -0700 Subject: [PATCH 32/33] Rename TextFormatContext -> PropagationContext. --- .../Utils/Messaging/MessageSender.cs | 2 +- src/OpenTelemetry.Api/CHANGELOG.md | 4 +- .../Context/Propagation/BaggageFormat.cs | 6 +-- .../Propagation/CompositePropagator.cs | 4 +- .../Context/Propagation/ITextFormat.cs | 4 +- ...FormatContext.cs => PropagationContext.cs} | 22 +++++----- .../Context/Propagation/TraceContextFormat.cs | 6 +-- .../HttpHandlerDiagnosticListener.cs | 2 +- .../HttpWebRequestActivitySource.netfx.cs | 2 +- .../TracerShim.cs | 8 ++-- .../Context/Propagation/B3Format.cs | 12 +++--- .../HttpInListenerTests.cs | 2 +- .../BasicTests.cs | 2 +- .../HttpClientTests.Basic.netcore31.cs | 4 +- .../HttpWebRequestTests.Basic.netfx.cs | 4 +- .../Trace/Propagation/B3FormatTest.cs | 36 ++++++++-------- .../Trace/Propagation/BaggageFormatTest.cs | 42 +++++++++---------- .../Propagation/CompositePropagatorTest.cs | 30 ++++++------- .../Trace/Propagation/TestPropagator.cs | 6 +-- .../Trace/Propagation/TraceContextTest.cs | 8 ++-- 20 files changed, 103 insertions(+), 103 deletions(-) rename src/OpenTelemetry.Api/Context/Propagation/{TextFormatContext.cs => PropagationContext.cs} (76%) diff --git a/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs b/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs index 5ca2b73cbb4..d8c93e70691 100644 --- a/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs +++ b/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs @@ -61,7 +61,7 @@ public string SendMessage() if (activity != null) { // Inject the ActivityContext into the message headers to propagate trace context to the receiving service. - TextFormat.Inject(new TextFormatContext(activity.Context, activity.Baggage), props, this.InjectTraceContextIntoBasicProperties); + TextFormat.Inject(new PropagationContext(activity.Context, activity.Baggage), props, this.InjectTraceContextIntoBasicProperties); // The OpenTelemetry messaging specification defines a number of attributes. These attributes are added here. RabbitMqHelper.AddMessagingTags(activity); diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index 6a6cd52c944..1d93745268b 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,8 +2,8 @@ ## Unreleased -* `TextFormatContext` is now used instead of `ActivityContext` in the - `ITextFormat` +* `PropagationContext` is now used instead of `ActivityContext` in the + `ITextFormat` API ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) * Added `BaggageFormat` an `ITextFormat` implementation for managing Baggage propagation via the [W3C diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index a48548aabcd..34378ac8036 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -37,7 +37,7 @@ public class BaggageFormat : ITextFormat public ISet Fields => new HashSet { BaggageHeaderName }; /// - public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) + public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { if (carrier == null) { @@ -60,7 +60,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Func - public void Inject(TextFormatContext context, T carrier, Action setter) + public void Inject(PropagationContext context, T carrier, Action setter) { if (carrier == null) { diff --git a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs index 0a06f9eee9c..dda775342a6 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/CompositePropagator.cs @@ -40,7 +40,7 @@ public CompositePropagator(IEnumerable textFormats) public ISet Fields => EmptyFields; /// - public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) + public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { foreach (var textFormat in this.textFormats) { @@ -51,7 +51,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func - public void Inject(TextFormatContext context, T carrier, Action setter) + public void Inject(PropagationContext context, T carrier, Action setter) { foreach (var textFormat in this.textFormats) { diff --git a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs index af26eba7875..f79e5639b3d 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/ITextFormat.cs @@ -39,7 +39,7 @@ public interface ITextFormat /// The default context to transmit over the wire. /// Object to set context on. Instance of this object will be passed to setter. /// Action that will set name and value pair on the object. - void Inject(TextFormatContext context, T carrier, Action setter); + void Inject(PropagationContext context, T carrier, Action setter); /// /// Extracts activity context from textual representation. @@ -49,6 +49,6 @@ public interface ITextFormat /// Object to extract context from. Instance of this object will be passed to the getter. /// Function that will return string value of a key with the specified name. /// Context from it's text representation. - TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter); + PropagationContext Extract(PropagationContext context, T carrier, Func> getter); } } diff --git a/src/OpenTelemetry.Api/Context/Propagation/TextFormatContext.cs b/src/OpenTelemetry.Api/Context/Propagation/PropagationContext.cs similarity index 76% rename from src/OpenTelemetry.Api/Context/Propagation/TextFormatContext.cs rename to src/OpenTelemetry.Api/Context/Propagation/PropagationContext.cs index e3ade2caa9f..ecaeea64b93 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TextFormatContext.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/PropagationContext.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,16 +22,16 @@ namespace OpenTelemetry.Context.Propagation { /// - /// TextFormat context. + /// Stores propagation data. /// - public readonly struct TextFormatContext : IEquatable + public readonly struct PropagationContext : IEquatable { /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// Entries for activity context. /// Entries for activity baggage. - public TextFormatContext(ActivityContext activityContext, IEnumerable> activityBaggage) + public PropagationContext(ActivityContext activityContext, IEnumerable> activityBaggage) { this.ActivityContext = activityContext; this.ActivityBaggage = activityBaggage; @@ -48,21 +48,21 @@ public TextFormatContext(ActivityContext activityContext, IEnumerable> ActivityBaggage { get; } /// - /// Compare two entries of for equality. + /// Compare two entries of for equality. /// /// First Entry to compare. /// Second Entry to compare. - public static bool operator ==(TextFormatContext left, TextFormatContext right) => left.Equals(right); + public static bool operator ==(PropagationContext left, PropagationContext right) => left.Equals(right); /// - /// Compare two entries of for not equality. + /// Compare two entries of for not equality. /// /// First Entry to compare. /// Second Entry to compare. - public static bool operator !=(TextFormatContext left, TextFormatContext right) => !(left == right); + public static bool operator !=(PropagationContext left, PropagationContext right) => !(left == right); /// - public bool Equals(TextFormatContext value) + public bool Equals(PropagationContext value) { if (this.ActivityContext != value.ActivityContext || this.ActivityBaggage is null != value.ActivityBaggage is null) @@ -96,7 +96,7 @@ public bool Equals(TextFormatContext value) } /// - public override bool Equals(object obj) => (obj is TextFormatContext context) && this.Equals(context); + public override bool Equals(object obj) => (obj is PropagationContext context) && this.Equals(context); /// public override int GetHashCode() diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index e007b245b61..618947c6554 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -43,7 +43,7 @@ public class TraceContextFormat : ITextFormat public ISet Fields => new HashSet { TraceState, TraceParent }; /// - public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) + public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { if (carrier == null) { @@ -82,7 +82,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Func - public void Inject(TextFormatContext context, T carrier, Action setter) + public void Inject(PropagationContext context, T carrier, Action setter) { if (context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) { diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs index 83cf32dc7e7..c30c1ccd28d 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs @@ -107,7 +107,7 @@ public override void OnStartActivity(Activity activity, object payload) if (!(this.httpClientSupportsW3C && this.options.TextFormat is TraceContextFormat)) { - this.options.TextFormat.Inject(new TextFormatContext(activity.Context, activity.Baggage), request, HttpRequestMessageHeaderValueSetter); + this.options.TextFormat.Inject(new PropagationContext(activity.Context, activity.Baggage), request, HttpRequestMessageHeaderValueSetter); } } diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 4732ddf7c45..62e3634f07f 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -187,7 +187,7 @@ private static void AddExceptionTags(Exception exception, Activity activity) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void InstrumentRequest(HttpWebRequest request, Activity activity) - => Options.TextFormat.Inject(new TextFormatContext(activity.Context, activity.Baggage), request, HttpWebRequestHeaderValuesSetter); + => Options.TextFormat.Inject(new PropagationContext(activity.Context, activity.Baggage), request, HttpWebRequestHeaderValuesSetter); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsRequestInstrumented(HttpWebRequest request) diff --git a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs index fdd32085a83..fb232e39803 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs +++ b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs @@ -60,7 +60,7 @@ public TracerShim(Trace.Tracer tracer, ITextFormat textFormat) throw new ArgumentNullException(nameof(carrier)); } - TextFormatContext textFormatContext = default; + PropagationContext propagationContext = default; if ((format == BuiltinFormats.TextMap || format == BuiltinFormats.HttpHeaders) && carrier is ITextMap textMapCarrier) { @@ -81,10 +81,10 @@ static IEnumerable GetCarrierKeyValue(Dictionary @@ -116,7 +116,7 @@ public void Inject( if ((format == BuiltinFormats.TextMap || format == BuiltinFormats.HttpHeaders) && carrier is ITextMap textMapCarrier) { this.textFormat.Inject( - new TextFormatContext(shim.SpanContext, shim.GetBaggageItems()), + new PropagationContext(shim.SpanContext, shim.GetBaggageItems()), textMapCarrier, (instrumentation, key, value) => instrumentation.Set(key, value)); } diff --git a/src/OpenTelemetry/Context/Propagation/B3Format.cs b/src/OpenTelemetry/Context/Propagation/B3Format.cs index 11e233aa0a0..d5b835df414 100644 --- a/src/OpenTelemetry/Context/Propagation/B3Format.cs +++ b/src/OpenTelemetry/Context/Propagation/B3Format.cs @@ -70,7 +70,7 @@ public B3Format(bool singleHeader) public ISet Fields => AllFields; /// - public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) + public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { if (carrier == null) { @@ -95,7 +95,7 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func - public void Inject(TextFormatContext context, T carrier, Action setter) + public void Inject(PropagationContext context, T carrier, Action setter) { if (context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default) { @@ -140,7 +140,7 @@ public void Inject(TextFormatContext context, T carrier, Action(TextFormatContext context, T carrier, Func> getter) + private static PropagationContext ExtractFromMultipleHeaders(PropagationContext context, T carrier, Func> getter) { try { @@ -179,7 +179,7 @@ private static TextFormatContext ExtractFromMultipleHeaders(TextFormatContext traceOptions |= ActivityTraceFlags.Recorded; } - return new TextFormatContext( + return new PropagationContext( new ActivityContext(traceId, spanId, traceOptions, isRemote: true), context.ActivityBaggage); } @@ -190,7 +190,7 @@ private static TextFormatContext ExtractFromMultipleHeaders(TextFormatContext } } - private static TextFormatContext ExtractFromSingleHeader(TextFormatContext context, T carrier, Func> getter) + private static PropagationContext ExtractFromSingleHeader(PropagationContext context, T carrier, Func> getter) { try { @@ -239,7 +239,7 @@ private static TextFormatContext ExtractFromSingleHeader(TextFormatContext co } } - return new TextFormatContext( + return new PropagationContext( new ActivityContext(traceId, spanId, traceOptions, isRemote: true), context.ActivityBaggage); } diff --git a/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs b/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs index c0c77bf7537..4c70d205191 100644 --- a/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNet.Tests/HttpInListenerTests.cs @@ -127,7 +127,7 @@ public void AspNetRequestsAreCollectedSuccessfully( var expectedTraceId = ActivityTraceId.CreateRandom(); var expectedSpanId = ActivitySpanId.CreateRandom(); var textFormat = new Mock(); - textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new TextFormatContext( + textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new PropagationContext( new ActivityContext( expectedTraceId, expectedSpanId, diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 5d5f8da6f91..1d6696a54c1 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -144,7 +144,7 @@ public async Task CustomTextFormat() var expectedSpanId = ActivitySpanId.CreateRandom(); var textFormat = new Mock(); - textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new TextFormatContext( + textFormat.Setup(m => m.Extract(It.IsAny(), It.IsAny(), It.IsAny>>())).Returns(new PropagationContext( new ActivityContext( expectedTraceId, expectedSpanId, diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs index c122d7bffd3..321bea4bcd9 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.netcore31.cs @@ -121,8 +121,8 @@ public async Task HttpClientInstrumentationInjectsHeadersAsync() public async Task HttpClientInstrumentationInjectsHeadersAsync_CustomFormat() { var textFormat = new Mock(); - textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((context, message, action) => + textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) + .Callback>((context, message, action) => { action(message, "custom_traceparent", $"00/{context.ActivityContext.TraceId}/{context.ActivityContext.SpanId}/01"); action(message, "custom_tracestate", Activity.Current.TraceStateString); diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs index dbb9fc9bad4..5352ad52171 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs @@ -97,8 +97,8 @@ public async Task HttpWebRequestInstrumentationInjectsHeadersAsync() public async Task HttpWebRequestInstrumentationInjectsHeadersAsync_CustomFormat() { var textFormat = new Mock(); - textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((context, message, action) => + textFormat.Setup(m => m.Inject(It.IsAny(), It.IsAny(), It.IsAny>())) + .Callback>((context, message, action) => { action(message, "custom_traceparent", $"00/{context.ActivityContext.TraceId}/{context.ActivityContext.SpanId}/01"); action(message, "custom_tracestate", Activity.Current.TraceStateString); diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/B3FormatTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/B3FormatTest.cs index bddef81baf8..2c6c381cb48 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/B3FormatTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/B3FormatTest.cs @@ -56,7 +56,7 @@ public B3FormatTest(ITestOutputHelper output) public void Serialize_SampledContext() { var carrier = new Dictionary(); - this.b3Format.Inject(new TextFormatContext(new ActivityContext(TraceId, SpanId, TraceOptions), null), carrier, Setter); + this.b3Format.Inject(new PropagationContext(new ActivityContext(TraceId, SpanId, TraceOptions), null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "1" } }); } @@ -66,7 +66,7 @@ public void Serialize_NotSampledContext() var carrier = new Dictionary(); var context = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); this.output.WriteLine(context.ToString()); - this.b3Format.Inject(new TextFormatContext(context, null), carrier, Setter); + this.b3Format.Inject(new PropagationContext(context, null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 } }); } @@ -78,7 +78,7 @@ public void ParseMissingSampledAndMissingFlag() { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, }; var spanContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - Assert.Equal(new TextFormatContext(spanContext, null), this.b3Format.Extract(default, headersNotSampled, Getter)); + Assert.Equal(new PropagationContext(spanContext, null), this.b3Format.Extract(default, headersNotSampled, Getter)); } [Fact] @@ -89,7 +89,7 @@ public void ParseSampled() { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "1" }, }; var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersSampled, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3Format.Extract(default, headersSampled, Getter)); } [Fact] @@ -100,7 +100,7 @@ public void ParseZeroSampled() { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "0" }, }; var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersNotSampled, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3Format.Extract(default, headersNotSampled, Getter)); } [Fact] @@ -111,7 +111,7 @@ public void ParseFlag() { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Flags, "1" }, }; var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersFlagSampled, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3Format.Extract(default, headersFlagSampled, Getter)); } [Fact] @@ -122,7 +122,7 @@ public void ParseZeroFlag() { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Flags, "0" }, }; var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersFlagNotSampled, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3Format.Extract(default, headersFlagNotSampled, Getter)); } [Fact] @@ -135,7 +135,7 @@ public void ParseEightBytesTraceId() { B3Format.XB3Sampled, "1" }, }; var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, TraceOptions); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter)); } [Fact] @@ -146,7 +146,7 @@ public void ParseEightBytesTraceId_NotSampledSpanContext() { B3Format.XB3TraceId, TraceIdBase16EightBytes }, { B3Format.XB3SpanId, SpanIdBase16 }, }; var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, ActivityTraceFlags.None); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter)); } [Fact] @@ -209,7 +209,7 @@ public void Serialize_SampledContext_SingleHeader() { var carrier = new Dictionary(); var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); - this.b3FormatSingleHeader.Inject(new TextFormatContext(activityContext, null), carrier, Setter); + this.b3FormatSingleHeader.Inject(new PropagationContext(activityContext, null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-1" } }); } @@ -219,7 +219,7 @@ public void Serialize_NotSampledContext_SingleHeader() var carrier = new Dictionary(); var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); this.output.WriteLine(activityContext.ToString()); - this.b3FormatSingleHeader.Inject(new TextFormatContext(activityContext, null), carrier, Setter); + this.b3FormatSingleHeader.Inject(new PropagationContext(activityContext, null), carrier, Setter); this.ContainsExactly(carrier, new Dictionary { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}" } }); } @@ -231,7 +231,7 @@ public void ParseMissingSampledAndMissingFlag_SingleHeader() { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}" }, }; var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter)); } [Fact] @@ -243,7 +243,7 @@ public void ParseSampled_SingleHeader() }; Assert.Equal( - new TextFormatContext(new ActivityContext(TraceId, SpanId, TraceOptions), null), + new PropagationContext(new ActivityContext(TraceId, SpanId, TraceOptions), null), this.b3FormatSingleHeader.Extract(default, headersSampled, Getter)); } @@ -256,7 +256,7 @@ public void ParseZeroSampled_SingleHeader() }; Assert.Equal( - new TextFormatContext(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), null), + new PropagationContext(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), null), this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter)); } @@ -268,7 +268,7 @@ public void ParseFlag_SingleHeader() { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-1" }, }; var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagSampled, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagSampled, Getter)); } [Fact] @@ -279,7 +279,7 @@ public void ParseZeroFlag_SingleHeader() { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-0" }, }; var activityContext = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagNotSampled, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagNotSampled, Getter)); } [Fact] @@ -290,7 +290,7 @@ public void ParseEightBytesTraceId_SingleHeader() { B3Format.XB3Combined, $"{TraceIdBase16EightBytes}-{SpanIdBase16}-1" }, }; var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, TraceOptions); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); } [Fact] @@ -301,7 +301,7 @@ public void ParseEightBytesTraceId_NotSampledSpanContext_SingleHeader() { B3Format.XB3Combined, $"{TraceIdBase16EightBytes}-{SpanIdBase16}" }, }; var activityContext = new ActivityContext(TraceIdEightBytes, SpanId, ActivityTraceFlags.None); - Assert.Equal(new TextFormatContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); + Assert.Equal(new PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter)); } [Fact] diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs index e52e28dcea3..455445e4b0e 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/BaggageFormatTest.cs @@ -52,24 +52,24 @@ public void ValidateFieldsProperty() [Fact] public void ValidateDefaultCarrierExtraction() { - var textFormatContext = this.baggage.Extract(default, null, null); - Assert.Equal(default, textFormatContext); + var propagationContext = this.baggage.Extract(default, null, null); + Assert.Equal(default, propagationContext); } [Fact] public void ValidateDefaultGetterExtraction() { var carrier = new Dictionary(); - var textFormatContext = this.baggage.Extract(default, carrier, null); - Assert.Equal(default, textFormatContext); + var propagationContext = this.baggage.Extract(default, carrier, null); + Assert.Equal(default, propagationContext); } [Fact] public void ValidateNoBaggageExtraction() { var carrier = new Dictionary(); - var textFormatContext = this.baggage.Extract(default, carrier, Getter); - Assert.Equal(default, textFormatContext); + var propagationContext = this.baggage.Extract(default, carrier, Getter); + Assert.Equal(default, propagationContext); } [Fact] @@ -79,11 +79,11 @@ public void ValidateOneBaggageExtraction() { { BaggageFormat.BaggageHeaderName, "name=test" }, }; - var textFormatContext = this.baggage.Extract(default, carrier, Getter); - Assert.False(textFormatContext == default); - Assert.Single(textFormatContext.ActivityBaggage); + var propagationContext = this.baggage.Extract(default, carrier, Getter); + Assert.False(propagationContext == default); + Assert.Single(propagationContext.ActivityBaggage); - var array = textFormatContext.ActivityBaggage.ToArray(); + var array = propagationContext.ActivityBaggage.ToArray(); Assert.Equal("name", array[0].Key); Assert.Equal("test", array[0].Value); @@ -99,14 +99,14 @@ public void ValidateMultipleBaggageExtraction() new KeyValuePair(BaggageFormat.BaggageHeaderName, "name2=test2"), }; - var textFormatContext = this.baggage.Extract(default, carrier, GetterList); + var propagationContext = this.baggage.Extract(default, carrier, GetterList); - Assert.False(textFormatContext == default); - Assert.True(textFormatContext.ActivityContext == default); + Assert.False(propagationContext == default); + Assert.True(propagationContext.ActivityContext == default); - Assert.Equal(2, textFormatContext.ActivityBaggage.Count()); + Assert.Equal(2, propagationContext.ActivityBaggage.Count()); - var array = textFormatContext.ActivityBaggage.ToArray(); + var array = propagationContext.ActivityBaggage.ToArray(); Assert.Equal("name1", array[0].Key); Assert.Equal("test1", array[0].Value); @@ -122,11 +122,11 @@ public void ValidateLongBaggageExtraction() { { BaggageFormat.BaggageHeaderName, $"name={new string('x', 8186)},clientId=1234" }, }; - var textFormatContext = this.baggage.Extract(default, carrier, Getter); - Assert.False(textFormatContext == default); - Assert.Single(textFormatContext.ActivityBaggage); + var propagationContext = this.baggage.Extract(default, carrier, Getter); + Assert.False(propagationContext == default); + Assert.Single(propagationContext.ActivityBaggage); - var array = textFormatContext.ActivityBaggage.ToArray(); + var array = propagationContext.ActivityBaggage.ToArray(); Assert.Equal("name", array[0].Key); Assert.Equal(new string('x', 8186), array[0].Value); @@ -145,13 +145,13 @@ public void ValidateEmptyBaggageInjection() public void ValidateBaggageInjection() { var carrier = new Dictionary(); - var textFormatContext = new TextFormatContext(default, new Dictionary + var propagationContext = new PropagationContext(default, new Dictionary { { "key1", "value1" }, { "key2", "value2" }, }); - this.baggage.Inject(textFormatContext, carrier, Setter); + this.baggage.Inject(propagationContext, carrier, Setter); Assert.Single(carrier); Assert.Equal("key1=value1,key2=value2", carrier[BaggageFormat.BaggageHeaderName]); diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs index 4efb9941a34..251a79b6549 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/CompositePropagatorTest.cs @@ -62,11 +62,11 @@ public void CompositePropagator_TestPropagator() }); var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null); - TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); + PropagationContext propagationContext = new PropagationContext(activityContext, null); var carrier = new Dictionary(); var activity = new Activity("test"); - compositePropagator.Inject(textFormatContext, carrier, Setter); + compositePropagator.Inject(propagationContext, carrier, Setter); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent-1"); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent-2"); } @@ -84,11 +84,11 @@ public void CompositePropagator_UsingSameTag() }); var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null); - TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); + PropagationContext propagationContext = new PropagationContext(activityContext, null); var carrier = new Dictionary(); - compositePropagator.Inject(textFormatContext, carrier, Setter); + compositePropagator.Inject(propagationContext, carrier, Setter); Assert.Contains(carrier, kv => kv.Key == "custom-traceparent"); // checking if the latest propagator is the one with the data. So, it will replace the previous one. @@ -96,7 +96,7 @@ public void CompositePropagator_UsingSameTag() // resetting counter count = 0; - TextFormatContext newTextFormatContext = compositePropagator.Extract(default, carrier, Getter); + compositePropagator.Extract(default, carrier, Getter); // checking if we accessed only two times: header/headerstate options // if that's true, we skipped the first one since we have a logic to for the default result @@ -115,24 +115,24 @@ public void CompositePropagator_ActivityContext_Baggage() var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null, isRemote: true); var baggage = new Dictionary { ["key1"] = "value1" }; - TextFormatContext textFormatContextActivityOnly = new TextFormatContext(activityContext, null); - TextFormatContext textFormatContextBaggageOnly = new TextFormatContext(default, baggage); - TextFormatContext textFormatContextBoth = new TextFormatContext(activityContext, baggage); + PropagationContext propagationContextActivityOnly = new PropagationContext(activityContext, null); + PropagationContext propagationContextBaggageOnly = new PropagationContext(default, baggage); + PropagationContext propagationContextBoth = new PropagationContext(activityContext, baggage); var carrier = new Dictionary(); - compositePropagator.Inject(textFormatContextActivityOnly, carrier, Setter); - TextFormatContext extractedContext = compositePropagator.Extract(default, carrier, Getter); - Assert.Equal(textFormatContextActivityOnly, extractedContext); + compositePropagator.Inject(propagationContextActivityOnly, carrier, Setter); + PropagationContext extractedContext = compositePropagator.Extract(default, carrier, Getter); + Assert.Equal(propagationContextActivityOnly, extractedContext); carrier = new Dictionary(); - compositePropagator.Inject(textFormatContextBaggageOnly, carrier, Setter); + compositePropagator.Inject(propagationContextBaggageOnly, carrier, Setter); extractedContext = compositePropagator.Extract(default, carrier, Getter); - Assert.Equal(textFormatContextBaggageOnly, extractedContext); + Assert.Equal(propagationContextBaggageOnly, extractedContext); carrier = new Dictionary(); - compositePropagator.Inject(textFormatContextBoth, carrier, Setter); + compositePropagator.Inject(propagationContextBoth, carrier, Setter); extractedContext = compositePropagator.Extract(default, carrier, Getter); - Assert.Equal(textFormatContextBoth, extractedContext); + Assert.Equal(propagationContextBoth, extractedContext); } } } diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/TestPropagator.cs b/test/OpenTelemetry.Tests/Trace/Propagation/TestPropagator.cs index 702391d94e0..a52ffbea1ee 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/TestPropagator.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/TestPropagator.cs @@ -36,7 +36,7 @@ public TestPropagator(string idHeaderName, string stateHeaderName, bool defaultC public ISet Fields => new HashSet() { this.idHeaderName, this.stateHeaderName }; - public TextFormatContext Extract(TextFormatContext context, T carrier, Func> getter) + public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { if (this.defaultContext) { @@ -62,12 +62,12 @@ public TextFormatContext Extract(TextFormatContext context, T carrier, Func(TextFormatContext context, T carrier, Action setter) + public void Inject(PropagationContext context, T carrier, Action setter) { string headerNumber = this.stateHeaderName.Split('-').Last(); diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs index 47b9adde249..0b9f1e522d6 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextTest.cs @@ -149,10 +149,10 @@ public void TraceContextFormat_Inject_NoTracestate() }; var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, traceState: null); - TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); + PropagationContext propagationContext = new PropagationContext(activityContext, null); var carrier = new Dictionary(); var f = new TraceContextFormat(); - f.Inject(textFormatContext, carrier, Setter); + f.Inject(propagationContext, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } @@ -169,10 +169,10 @@ public void TraceContextFormat_Inject_WithTracestate() }; var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, expectedHeaders[TraceState]); - TextFormatContext textFormatContext = new TextFormatContext(activityContext, null); + PropagationContext propagationContext = new PropagationContext(activityContext, null); var carrier = new Dictionary(); var f = new TraceContextFormat(); - f.Inject(textFormatContext, carrier, Setter); + f.Inject(propagationContext, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } From 07b8b468fe995341c46069b240c347d52f484a09 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 18 Aug 2020 10:03:31 -0700 Subject: [PATCH 33/33] Updated ITextFormat implementations so they don't double-extract. --- src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs | 6 ++++++ .../Context/Propagation/TraceContextFormat.cs | 6 ++++++ src/OpenTelemetry/Context/Propagation/B3Format.cs | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs index 34378ac8036..7fa8afd45da 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/BaggageFormat.cs @@ -39,6 +39,12 @@ public class BaggageFormat : ITextFormat /// public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { + if (context.ActivityBaggage != null) + { + // If baggage has already been extracted, perform a noop. + return context; + } + if (carrier == null) { OpenTelemetryApiEventSource.Log.FailedToExtractBaggage(nameof(BaggageFormat), "null carrier"); diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs index 618947c6554..971b4bb4d1f 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextFormat.cs @@ -45,6 +45,12 @@ public class TraceContextFormat : ITextFormat /// public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { + if (context.ActivityContext.IsValid()) + { + // If a valid context has already been extracted, perform a noop. + return context; + } + if (carrier == null) { OpenTelemetryApiEventSource.Log.FailedToExtractActivityContext(nameof(TraceContextFormat), "null carrier"); diff --git a/src/OpenTelemetry/Context/Propagation/B3Format.cs b/src/OpenTelemetry/Context/Propagation/B3Format.cs index d5b835df414..217bbee794a 100644 --- a/src/OpenTelemetry/Context/Propagation/B3Format.cs +++ b/src/OpenTelemetry/Context/Propagation/B3Format.cs @@ -72,6 +72,12 @@ public B3Format(bool singleHeader) /// public PropagationContext Extract(PropagationContext context, T carrier, Func> getter) { + if (context.ActivityContext.IsValid()) + { + // If a valid context has already been extracted, perform a noop. + return context; + } + if (carrier == null) { OpenTelemetrySdkEventSource.Log.FailedToExtractActivityContext(nameof(B3Format), "null carrier");