Baggage + CorrelationContext improvements by Eddy & Mike (#1048)
* Support W3C Baggage spec. * Moved baggage propagation to its own ITextFormat. Removed IsInjected. * updating some tests * creating nw files * updating files * buildable in release * adding baggage tests * updating tests * updating default textformat for http instrumentation * Removed a few null checks. * Removed DistributedContext. Drive CorrelationContext off of Activity.Baggage. * updating issues after merge * updating based on sanity check * updating baggage test * updating tests * reiley's comments * move to using * Updates for http-in and http-out. Updated CHANGELOGs. * Adding tests. * updating correlation context * Added test for TraceContextFormat + BaggageFormat used together. * Fixed broken tests. * Code review. * Test fixup. * updating order * updating tests * updating tests, adding dispose, clearing objects * updating changelog * Use "Baggage" instead of "baggage" as the header name. * Added some basic support for the Baggage limits specified in the spec. * Fixed and improved ITextFormat log messages. * Rename TextFormatContext -> PropagationContext. * Updated ITextFormat implementations so they don't double-extract. Co-authored-by: Eddy Nakamura <ednakamu@microsoft.com> Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
61d9cc2fcc
commit
dcaea5bd45
|
|
@ -63,7 +63,7 @@ namespace Utils.Messaging
|
|||
// 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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace Utils.Messaging
|
|||
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 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);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,16 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
* `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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
|
|
@ -25,27 +26,33 @@ namespace OpenTelemetry.Context
|
|||
/// </summary>
|
||||
public readonly struct CorrelationContext : IEquatable<CorrelationContext>
|
||||
{
|
||||
private static readonly List<CorrelationContextEntry> EmptyList = new List<CorrelationContextEntry>();
|
||||
private readonly List<CorrelationContextEntry> entries;
|
||||
internal static readonly CorrelationContext Empty = new CorrelationContext(null);
|
||||
internal static readonly IEnumerable<KeyValuePair<string, string>> EmptyBaggage = new KeyValuePair<string, string>[0];
|
||||
private readonly Activity activity;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CorrelationContext"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="entries">Entries for correlation context.</param>
|
||||
internal CorrelationContext(List<CorrelationContextEntry> entries)
|
||||
internal CorrelationContext(in Activity activity)
|
||||
{
|
||||
this.entries = entries;
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets empty object of <see cref="CorrelationContext"/> struct.
|
||||
/// Gets the current <see cref="CorrelationContext"/>.
|
||||
/// </summary>
|
||||
public static CorrelationContext Empty { get; } = new CorrelationContext(EmptyList);
|
||||
public static CorrelationContext Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Activity activity = Activity.Current;
|
||||
return activity == null
|
||||
? Empty
|
||||
: new CorrelationContext(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the <see cref="CorrelationContextEntry"/> in this <see cref="CorrelationContext"/>.
|
||||
/// Gets the correlation values.
|
||||
/// </summary>
|
||||
public IEnumerable<CorrelationContextEntry> Entries => this.entries;
|
||||
public IEnumerable<KeyValuePair<string, string>> Correlations => this.activity?.Baggage ?? EmptyBaggage;
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="CorrelationContext"/> for equality.
|
||||
|
|
@ -62,23 +69,62 @@ namespace OpenTelemetry.Context
|
|||
public static bool operator !=(CorrelationContext left, CorrelationContext right) => !(left == right);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CorrelationContextEntry"/> with the specified name.
|
||||
/// Retrieves a correlation item.
|
||||
/// </summary>
|
||||
/// <param name="key">Name of the <see cref="CorrelationContextEntry"/> to get.</param>
|
||||
/// <returns>The <see cref="string"/> with the specified name. If not found - null.</returns>
|
||||
public string GetEntryValue(string key) => this.entries.LastOrDefault(x => x.Key == key).Value;
|
||||
/// <param name="key">Correlation item key.</param>
|
||||
/// <returns>Retrieved correlation value or <see langword="null"/> if no match was found.</returns>
|
||||
public string GetCorrelation(string key)
|
||||
=> this.activity?.GetBaggageItem(key);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a correlation item.
|
||||
/// </summary>
|
||||
/// <param name="key">Correlation item key.</param>
|
||||
/// <param name="value">Correlation item value.</param>
|
||||
/// <returns>The <see cref="CorrelationContext"/> instance for chaining.</returns>
|
||||
public CorrelationContext AddCorrelation(string key, string value)
|
||||
{
|
||||
this.activity?.AddBaggage(key, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds correlation items.
|
||||
/// </summary>
|
||||
/// <param name="correlations">Correlation items.</param>
|
||||
/// <returns>The <see cref="CorrelationContext"/> instance for chaining.</returns>
|
||||
public CorrelationContext AddCorrelation(IEnumerable<KeyValuePair<string, string>> correlations)
|
||||
{
|
||||
if (correlations != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> correlation in correlations)
|
||||
{
|
||||
this.activity?.AddBaggage(correlation.Key, correlation.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
|
|
@ -96,7 +142,7 @@ namespace OpenTelemetry.Context
|
|||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.entries.GetHashCode();
|
||||
return this.Correlations.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,238 +0,0 @@
|
|||
// <copyright file="CorrelationContextBuilder.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Correlation context Builder.
|
||||
/// </summary>
|
||||
public struct CorrelationContextBuilder : System.IEquatable<CorrelationContextBuilder>
|
||||
{
|
||||
private List<CorrelationContextEntry> entries;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CorrelationContextBuilder"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="inheritCurrentContext">Flag to allow inheriting the current context entries.</param>
|
||||
public CorrelationContextBuilder(bool inheritCurrentContext)
|
||||
{
|
||||
this.entries = null;
|
||||
|
||||
if (DistributedContext.Carrier is NoopDistributedContextCarrier)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (inheritCurrentContext)
|
||||
{
|
||||
this.entries = new List<CorrelationContextEntry>(DistributedContext.Current.CorrelationContext.Entries);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CorrelationContextBuilder"/> struct using some context.
|
||||
/// </summary>
|
||||
/// <param name="context">Initial context.</param>
|
||||
public CorrelationContextBuilder(CorrelationContext context)
|
||||
{
|
||||
if (DistributedContext.Carrier is NoopDistributedContextCarrier)
|
||||
{
|
||||
this.entries = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.entries = new List<CorrelationContextEntry>(context.Entries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="CorrelationContextBuilder"/> for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">First Entry to compare.</param>
|
||||
/// <param name="right">Second Entry to compare.</param>
|
||||
public static bool operator ==(CorrelationContextBuilder left, CorrelationContextBuilder right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="CorrelationContextBuilder"/> for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">First Entry to compare.</param>
|
||||
/// <param name="right">Second Entry to compare.</param>
|
||||
public static bool operator !=(CorrelationContextBuilder left, CorrelationContextBuilder right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="CorrelationContext"/> instance from key and value entry.
|
||||
/// </summary>
|
||||
/// <param name="key">Entry key.</param>
|
||||
/// <param name="value">Entry value.</param>
|
||||
/// <returns>Instance of <see cref="CorrelationContext"/>.</returns>
|
||||
public static CorrelationContext CreateContext(string key, string value) =>
|
||||
new CorrelationContextBuilder(inheritCurrentContext: false).Add(key, value).Build();
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="CorrelationContext"/> instance from entry.
|
||||
/// </summary>
|
||||
/// <param name="entry">Entry to add to the context.</param>
|
||||
/// <returns>Instance of <see cref="CorrelationContext"/>.</returns>
|
||||
public static CorrelationContext CreateContext(CorrelationContextEntry entry) =>
|
||||
new CorrelationContextBuilder(inheritCurrentContext: false).Add(entry).Build();
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="CorrelationContext"/> instance from entry.
|
||||
/// </summary>
|
||||
/// <param name="entries">List of entries to add to the context.</param>
|
||||
/// <returns>Instance of <see cref="CorrelationContext"/>.</returns>
|
||||
public static CorrelationContext CreateContext(IEnumerable<CorrelationContextEntry> entries) =>
|
||||
new CorrelationContextBuilder(inheritCurrentContext: false).Add(entries).Build();
|
||||
|
||||
/// <summary>
|
||||
/// Add Distributed Context entry to the builder.
|
||||
/// </summary>
|
||||
/// <param name="entry">Entry to add to the context.</param>
|
||||
/// <returns>The current <see cref="CorrelationContextBuilder"/> instance.</returns>
|
||||
public CorrelationContextBuilder Add(CorrelationContextEntry entry)
|
||||
{
|
||||
if (DistributedContext.Carrier is NoopDistributedContextCarrier || entry == default)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.entries == null)
|
||||
{
|
||||
this.entries = new List<CorrelationContextEntry>();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Distributed Context entry to the builder.
|
||||
/// </summary>
|
||||
/// <param name="key">Entry key.</param>
|
||||
/// <param name="value">Entry value.</param>
|
||||
/// <param name="metadata">Entry metadata.</param>
|
||||
/// <returns>The current <see cref="CorrelationContextBuilder"/> instance.</returns>
|
||||
public CorrelationContextBuilder Add(string key, string value, EntryMetadata metadata)
|
||||
{
|
||||
return this.Add(new CorrelationContextEntry(key, value, metadata));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Distributed Context entry to the builder.
|
||||
/// </summary>
|
||||
/// <param name="key">Entry key.</param>
|
||||
/// <param name="value">Entry value.</param>
|
||||
/// <returns>The current <see cref="CorrelationContextBuilder"/> instance.</returns>
|
||||
public CorrelationContextBuilder Add(string key, string value)
|
||||
{
|
||||
return this.Add(new CorrelationContextEntry(key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Distributed Context entry to the builder.
|
||||
/// </summary>
|
||||
/// <param name="entries">List of entries to add to the context.</param>
|
||||
/// <returns>The current <see cref="CorrelationContextBuilder"/> instance.</returns>
|
||||
public CorrelationContextBuilder Add(IEnumerable<CorrelationContextEntry> entries)
|
||||
{
|
||||
if (DistributedContext.Carrier is NoopDistributedContextCarrier || entries == null)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
this.Add(entry);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove Distributed Context entry from the context.
|
||||
/// </summary>
|
||||
/// <param name="key">Entry key.</param>
|
||||
/// <returns>The current <see cref="CorrelationContextBuilder"/> instance.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a Correlation Context from current builder.
|
||||
/// </summary>
|
||||
/// <returns><see cref="CorrelationContext"/> instance.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is CorrelationContextBuilder builder &&
|
||||
EqualityComparer<List<CorrelationContextEntry>>.Default.Equals(this.entries, builder.entries);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.entries.GetHashCode();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(CorrelationContextBuilder other)
|
||||
{
|
||||
return EqualityComparer<List<CorrelationContextEntry>>.Default.Equals(this.entries, other.entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
// <copyright file="CorrelationContextEntry.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using OpenTelemetry.Internal;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Distributed Context entry with the key, value and metadata.
|
||||
/// </summary>
|
||||
public readonly struct CorrelationContextEntry : System.IEquatable<CorrelationContextEntry>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CorrelationContextEntry"/> struct with the key and value.
|
||||
/// </summary>
|
||||
/// <param name="key">Key name for the entry.</param>
|
||||
/// <param name="value">Value associated with the key name.</param>
|
||||
public CorrelationContextEntry(string key, string value)
|
||||
: this(key, value, EntryMetadata.NoPropagationEntry)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CorrelationContextEntry"/> struct with the key, value, and metadata.
|
||||
/// </summary>
|
||||
/// <param name="key">Key name for the entry.</param>
|
||||
/// <param name="value">Value associated with the key name.</param>
|
||||
/// <param name="metadata">Entry metadata.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag key.
|
||||
/// </summary>
|
||||
public string Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag value.
|
||||
/// </summary>
|
||||
public string Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata associated with this entry.
|
||||
/// </summary>
|
||||
public EntryMetadata Metadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="CorrelationContextEntry"/> for equality.
|
||||
/// </summary>
|
||||
/// <param name="entry1">First Entry to compare.</param>
|
||||
/// <param name="entry2">Second Entry to compare.</param>
|
||||
public static bool operator ==(CorrelationContextEntry entry1, CorrelationContextEntry entry2) => entry1.Equals(entry2);
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="CorrelationContextEntry"/> for not equality.
|
||||
/// </summary>
|
||||
/// <param name="entry1">First Entry to compare.</param>
|
||||
/// <param name="entry2">Second Entry to compare.</param>
|
||||
public static bool operator !=(CorrelationContextEntry entry1, CorrelationContextEntry entry2) => !entry1.Equals(entry2);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is CorrelationContextEntry that && this.Key == that.Key && this.Value == that.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(CorrelationContextEntry)}{{{nameof(this.Key)}={this.Key}, {nameof(this.Value)}={this.Value}}}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var h = 1;
|
||||
h *= 1000003;
|
||||
h ^= this.Key.GetHashCode();
|
||||
h *= 1000003;
|
||||
h ^= this.Value.GetHashCode();
|
||||
return h;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(CorrelationContextEntry other)
|
||||
{
|
||||
return this.Key == other.Key && this.Value == other.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
// <copyright file="DistributedContext.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using OpenTelemetry.Internal;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Distributed context.
|
||||
/// </summary>
|
||||
public readonly struct DistributedContext : IEquatable<DistributedContext>
|
||||
{
|
||||
private static DistributedContextCarrier carrier = NoopDistributedContextCarrier.Instance;
|
||||
private readonly CorrelationContext correlationContext;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DistributedContext"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="correlationContext">The correlation context.</param>
|
||||
internal DistributedContext(CorrelationContext correlationContext)
|
||||
{
|
||||
this.correlationContext = correlationContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets empty object of <see cref="DistributedContext"/> struct.
|
||||
/// </summary>
|
||||
public static DistributedContext Empty { get; } = new DistributedContext(CorrelationContext.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="CorrelationContext"/>.
|
||||
/// </summary>
|
||||
public static DistributedContext Current => carrier.Current;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default carrier instance of the <see cref="DistributedContextCarrier"/> class.
|
||||
/// SDK will need to override the value to AsyncLocalDistributedContextCarrier.Instance.
|
||||
/// </summary>
|
||||
public static DistributedContextCarrier Carrier
|
||||
{
|
||||
get => carrier;
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.InvalidArgument("set_Carrier", nameof(value), "is null");
|
||||
}
|
||||
|
||||
carrier = value ?? NoopDistributedContextCarrier.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CorrelationContext"/> for the current distributed context.
|
||||
/// </summary>
|
||||
public CorrelationContext CorrelationContext => this.correlationContext;
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="DistributedContext"/> for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">First Entry to compare.</param>
|
||||
/// <param name="right">Second Entry to compare.</param>
|
||||
public static bool operator ==(DistributedContext left, DistributedContext right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="DistributedContext"/> for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">First Entry to compare.</param>
|
||||
/// <param name="right">Second Entry to compare.</param>
|
||||
public static bool operator !=(DistributedContext left, DistributedContext right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current <see cref="DistributedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">Context to set as current.</param>
|
||||
/// <returns>Scope object. On disposal - original context will be restored.</returns>
|
||||
public static IDisposable SetCurrent(in DistributedContext context) => carrier.SetCurrent(context);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(DistributedContext other)
|
||||
{
|
||||
return this.CorrelationContext.Equals(other.CorrelationContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is DistributedContext context && this.Equals(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.correlationContext.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
// <copyright file="DistributedContextBuilder.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Distributed context Builder.
|
||||
/// </summary>
|
||||
public struct DistributedContextBuilder
|
||||
{
|
||||
private CorrelationContextBuilder correlationContextBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DistributedContextBuilder"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="inheritCurrentContext">Flag to allow inheriting the current context entries.</param>
|
||||
public DistributedContextBuilder(bool inheritCurrentContext)
|
||||
{
|
||||
this.correlationContextBuilder = new CorrelationContextBuilder(false);
|
||||
|
||||
if (DistributedContext.Carrier is NoopDistributedContextCarrier)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (inheritCurrentContext)
|
||||
{
|
||||
this.correlationContextBuilder.Add(DistributedContext.Current.CorrelationContext.Entries);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create context.
|
||||
/// </summary>
|
||||
/// <param name="key">The correlation key.</param>
|
||||
/// <param name="value">The correlation value.</param>
|
||||
/// <returns>A <see cref="DistributedContext"/> instance.</returns>
|
||||
public static DistributedContext CreateContext(string key, string value) =>
|
||||
new DistributedContext(new CorrelationContextBuilder(inheritCurrentContext: false).Add(key, value).Build());
|
||||
|
||||
/// <summary>
|
||||
/// Create context.
|
||||
/// </summary>
|
||||
/// <param name="entries">A list of correlations to create the context with.</param>
|
||||
/// <returns>A <see cref="DistributedContext"/> instance.</returns>
|
||||
public static DistributedContext CreateContext(IEnumerable<CorrelationContextEntry> entries) =>
|
||||
new DistributedContext(new CorrelationContextBuilder(inheritCurrentContext: false).Add(entries).Build());
|
||||
|
||||
/// <summary>
|
||||
/// Configures correlations to be used with the context.
|
||||
/// </summary>
|
||||
/// <param name="configureCorrelations">An <see cref="Action{CorrelationContextBuilder}"/> used to configure correlations.</param>
|
||||
/// <returns>The current <see cref="DistributedContextBuilder"/> instance.</returns>
|
||||
public DistributedContextBuilder Correlations(Action<CorrelationContextBuilder> configureCorrelations)
|
||||
{
|
||||
configureCorrelations?.Invoke(this.correlationContextBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a Distributed Context from current builder.
|
||||
/// </summary>
|
||||
/// <returns><see cref="DistributedContext"/> instance.</returns>
|
||||
public DistributedContext Build()
|
||||
{
|
||||
if (DistributedContext.Carrier is NoopDistributedContextCarrier)
|
||||
{
|
||||
return DistributedContext.Empty;
|
||||
}
|
||||
|
||||
return new DistributedContext(this.correlationContextBuilder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// <copyright file="DistributedContextCarrier.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstraction to the carrier of the DistributedContext.Current object.
|
||||
/// </summary>
|
||||
public abstract class DistributedContextCarrier
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="DistributedContext"/>.
|
||||
/// </summary>
|
||||
public abstract DistributedContext Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current <see cref="DistributedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">Context to set as current.</param>
|
||||
/// <returns>Scope object. On disposal - original context will be restored.</returns>
|
||||
public abstract IDisposable SetCurrent(in DistributedContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
// <copyright file="NoopDistributedContextCarrier.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// No-op Distributed context carrier.
|
||||
/// </summary>
|
||||
public class NoopDistributedContextCarrier : DistributedContextCarrier
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NoopDistributedContextCarrier"/> class.
|
||||
/// </summary>
|
||||
private NoopDistributedContextCarrier()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of <see cref="NoopDistributedContextCarrier"/>.
|
||||
/// </summary>
|
||||
public static NoopDistributedContextCarrier Instance { get; } = new NoopDistributedContextCarrier();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="DistributedContext"/>.
|
||||
/// </summary>
|
||||
public override DistributedContext Current => DistributedContext.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current <see cref="DistributedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">Context to set as current.</param>
|
||||
/// <returns>Scope object. On disposal - original context will be restored.</returns>
|
||||
public override IDisposable SetCurrent(in DistributedContext context) => EmptyDisposable.Instance;
|
||||
|
||||
private class EmptyDisposable : IDisposable
|
||||
{
|
||||
public static EmptyDisposable Instance { get; } = new EmptyDisposable();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
// <copyright file="BaggageFormat.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using OpenTelemetry.Internal;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
{
|
||||
/// <summary>
|
||||
/// W3C baggage: https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md.
|
||||
/// </summary>
|
||||
public class BaggageFormat : ITextFormat
|
||||
{
|
||||
internal const string BaggageHeaderName = "Baggage";
|
||||
|
||||
private const int MaxBaggageLength = 8192;
|
||||
private const int MaxBaggageItems = 180;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISet<string> Fields => new HashSet<string> { BaggageHeaderName };
|
||||
|
||||
/// <inheritdoc/>
|
||||
public PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> 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");
|
||||
return context;
|
||||
}
|
||||
|
||||
if (getter == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToExtractBaggage(nameof(BaggageFormat), "null getter");
|
||||
return context;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IEnumerable<KeyValuePair<string, string>> baggage = null;
|
||||
var baggageCollection = getter(carrier, BaggageHeaderName);
|
||||
if (baggageCollection?.Any() ?? false)
|
||||
{
|
||||
TryExtractBaggage(baggageCollection.ToArray(), out baggage);
|
||||
}
|
||||
|
||||
return new PropagationContext(
|
||||
context.ActivityContext,
|
||||
baggage ?? context.ActivityBaggage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.BaggageExtractException(nameof(BaggageFormat), ex);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter)
|
||||
{
|
||||
if (carrier == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectBaggage(nameof(BaggageFormat), "null carrier");
|
||||
return;
|
||||
}
|
||||
|
||||
if (setter == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectBaggage(nameof(BaggageFormat), "null setter");
|
||||
return;
|
||||
}
|
||||
|
||||
using IEnumerator<KeyValuePair<string, string>> e = context.ActivityBaggage?.GetEnumerator();
|
||||
|
||||
if (e?.MoveNext() == true)
|
||||
{
|
||||
int itemCount = 1;
|
||||
StringBuilder baggage = new StringBuilder();
|
||||
do
|
||||
{
|
||||
KeyValuePair<string, string> item = e.Current;
|
||||
baggage.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(',');
|
||||
}
|
||||
while (e.MoveNext() && itemCount++ < MaxBaggageItems && baggage.Length < MaxBaggageLength);
|
||||
baggage.Remove(baggage.Length - 1, 1);
|
||||
setter(carrier, BaggageHeaderName, baggage.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryExtractBaggage(string[] baggageCollection, out IEnumerable<KeyValuePair<string, string>> baggage)
|
||||
{
|
||||
int baggageLength = -1;
|
||||
bool done = false;
|
||||
Dictionary<string, string> baggageDictionary = null;
|
||||
|
||||
foreach (var item in baggageCollection)
|
||||
{
|
||||
if (done)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var pair in item.Split(','))
|
||||
{
|
||||
baggageLength += pair.Length + 1; // pair and comma
|
||||
|
||||
if (baggageLength >= MaxBaggageLength || baggageDictionary?.Count >= MaxBaggageItems)
|
||||
{
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem))
|
||||
{
|
||||
if (baggageDictionary == null)
|
||||
{
|
||||
baggageDictionary = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
baggageDictionary[baggageItem.Name] = baggageItem.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baggage = baggageDictionary;
|
||||
return baggageDictionary != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// <copyright file="BaseHeaderParser.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
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<T> : HttpHeaderParser<T>
|
||||
{
|
||||
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 this.SupportsMultipleValues;
|
||||
}
|
||||
|
||||
var separatorFound = false;
|
||||
var current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, index, this.SupportsMultipleValues, out separatorFound);
|
||||
|
||||
if (separatorFound && !this.SupportsMultipleValues)
|
||||
{
|
||||
return false; // leading separators not allowed if we don't support multiple values.
|
||||
}
|
||||
|
||||
if (current == value.Length)
|
||||
{
|
||||
if (this.SupportsMultipleValues)
|
||||
{
|
||||
index = current;
|
||||
}
|
||||
|
||||
return this.SupportsMultipleValues;
|
||||
}
|
||||
|
||||
T result;
|
||||
var length = this.GetParsedValueLength(value, current, out result);
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
current = current + length;
|
||||
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 && !this.SupportsMultipleValues) || (!separatorFound && (current < value.Length)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
index = current;
|
||||
parsedValue = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract int GetParsedValueLength(string value, int startIndex, out T parsedValue);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
{
|
||||
|
|
@ -33,42 +31,32 @@ namespace OpenTelemetry.Context.Propagation
|
|||
/// Initializes a new instance of the <see cref="CompositePropagator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="textFormats">List of <see cref="ITextFormat"/> wire context propagator.</param>
|
||||
public CompositePropagator(List<ITextFormat> textFormats)
|
||||
public CompositePropagator(IEnumerable<ITextFormat> textFormats)
|
||||
{
|
||||
this.textFormats = textFormats ?? throw new ArgumentNullException(nameof(textFormats));
|
||||
this.textFormats = new List<ITextFormat>(textFormats ?? throw new ArgumentNullException(nameof(textFormats)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISet<string> Fields => EmptyFields;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ActivityContext Extract<T>(ActivityContext activityContext, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
public PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
foreach (var textFormat in this.textFormats)
|
||||
{
|
||||
activityContext = textFormat.Extract(activityContext, carrier, getter);
|
||||
if (activityContext.IsValid())
|
||||
{
|
||||
return activityContext;
|
||||
}
|
||||
context = textFormat.Extract(context, carrier, getter);
|
||||
}
|
||||
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Inject<T>(ActivityContext activityContext, T carrier, Action<T, string, string> setter)
|
||||
public void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter)
|
||||
{
|
||||
foreach (var textFormat in this.textFormats)
|
||||
{
|
||||
textFormat.Inject(activityContext, carrier, setter);
|
||||
textFormat.Inject(context, carrier, setter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInjected<T>(T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
return this.textFormats.All(textFormat => textFormat.IsInjected(carrier, getter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
// <copyright file="EntryPropagationFilter.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter defining propagation rules for <see cref="CorrelationContextEntry"/>.
|
||||
/// </summary>
|
||||
public readonly struct EntryPropagationFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EntryPropagationFilter"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="op">Operator to apply.</param>
|
||||
/// <param name="matchString">String to apply the operator with.</param>
|
||||
/// <param name="action">Action to execute on entry.</param>
|
||||
public EntryPropagationFilter(FilterMatchOperator op, string matchString, Action<CorrelationContextEntry> action)
|
||||
{
|
||||
this.Operator = op;
|
||||
this.MatchString = matchString;
|
||||
this.Action = action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator to use in a filter.
|
||||
/// </summary>
|
||||
public enum FilterMatchOperator
|
||||
{
|
||||
/// <summary>
|
||||
/// Equals operator.
|
||||
/// </summary>
|
||||
Equal,
|
||||
|
||||
/// <summary>
|
||||
/// Not equals operator.
|
||||
/// </summary>
|
||||
NotEqual,
|
||||
|
||||
/// <summary>
|
||||
/// Operator checking the prefix.
|
||||
/// </summary>
|
||||
HasPrefix,
|
||||
}
|
||||
|
||||
internal FilterMatchOperator Operator { get; }
|
||||
|
||||
internal string MatchString { get; }
|
||||
|
||||
internal Action<CorrelationContextEntry> Action { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Check whether <see cref="CorrelationContextEntry"/> matches this filter pattern.
|
||||
/// </summary>
|
||||
/// <param name="entry">Distributed Context entry to check.</param>
|
||||
/// <returns>True if <see cref="CorrelationContextEntry"/> matches this filter, false - otherwise.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// <copyright file="GenericHeaderParser.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
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<T> : BaseHeaderParser<T>
|
||||
{
|
||||
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 this.getParsedValueLength(value, startIndex, out parsedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// <copyright file="HeaderUtilities.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// <copyright file="HttpHeaderParser.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
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<T>
|
||||
{
|
||||
private bool supportsMultipleValues;
|
||||
|
||||
protected HttpHeaderParser(bool supportsMultipleValues)
|
||||
{
|
||||
this.supportsMultipleValues = supportsMultipleValues;
|
||||
}
|
||||
|
||||
public bool SupportsMultipleValues
|
||||
{
|
||||
get { return this.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// <copyright file="DistributedContextBinarySerializer.cs" company="OpenTelemetry Authors">
|
||||
// <copyright file="HttpParseResult.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
|
@ -16,22 +16,22 @@
|
|||
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
{
|
||||
internal sealed class DistributedContextBinarySerializer : DistributedContextBinarySerializerBase
|
||||
// Adoptation of code from https://github.com/aspnet/HttpAbstractions/blob/07d115400e4f8c7a66ba239f230805f03a14ee3d/src/Microsoft.Net.Http.Headers/HttpParseResult.cs
|
||||
internal enum HttpParseResult
|
||||
{
|
||||
internal DistributedContextBinarySerializer()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Parsed successfully.
|
||||
/// </summary>
|
||||
Parsed,
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] ToByteArray(DistributedContext context)
|
||||
{
|
||||
return SerializationUtils.SerializeBinary(context);
|
||||
}
|
||||
/// <summary>
|
||||
/// Was not parsed.
|
||||
/// </summary>
|
||||
NotParsed,
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DistributedContext FromByteArray(byte[] bytes)
|
||||
{
|
||||
return SerializationUtils.DeserializeBinary(bytes);
|
||||
}
|
||||
/// <summary>
|
||||
/// Invalid format.
|
||||
/// </summary>
|
||||
InvalidFormat,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
// <copyright file="HttpRuleParser.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
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 = <any US-ASCII character (octets 0 - 127)>
|
||||
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 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))
|
||||
{
|
||||
return HttpParseResult.InvalidFormat;
|
||||
}
|
||||
|
||||
// We don't care what the char next to '\' is.
|
||||
length = 2;
|
||||
return HttpParseResult.Parsed;
|
||||
}
|
||||
|
||||
private static bool[] CreateTokenChars()
|
||||
{
|
||||
// token = 1*<any CHAR except CTLs or separators>
|
||||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
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 = <any OCTET except CTLs, but including LWS>
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
//
|
||||
// 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. "\u" ('\' 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
{
|
||||
|
|
@ -36,28 +36,19 @@ namespace OpenTelemetry.Context.Propagation
|
|||
/// Injects textual representation of activity context to transmit over the wire.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of an object to set context on. Typically HttpRequest or similar.</typeparam>
|
||||
/// <param name="activityContext">Activity context to transmit over the wire.</param>
|
||||
/// <param name="context">The default context to transmit over the wire.</param>
|
||||
/// <param name="carrier">Object to set context on. Instance of this object will be passed to setter.</param>
|
||||
/// <param name="setter">Action that will set name and value pair on the object.</param>
|
||||
void Inject<T>(ActivityContext activityContext, T carrier, Action<T, string, string> setter);
|
||||
void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts activity context from textual representation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object to extract context from. Typically HttpRequest or similar.</typeparam>
|
||||
/// <param name="activityContext">The default activity context to be used if Extract fails.</param>
|
||||
/// <param name="context">The default context to be used if Extract fails.</param>
|
||||
/// <param name="carrier">Object to extract context from. Instance of this object will be passed to the getter.</param>
|
||||
/// <param name="getter">Function that will return string value of a key with the specified name.</param>
|
||||
/// <returns>Activity context from it's text representation.</returns>
|
||||
ActivityContext Extract<T>(ActivityContext activityContext, T carrier, Func<T, string, IEnumerable<string>> getter);
|
||||
|
||||
/// <summary>
|
||||
/// Tests if an activity context has been injected into a carrier.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object to extract context from. Typically HttpRequest or similar.</typeparam>
|
||||
/// <param name="carrier">Object to extract context from. Instance of this object will be passed to the getter.</param>
|
||||
/// <param name="getter">Function that will return string value of a key with the specified name.</param>
|
||||
/// <returns><see langword="true" /> if the carrier has been injected with an activity context.</returns>
|
||||
bool IsInjected<T>(T carrier, Func<T, string, IEnumerable<string>> getter);
|
||||
/// <returns>Context from it's text representation.</returns>
|
||||
PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
// <copyright file="NameValueHeaderValue.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
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<NameValueHeaderValue> SingleValueParser
|
||||
= new GenericHeaderParser<NameValueHeaderValue>(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 this.name; }
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get { return this.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. <name> in name/value string "<name>=<value>". 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 += 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
|
||||
{
|
||||
name = name,
|
||||
};
|
||||
current += HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces
|
||||
return current - startIndex;
|
||||
}
|
||||
|
||||
current++; // skip delimiter.
|
||||
current += HttpRuleParser.GetWhitespaceLength(input, current);
|
||||
|
||||
// Parse the value, i.e. <value> in name/value string "<name>=<value>"
|
||||
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
|
||||
{
|
||||
name = name,
|
||||
value = input.Substring(current, valueLength),
|
||||
};
|
||||
current += valueLength;
|
||||
current += HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces
|
||||
return current - startIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// <copyright file="PropagationContext.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores propagation data.
|
||||
/// </summary>
|
||||
public readonly struct PropagationContext : IEquatable<PropagationContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropagationContext"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="activityContext">Entries for activity context.</param>
|
||||
/// <param name="activityBaggage">Entries for activity baggage.</param>
|
||||
public PropagationContext(ActivityContext activityContext, IEnumerable<KeyValuePair<string, string>> activityBaggage)
|
||||
{
|
||||
this.ActivityContext = activityContext;
|
||||
this.ActivityBaggage = activityBaggage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="ActivityContext"/>.
|
||||
/// </summary>
|
||||
public ActivityContext ActivityContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets ActivityBaggage.
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<string, string>> ActivityBaggage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="PropagationContext"/> for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">First Entry to compare.</param>
|
||||
/// <param name="right">Second Entry to compare.</param>
|
||||
public static bool operator ==(PropagationContext left, PropagationContext right) => left.Equals(right);
|
||||
|
||||
/// <summary>
|
||||
/// Compare two entries of <see cref="PropagationContext"/> for not equality.
|
||||
/// </summary>
|
||||
/// <param name="left">First Entry to compare.</param>
|
||||
/// <param name="right">Second Entry to compare.</param>
|
||||
public static bool operator !=(PropagationContext left, PropagationContext right) => !(left == right);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(PropagationContext 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj) => (obj is PropagationContext context) && this.Equals(context);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = 323591981;
|
||||
hashCode = (hashCode * -1521134295) + this.ActivityContext.GetHashCode();
|
||||
hashCode = (hashCode * -1521134295) + EqualityComparer<IEnumerable<KeyValuePair<string, string>>>.Default.GetHashCode(this.ActivityBaggage);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
|
@ -42,49 +43,24 @@ namespace OpenTelemetry.Context.Propagation
|
|||
public ISet<string> Fields => new HashSet<string> { TraceState, TraceParent };
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInjected<T>(T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
public PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
if (context.ActivityContext.IsValid())
|
||||
{
|
||||
// If a valid context has already been extracted, perform a noop.
|
||||
return context;
|
||||
}
|
||||
|
||||
if (carrier == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier");
|
||||
return false;
|
||||
OpenTelemetryApiEventSource.Log.FailedToExtractActivityContext(nameof(TraceContextFormat), "null carrier");
|
||||
return context;
|
||||
}
|
||||
|
||||
if (getter == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToExtractContext("null getter");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var traceparentCollection = getter(carrier, TraceParent);
|
||||
|
||||
// There must be a single traceparent
|
||||
return traceparentCollection != null && traceparentCollection.Count() == 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ActivityContext Extract<T>(ActivityContext activityContext, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
if (carrier == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier");
|
||||
return activityContext;
|
||||
}
|
||||
|
||||
if (getter == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToExtractContext("null getter");
|
||||
return activityContext;
|
||||
OpenTelemetryApiEventSource.Log.FailedToExtractActivityContext(nameof(TraceContextFormat), "null getter");
|
||||
return context;
|
||||
}
|
||||
|
||||
try
|
||||
|
|
@ -94,7 +70,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
// There must be a single traceparent
|
||||
if (traceparentCollection == null || traceparentCollection.Count() != 1)
|
||||
{
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
var traceparent = traceparentCollection.First();
|
||||
|
|
@ -102,54 +78,56 @@ namespace OpenTelemetry.Context.Propagation
|
|||
|
||||
if (!traceparentParsed)
|
||||
{
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
string tracestate = string.Empty;
|
||||
string tracestate = null;
|
||||
var tracestateCollection = getter(carrier, TraceState);
|
||||
if (tracestateCollection?.Any() ?? false)
|
||||
{
|
||||
TryExtractTracestate(tracestateCollection.ToArray(), out tracestate);
|
||||
}
|
||||
|
||||
return new ActivityContext(traceId, spanId, traceoptions, tracestate, isRemote: true);
|
||||
return new PropagationContext(
|
||||
new ActivityContext(traceId, spanId, traceoptions, tracestate, isRemote: true),
|
||||
context.ActivityBaggage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.ActivityContextExtractException(ex);
|
||||
OpenTelemetryApiEventSource.Log.ActivityContextExtractException(nameof(TraceContextFormat), ex);
|
||||
}
|
||||
|
||||
// in case of exception indicate to upstream that there is no parseable context from the top
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Inject<T>(ActivityContext activityContext, T carrier, Action<T, string, string> setter)
|
||||
public void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter)
|
||||
{
|
||||
if (activityContext == default)
|
||||
if (context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("Invalid context");
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext(nameof(TraceContextFormat), "Invalid context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (carrier == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null carrier");
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext(nameof(TraceContextFormat), "null carrier");
|
||||
return;
|
||||
}
|
||||
|
||||
if (setter == null)
|
||||
{
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext("null setter");
|
||||
OpenTelemetryApiEventSource.Log.FailedToInjectActivityContext(nameof(TraceContextFormat), "null setter");
|
||||
return;
|
||||
}
|
||||
|
||||
var traceparent = string.Concat("00-", activityContext.TraceId.ToHexString(), "-", activityContext.SpanId.ToHexString());
|
||||
traceparent = string.Concat(traceparent, (activityContext.TraceFlags & 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 = activityContext.TraceState;
|
||||
string tracestateStr = context.ActivityContext.TraceState;
|
||||
if (tracestateStr?.Length > 0)
|
||||
{
|
||||
setter(carrier, TraceState, tracestateStr);
|
||||
|
|
|
|||
|
|
@ -28,11 +28,20 @@ namespace OpenTelemetry.Internal
|
|||
public static OpenTelemetryApiEventSource Log = new OpenTelemetryApiEventSource();
|
||||
|
||||
[NonEvent]
|
||||
public void ActivityContextExtractException(Exception ex)
|
||||
public void ActivityContextExtractException(string format, Exception ex)
|
||||
{
|
||||
if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1)))
|
||||
{
|
||||
this.FailedToExtractActivityContext(ex.ToInvariantString());
|
||||
this.FailedToExtractActivityContext(format, ex.ToInvariantString());
|
||||
}
|
||||
}
|
||||
|
||||
[NonEvent]
|
||||
public void BaggageExtractException(string format, Exception ex)
|
||||
{
|
||||
if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1)))
|
||||
{
|
||||
this.FailedToExtractBaggage(format, ex.ToInvariantString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,22 +102,28 @@ namespace OpenTelemetry.Internal
|
|||
this.WriteEvent(7, methodName, argumentName, issue);
|
||||
}
|
||||
|
||||
[Event(8, Message = "Failed to extract activity context: '{0}'", Level = EventLevel.Warning)]
|
||||
public void FailedToExtractActivityContext(string exception)
|
||||
[Event(8, Message = "Failed to extract activity context in format: '{0}', context: '{1}'.", Level = EventLevel.Warning)]
|
||||
public void FailedToExtractActivityContext(string format, string exception)
|
||||
{
|
||||
this.WriteEvent(8, exception);
|
||||
this.WriteEvent(8, format, exception);
|
||||
}
|
||||
|
||||
[Event(9, Message = "Failed to inject activity context: '{0}'", Level = EventLevel.Warning)]
|
||||
public void FailedToInjectActivityContext(string error)
|
||||
[Event(9, Message = "Failed to inject activity context in format: '{0}', context: '{1}'.", Level = EventLevel.Warning)]
|
||||
public void FailedToInjectActivityContext(string format, string error)
|
||||
{
|
||||
this.WriteEvent(9, error);
|
||||
this.WriteEvent(9, format, error);
|
||||
}
|
||||
|
||||
[Event(10, Message = "Failed to extract span context: '{0}'", Level = EventLevel.Warning)]
|
||||
public void FailedToExtractContext(string error)
|
||||
[Event(10, Message = "Failed to extract baggage in format: '{0}', baggage: '{1}'.", Level = EventLevel.Warning)]
|
||||
public void FailedToExtractBaggage(string format, string exception)
|
||||
{
|
||||
this.WriteEvent(10, error);
|
||||
this.WriteEvent(10, format, exception);
|
||||
}
|
||||
|
||||
[Event(11, Message = "Failed to inject baggage in format: '{0}', baggage: '{1}'.", Level = EventLevel.Warning)]
|
||||
public void FailedToInjectBaggage(string format, string error)
|
||||
{
|
||||
this.WriteEvent(11, format, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,6 @@ namespace OpenTelemetry.Metrics
|
|||
/// </summary>
|
||||
/// <param name="context">the associated distributed context.</param>
|
||||
/// <param name="value">value by which the bound counter metric should be added.</param>
|
||||
public abstract void Add(in DistributedContext context, T value);
|
||||
public abstract void Add(in CorrelationContext context, T value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,6 @@ namespace OpenTelemetry.Metrics
|
|||
/// </summary>
|
||||
/// <param name="context">the associated distributed context.</param>
|
||||
/// <param name="value">the measurement to be recorded.</param>
|
||||
public abstract void Record(in DistributedContext context, T value);
|
||||
public abstract void Record(in CorrelationContext context, T value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace OpenTelemetry.Metrics
|
|||
/// <param name="context">the associated distributed context.</param>
|
||||
/// <param name="value">value by which the counter should be incremented.</param>
|
||||
/// <param name="labelset">The labelset associated with this value.</param>
|
||||
public abstract void Add(in DistributedContext context, T value, LabelSet labelset);
|
||||
public abstract void Add(in CorrelationContext context, T value, LabelSet labelset);
|
||||
|
||||
/// <summary>
|
||||
/// Adds or Increments the counter.
|
||||
|
|
@ -57,7 +57,7 @@ namespace OpenTelemetry.Metrics
|
|||
/// <param name="context">the associated distributed context.</param>
|
||||
/// <param name="value">value by which the counter should be incremented.</param>
|
||||
/// <param name="labels">The labels or dimensions associated with this value.</param>
|
||||
public abstract void Add(in DistributedContext context, T value, IEnumerable<KeyValuePair<string, string>> labels);
|
||||
public abstract void Add(in CorrelationContext context, T value, IEnumerable<KeyValuePair<string, string>> labels);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound counter metric with given labelset.
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace OpenTelemetry.Metrics
|
|||
/// <param name="context">the associated distributed context.</param>
|
||||
/// <param name="value">value to record.</param>
|
||||
/// <param name="labelset">The labelset associated with this value.</param>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Records a measure.
|
||||
|
|
@ -57,7 +57,7 @@ namespace OpenTelemetry.Metrics
|
|||
/// <param name="context">the associated distributed context.</param>
|
||||
/// <param name="value">value to record.</param>
|
||||
/// <param name="labels">The labels or dimensions associated with this value.</param>
|
||||
public void Record(in DistributedContext context, T value, IEnumerable<KeyValuePair<string, string>> labels) => this.Bind(labels).Record(context, value);
|
||||
public void Record(in CorrelationContext context, T value, IEnumerable<KeyValuePair<string, string>> labels) => this.Bind(labels).Record(context, value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound measure metric with given labelset.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace OpenTelemetry.Metrics
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Add(in DistributedContext context, T value)
|
||||
public override void Add(in CorrelationContext context, T value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace OpenTelemetry.Metrics
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Record(in DistributedContext context, T value)
|
||||
public override void Record(in CorrelationContext context, T value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,12 +43,12 @@ namespace OpenTelemetry.Metrics
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Add(in DistributedContext context, T value, LabelSet labelset)
|
||||
public override void Add(in CorrelationContext context, T value, LabelSet labelset)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Add(in DistributedContext context, T value, IEnumerable<KeyValuePair<string, string>> labels)
|
||||
public override void Add(in CorrelationContext context, T value, IEnumerable<KeyValuePair<string, string>> labels)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ namespace OpenTelemetry.Trace
|
|||
{
|
||||
internal static readonly TelemetrySpan NoopInstance = new TelemetrySpan(null);
|
||||
internal readonly Activity Activity;
|
||||
private static readonly IEnumerable<KeyValuePair<string, string>> EmptyBaggage = new KeyValuePair<string, string>[0];
|
||||
|
||||
internal TelemetrySpan(Activity activity)
|
||||
{
|
||||
|
|
@ -65,6 +66,11 @@ namespace OpenTelemetry.Trace
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the span baggage.
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<string, string>> Baggage => this.Activity?.Baggage ?? EmptyBaggage;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the status of the span execution.
|
||||
/// </summary>
|
||||
|
|
@ -272,6 +278,31 @@ namespace OpenTelemetry.Trace
|
|||
this.Activity?.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a baggage item.
|
||||
/// </summary>
|
||||
/// <param name="key">Baggage item key.</param>
|
||||
/// <returns>Retrieved baggage value or <see langword="null"/> if no match was found.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string GetBaggageItem(string key)
|
||||
{
|
||||
return this.Activity?.GetBaggageItem(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a baggage item to the <see cref="TelemetrySpan"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">Baggage item key.</param>
|
||||
/// <param name="value">Baggage item value.</param>
|
||||
/// <returns>The <see cref="TelemetrySpan"/> instance for chaining.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TelemetrySpan AddBaggage(string key, string value)
|
||||
{
|
||||
this.Activity?.AddBaggage(key, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -26,18 +26,17 @@ namespace OpenTelemetry.Instrumentation.AspNet
|
|||
public class AspNetInstrumentationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation.
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation. Default value: <see cref="CompositePropagator"/> with <see cref="TraceContextFormat"/> & <see cref="BaggageFormat"/>.
|
||||
/// </summary>
|
||||
public ITextFormat TextFormat { get; set; } = new TraceContextFormat();
|
||||
public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[]
|
||||
{
|
||||
new TraceContextFormat(),
|
||||
new BaggageFormat(),
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a hook to exclude calls based on domain or other per-request criterion.
|
||||
/// </summary>
|
||||
internal Predicate<HttpContext> RequestFilter { get; set; } = DefaultFilter;
|
||||
|
||||
private static bool DefaultFilter(HttpContext httpContext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
internal Predicate<HttpContext> RequestFilter { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.4.0-beta.2
|
||||
|
||||
Released 2020-07-24
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
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;
|
||||
|
|
@ -61,17 +61,16 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
|
||||
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 != default)
|
||||
|
||||
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
|
||||
// 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;
|
||||
|
||||
// Starting the new activity make it the Activity.Current one.
|
||||
newOne.Start();
|
||||
|
|
@ -83,6 +82,14 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
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
|
||||
|
|
@ -114,6 +121,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
public override void OnStopActivity(Activity activity, object payload)
|
||||
{
|
||||
Activity activityToEnrich = activity;
|
||||
Activity createdActivity = null;
|
||||
|
||||
if (!(this.options.TextFormat is TraceContextFormat))
|
||||
{
|
||||
|
|
@ -125,9 +133,10 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,13 +199,12 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -26,18 +26,17 @@ namespace OpenTelemetry.Instrumentation.AspNetCore
|
|||
public class AspNetCoreInstrumentationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation.
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation. Default value: <see cref="CompositePropagator"/> with <see cref="TraceContextFormat"/> & <see cref="BaggageFormat"/>.
|
||||
/// </summary>
|
||||
public ITextFormat TextFormat { get; set; } = new TraceContextFormat();
|
||||
public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[]
|
||||
{
|
||||
new TraceContextFormat(),
|
||||
new BaggageFormat(),
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a hook to exclude calls based on domain or other per-request criterion.
|
||||
/// </summary>
|
||||
internal Predicate<HttpContext> RequestFilter { get; set; } = DefaultFilter;
|
||||
|
||||
private static bool DefaultFilter(HttpContext httpContext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
internal Predicate<HttpContext> RequestFilter { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.4.0-beta.2
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
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;
|
||||
|
|
@ -67,24 +67,29 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
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 != default)
|
||||
|
||||
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
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -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` +
|
||||
|
|
|
|||
|
|
@ -31,9 +31,13 @@ namespace OpenTelemetry.Instrumentation.Http
|
|||
public bool SetHttpFlavor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation. Default value: <see cref="TraceContextFormat"/>.
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation. Default value: <see cref="CompositePropagator"/> with <see cref="TraceContextFormat"/> & <see cref="BaggageFormat"/>.
|
||||
/// </summary>
|
||||
public ITextFormat TextFormat { get; set; } = new TraceContextFormat();
|
||||
public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[]
|
||||
{
|
||||
new TraceContextFormat(),
|
||||
new BaggageFormat(),
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional callback method for filtering <see cref="HttpRequestMessage"/> requests that are sent through the instrumentation.
|
||||
|
|
|
|||
|
|
@ -32,9 +32,13 @@ namespace OpenTelemetry.Instrumentation.Http
|
|||
public bool SetHttpFlavor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation. Default value: <see cref="TraceContextFormat"/>.
|
||||
/// Gets or sets <see cref="ITextFormat"/> for context propagation. Default value: <see cref="CompositePropagator"/> with <see cref="TraceContextFormat"/> & <see cref="BaggageFormat"/>.
|
||||
/// </summary>
|
||||
public ITextFormat TextFormat { get; set; } = new TraceContextFormat();
|
||||
public ITextFormat TextFormat { get; set; } = new CompositePropagator(new ITextFormat[]
|
||||
{
|
||||
new TraceContextFormat(),
|
||||
new BaggageFormat(),
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional callback method for filtering <see cref="HttpWebRequest"/> requests that are sent through the instrumentation.
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
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 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
|
||||
if (!(this.httpClientSupportsW3C && this.options.TextFormat is TraceContextFormat))
|
||||
{
|
||||
this.options.TextFormat.Inject(activity.Context, request, HttpRequestMessageHeaderValueSetter);
|
||||
this.options.TextFormat.Inject(new PropagationContext(activity.Context, activity.Baggage), request, HttpRequestMessageHeaderValueSetter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ using System.Reflection;
|
|||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using OpenTelemetry.Context.Propagation;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.Http.Implementation
|
||||
|
|
@ -44,8 +45,6 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
|
||||
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());
|
||||
|
||||
|
|
@ -188,32 +187,11 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
|
||||
[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<KeyValuePair<string, string>> e = activity.Baggage.GetEnumerator();
|
||||
|
||||
if (e.MoveNext())
|
||||
{
|
||||
StringBuilder baggage = new StringBuilder();
|
||||
do
|
||||
{
|
||||
KeyValuePair<string, string> 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(new PropagationContext(activity.Context, activity.Baggage), 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
{
|
||||
public sealed class SpanContextShim : ISpanContext
|
||||
{
|
||||
public SpanContextShim(in Trace.SpanContext spanContext)
|
||||
private readonly IEnumerable<KeyValuePair<string, string>> baggage;
|
||||
|
||||
public SpanContextShim(in Trace.SpanContext spanContext, IEnumerable<KeyValuePair<string, string>> baggage = null)
|
||||
{
|
||||
if (!spanContext.IsValid)
|
||||
{
|
||||
|
|
@ -30,6 +32,7 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
}
|
||||
|
||||
this.SpanContext = spanContext;
|
||||
this.baggage = baggage;
|
||||
}
|
||||
|
||||
public Trace.SpanContext SpanContext { get; private set; }
|
||||
|
|
@ -42,9 +45,6 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<KeyValuePair<string, string>> GetBaggageItems()
|
||||
{
|
||||
// TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
=> this.baggage;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
throw new ArgumentNullException(nameof(carrier));
|
||||
}
|
||||
|
||||
ActivityContext activityContext = default;
|
||||
PropagationContext propagationContext = default;
|
||||
|
||||
if ((format == BuiltinFormats.TextMap || format == BuiltinFormats.HttpHeaders) && carrier is ITextMap textMapCarrier)
|
||||
{
|
||||
|
|
@ -71,7 +71,7 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
carrierMap.Add(entry.Key, new[] { entry.Value });
|
||||
}
|
||||
|
||||
IEnumerable<string> GetCarrierKeyValue(Dictionary<string, IEnumerable<string>> source, string key)
|
||||
static IEnumerable<string> GetCarrierKeyValue(Dictionary<string, IEnumerable<string>> source, string key)
|
||||
{
|
||||
if (key == null || !source.TryGetValue(key, out var value))
|
||||
{
|
||||
|
|
@ -81,10 +81,10 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
return value;
|
||||
}
|
||||
|
||||
activityContext = this.textFormat.Extract(default, carrierMap, GetCarrierKeyValue);
|
||||
propagationContext = this.textFormat.Extract(propagationContext, carrierMap, GetCarrierKeyValue);
|
||||
}
|
||||
|
||||
return !activityContext.IsValid() ? null : new SpanContextShim(new Trace.SpanContext(activityContext));
|
||||
return !propagationContext.ActivityContext.IsValid() ? null : new SpanContextShim(new Trace.SpanContext(propagationContext.ActivityContext), propagationContext.ActivityBaggage);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -115,7 +115,10 @@ namespace OpenTelemetry.Shims.OpenTracing
|
|||
|
||||
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 PropagationContext(shim.SpanContext, shim.GetBaggageItems()),
|
||||
textMapCarrier,
|
||||
(instrumentation, key, value) => instrumentation.Set(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
// <copyright file="AsyncLocalDistributedContextCarrier.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
#if NET452
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
#endif
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Distributed Context carrier using AsyncLocal.
|
||||
/// </summary>
|
||||
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<DistributedContext> carrier = new AsyncLocal<DistributedContext>();
|
||||
#endif
|
||||
|
||||
private AsyncLocalDistributedContextCarrier()
|
||||
{
|
||||
this.OverwriteCurrent(DistributedContext.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of <see cref="AsyncLocalDistributedContextCarrier"/>.
|
||||
/// </summary>
|
||||
public static DistributedContextCarrier Instance { get; } = new AsyncLocalDistributedContextCarrier();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="DistributedContext"/>.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current <see cref="DistributedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">Context to set as current.</param>
|
||||
/// <returns>Scope object. On disposal - original context will be restored.</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
// <copyright file="DistributedContextState.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
// <copyright file="NoopDistributedContextBinarySerializer.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
#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<byte>();
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] ToByteArray(DistributedContext context)
|
||||
{
|
||||
if (context.CorrelationContext.Entries is null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToInjectContext("entries are null");
|
||||
}
|
||||
|
||||
return EmptyByteArray;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DistributedContext FromByteArray(byte[] bytes)
|
||||
{
|
||||
if (bytes == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null bytes");
|
||||
}
|
||||
|
||||
return DistributedContext.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,95 +70,64 @@ namespace OpenTelemetry.Context.Propagation
|
|||
public ISet<string> Fields => AllFields;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInjected<T>(T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
public PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
if (context.ActivityContext.IsValid())
|
||||
{
|
||||
// If a valid context has already been extracted, perform a noop.
|
||||
return context;
|
||||
}
|
||||
|
||||
if (carrier == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null carrier");
|
||||
return false;
|
||||
OpenTelemetrySdkEventSource.Log.FailedToExtractActivityContext(nameof(B3Format), "null carrier");
|
||||
return context;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ActivityContext Extract<T>(ActivityContext activityContext, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
if (carrier == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null carrier");
|
||||
return activityContext;
|
||||
}
|
||||
|
||||
if (getter == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToExtractContext("null getter");
|
||||
return activityContext;
|
||||
OpenTelemetrySdkEventSource.Log.FailedToExtractActivityContext(nameof(B3Format), "null getter");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Inject<T>(ActivityContext activityContext, T carrier, Action<T, string, string> setter)
|
||||
public void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter)
|
||||
{
|
||||
if (!activityContext.IsValid())
|
||||
if (context.ActivityContext.TraceId == default || context.ActivityContext.SpanId == default)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToInjectContext("invalid context");
|
||||
OpenTelemetrySdkEventSource.Log.FailedToInjectActivityContext(nameof(B3Format), "invalid context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (carrier == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToInjectContext("null carrier");
|
||||
OpenTelemetrySdkEventSource.Log.FailedToInjectActivityContext(nameof(B3Format), "null carrier");
|
||||
return;
|
||||
}
|
||||
|
||||
if (setter == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.FailedToInjectContext("null setter");
|
||||
OpenTelemetrySdkEventSource.Log.FailedToInjectActivityContext(nameof(B3Format), "null setter");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.singleHeader)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(activityContext.TraceId.ToHexString());
|
||||
sb.Append(context.ActivityContext.TraceId.ToHexString());
|
||||
sb.Append(XB3CombinedDelimiter);
|
||||
sb.Append(activityContext.SpanId.ToHexString());
|
||||
if ((activityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0)
|
||||
sb.Append(context.ActivityContext.SpanId.ToHexString());
|
||||
if ((context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0)
|
||||
{
|
||||
sb.Append(XB3CombinedDelimiter);
|
||||
sb.Append(SampledValue);
|
||||
|
|
@ -168,16 +137,16 @@ namespace OpenTelemetry.Context.Propagation
|
|||
}
|
||||
else
|
||||
{
|
||||
setter(carrier, XB3TraceId, activityContext.TraceId.ToHexString());
|
||||
setter(carrier, XB3SpanId, activityContext.SpanId.ToHexString());
|
||||
if ((activityContext.TraceFlags & 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ActivityContext ExtractFromMultipleHeaders<T>(ActivityContext activityContext, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
private static PropagationContext ExtractFromMultipleHeaders<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -195,7 +164,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
}
|
||||
else
|
||||
{
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
ActivitySpanId spanId;
|
||||
|
|
@ -206,7 +175,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
}
|
||||
else
|
||||
{
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
var traceOptions = ActivityTraceFlags.None;
|
||||
|
|
@ -216,35 +185,37 @@ namespace OpenTelemetry.Context.Propagation
|
|||
traceOptions |= ActivityTraceFlags.Recorded;
|
||||
}
|
||||
|
||||
return new ActivityContext(traceId, spanId, traceOptions, isRemote: true);
|
||||
return new PropagationContext(
|
||||
new ActivityContext(traceId, spanId, traceOptions, isRemote: true),
|
||||
context.ActivityBaggage);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.ContextExtractException(e);
|
||||
return activityContext;
|
||||
OpenTelemetrySdkEventSource.Log.ActivityContextExtractException(nameof(B3Format), e);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
private static ActivityContext ExtractFromSingleHeader<T>(ActivityContext activityContext, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
private static PropagationContext ExtractFromSingleHeader<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> 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 +229,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
var spanIdStr = parts[1];
|
||||
if (string.IsNullOrWhiteSpace(spanIdStr))
|
||||
{
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
var spanId = ActivitySpanId.CreateFromString(spanIdStr.AsSpan());
|
||||
|
|
@ -274,12 +245,14 @@ namespace OpenTelemetry.Context.Propagation
|
|||
}
|
||||
}
|
||||
|
||||
return new ActivityContext(traceId, spanId, traceOptions, isRemote: true);
|
||||
return new PropagationContext(
|
||||
new ActivityContext(traceId, spanId, traceOptions, isRemote: true),
|
||||
context.ActivityBaggage);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.ContextExtractException(e);
|
||||
return activityContext;
|
||||
OpenTelemetrySdkEventSource.Log.ActivityContextExtractException(nameof(B3Format), e);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
// <copyright file="DistributedContextBinarySerializerBase.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
{
|
||||
/// <summary>
|
||||
/// DistributedContextBinarySerializerBase base class.
|
||||
/// </summary>
|
||||
public abstract class DistributedContextBinarySerializerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes input to <see cref="DistributedContext"/> based on the binary format standard.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Array of bytes in binary format standard.</param>
|
||||
/// <returns><see cref="DistributedContext"/>.</returns>
|
||||
public abstract DistributedContext FromByteArray(byte[] bytes);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="DistributedContext"/> to the on-the-wire format.
|
||||
/// </summary>
|
||||
/// <param name="tags"><see cref="DistributedContext"/>.</param>
|
||||
/// <returns>Serialized <see cref="DistributedContext"/>.</returns>
|
||||
public abstract byte[] ToByteArray(DistributedContext tags);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
// <copyright file="SerializationUtils.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
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<byte>();
|
||||
#endif
|
||||
|
||||
// Serializes a DistributedContext to the on-the-wire format.
|
||||
// Encoded tags are of the form: <version_id><encoded_tags>
|
||||
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<Tag> 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: <version_id><encoded_tags>
|
||||
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<CorrelationContextEntry> tags)
|
||||
{
|
||||
tags = new List<CorrelationContextEntry>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,11 +38,11 @@ namespace OpenTelemetry.Internal
|
|||
}
|
||||
|
||||
[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 @@ namespace OpenTelemetry.Internal
|
|||
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)]
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace OpenTelemetry.Metrics
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace OpenTelemetry.Metrics
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ namespace OpenTelemetry.Metrics
|
|||
this.Bind(new LabelSetSdk(labels), isShortLived: true).Add(context, value);
|
||||
}
|
||||
|
||||
public override void Add(in DistributedContext context, double value, LabelSet labelset)
|
||||
public override void Add(in CorrelationContext context, double value, LabelSet labelset)
|
||||
{
|
||||
// user not using bound instrument. Hence create a short-lived bound instrument.
|
||||
this.Bind(labelset, isShortLived: true).Add(context, value);
|
||||
}
|
||||
|
||||
public override void Add(in DistributedContext context, double value, IEnumerable<KeyValuePair<string, string>> labels)
|
||||
public override void Add(in CorrelationContext context, double value, IEnumerable<KeyValuePair<string, string>> labels)
|
||||
{
|
||||
// user not using bound instrument. Hence create a short-lived bound instrument.
|
||||
this.Bind(new LabelSetSdk(labels), isShortLived: true).Add(context, value);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace OpenTelemetry.Metrics
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace OpenTelemetry.Metrics
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ namespace OpenTelemetry.Metrics
|
|||
this.Bind(new LabelSetSdk(labels), isShortLived: true).Add(context, value);
|
||||
}
|
||||
|
||||
public override void Add(in DistributedContext context, long value, LabelSet labelset)
|
||||
public override void Add(in CorrelationContext context, long value, LabelSet labelset)
|
||||
{
|
||||
// user not using bound instrument. Hence create a short-lived bound instrument.
|
||||
this.Bind(labelset, isShortLived: true).Add(context, value);
|
||||
}
|
||||
|
||||
public override void Add(in DistributedContext context, long value, IEnumerable<KeyValuePair<string, string>> labels)
|
||||
public override void Add(in CorrelationContext context, long value, IEnumerable<KeyValuePair<string, string>> labels)
|
||||
{
|
||||
// user not using bound instrument. Hence create a short-lived bound instrument.
|
||||
this.Bind(new LabelSetSdk(labels), isShortLived: true).Add(context, value);
|
||||
|
|
|
|||
|
|
@ -53,10 +53,8 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
|
|||
[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(
|
||||
|
|
@ -129,10 +127,12 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
|
|||
var expectedTraceId = ActivityTraceId.CreateRandom();
|
||||
var expectedSpanId = ActivitySpanId.CreateRandom();
|
||||
var textFormat = new Mock<ITextFormat>();
|
||||
textFormat.Setup(m => m.Extract<HttpRequest>(It.IsAny<ActivityContext>(), It.IsAny<HttpRequest>(), It.IsAny<Func<HttpRequest, string, IEnumerable<string>>>())).Returns(new ActivityContext(
|
||||
expectedTraceId,
|
||||
expectedSpanId,
|
||||
ActivityTraceFlags.Recorded));
|
||||
textFormat.Setup(m => m.Extract<HttpRequest>(It.IsAny<PropagationContext>(), It.IsAny<HttpRequest>(), It.IsAny<Func<HttpRequest, string, IEnumerable<string>>>())).Returns(new PropagationContext(
|
||||
new ActivityContext(
|
||||
expectedTraceId,
|
||||
expectedSpanId,
|
||||
ActivityTraceFlags.Recorded),
|
||||
null));
|
||||
|
||||
var activity = new Activity(ActivityNameAspNet).AddBaggage("Stuff", "123");
|
||||
activity.SetParentId(expectedTraceId, expectedSpanId, ActivityTraceFlags.Recorded);
|
||||
|
|
@ -186,7 +186,19 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,10 +144,11 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
var expectedSpanId = ActivitySpanId.CreateRandom();
|
||||
|
||||
var textFormat = new Mock<ITextFormat>();
|
||||
textFormat.Setup(m => m.Extract(It.IsAny<ActivityContext>(), It.IsAny<HttpRequest>(), It.IsAny<Func<HttpRequest, string, IEnumerable<string>>>())).Returns(new ActivityContext(
|
||||
textFormat.Setup(m => m.Extract(It.IsAny<PropagationContext>(), It.IsAny<HttpRequest>(), It.IsAny<Func<HttpRequest, string, IEnumerable<string>>>())).Returns(new PropagationContext(
|
||||
new ActivityContext(
|
||||
expectedTraceId,
|
||||
expectedSpanId,
|
||||
ActivityTraceFlags.Recorded));
|
||||
ActivityTraceFlags.Recorded), null));
|
||||
|
||||
// Arrange
|
||||
using (var testFactory = this.factory
|
||||
|
|
|
|||
|
|
@ -73,22 +73,23 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
|
||||
// Ensure that the header value func does not throw if the header key can't be found
|
||||
var mockTextFormat = new Mock<ITextFormat>();
|
||||
var isInjectedHeaderValueGetterThrows = false;
|
||||
mockTextFormat
|
||||
.Setup(x => x.IsInjected(It.IsAny<HttpRequestMessage>(), It.IsAny<Func<HttpRequestMessage, string, IEnumerable<string>>>()))
|
||||
.Callback<HttpRequestMessage, Func<HttpRequestMessage, string, IEnumerable<string>>>(
|
||||
(carrier, getter) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// traceparent doesn't exist
|
||||
getter(carrier, "traceparent");
|
||||
}
|
||||
catch
|
||||
{
|
||||
isInjectedHeaderValueGetterThrows = true;
|
||||
}
|
||||
});
|
||||
|
||||
// var isInjectedHeaderValueGetterThrows = false;
|
||||
// mockTextFormat
|
||||
// .Setup(x => x.IsInjected(It.IsAny<HttpRequestMessage>(), It.IsAny<Func<HttpRequestMessage, string, IEnumerable<string>>>()))
|
||||
// .Callback<HttpRequestMessage, Func<HttpRequestMessage, string, IEnumerable<string>>>(
|
||||
// (carrier, getter) =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // traceparent doesn't exist
|
||||
// getter(carrier, "traceparent");
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// isInjectedHeaderValueGetterThrows = true;
|
||||
// }
|
||||
// });
|
||||
|
||||
using (Sdk.CreateTracerProviderBuilder()
|
||||
.AddHttpClientInstrumentation(o => o.TextFormat = mockTextFormat.Object)
|
||||
|
|
@ -114,19 +115,16 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
|
||||
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<HttpRequestMessage>(), It.IsAny<Func<HttpRequestMessage, string, IEnumerable<string>>>()), Times.Once);
|
||||
Assert.False(isInjectedHeaderValueGetterThrows);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HttpClientInstrumentationInjectsHeadersAsync_CustomFormat()
|
||||
{
|
||||
var textFormat = new Mock<ITextFormat>();
|
||||
textFormat.Setup(m => m.Inject<HttpRequestMessage>(It.IsAny<ActivityContext>(), It.IsAny<HttpRequestMessage>(), It.IsAny<Action<HttpRequestMessage, string, string>>()))
|
||||
.Callback<ActivityContext, HttpRequestMessage, Action<HttpRequestMessage, string, string>>((context, message, action) =>
|
||||
textFormat.Setup(m => m.Inject<HttpRequestMessage>(It.IsAny<PropagationContext>(), It.IsAny<HttpRequestMessage>(), It.IsAny<Action<HttpRequestMessage, string, string>>()))
|
||||
.Callback<PropagationContext, HttpRequestMessage, Action<HttpRequestMessage, string, string>>((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);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
|
||||
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 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -97,10 +97,10 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
public async Task HttpWebRequestInstrumentationInjectsHeadersAsync_CustomFormat()
|
||||
{
|
||||
var textFormat = new Mock<ITextFormat>();
|
||||
textFormat.Setup(m => m.Inject(It.IsAny<ActivityContext>(), It.IsAny<HttpWebRequest>(), It.IsAny<Action<HttpWebRequest, string, string>>()))
|
||||
.Callback<ActivityContext, HttpWebRequest, Action<HttpWebRequest, string, string>>((context, message, action) =>
|
||||
textFormat.Setup(m => m.Inject(It.IsAny<PropagationContext>(), It.IsAny<HttpWebRequest>(), It.IsAny<Action<HttpWebRequest, string, string>>()))
|
||||
.Callback<PropagationContext, HttpWebRequest, Action<HttpWebRequest, string, string>>((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);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ namespace OpenTelemetry.Shims.OpenTracing.Tests
|
|||
public void GetBaggage()
|
||||
{
|
||||
var shim = GetSpanContextShim();
|
||||
Assert.Throws<NotImplementedException>(() => shim.GetBaggageItems());
|
||||
var baggage = shim.GetBaggageItems();
|
||||
Assert.Null(baggage);
|
||||
}
|
||||
|
||||
internal static SpanContextShim GetSpanContextShim()
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
// <copyright file="CorrelationContextBuilderTest.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Context.Tests
|
||||
{
|
||||
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<CorrelationContextEntry> List1 = new List<CorrelationContextEntry>(1)
|
||||
{ new CorrelationContextEntry(Key1, Value1) };
|
||||
|
||||
private static readonly List<CorrelationContextEntry> List2 = new List<CorrelationContextEntry>(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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
// <copyright file="CorrelationContextEntryTest.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Context.Tests
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestEquality()
|
||||
{
|
||||
var entry1 = new CorrelationContextEntry("key", "value1");
|
||||
var entry2 = new CorrelationContextEntry("key", "value1");
|
||||
object entry3 = entry2;
|
||||
var entry4 = new CorrelationContextEntry("key", "value2");
|
||||
|
||||
Assert.True(entry1 == entry2);
|
||||
Assert.True(entry1.Equals(entry2));
|
||||
Assert.True(entry1.Equals(entry3));
|
||||
|
||||
Assert.True(entry1 != entry4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestToString()
|
||||
{
|
||||
var entry1 = new CorrelationContextEntry("key1", "value1");
|
||||
Assert.Equal("CorrelationContextEntry{Key=key1, Value=value1}", entry1.ToString());
|
||||
|
||||
var entry2 = new CorrelationContextEntry(null, "value1");
|
||||
Assert.Equal("CorrelationContextEntry{Key=, Value=value1}", entry2.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestGetHashCode()
|
||||
{
|
||||
var entry1 = new CorrelationContextEntry("key1", "value1");
|
||||
Assert.NotEqual(0, entry1.GetHashCode());
|
||||
|
||||
var entry2 = new CorrelationContextEntry(null, "value1");
|
||||
Assert.NotEqual(0, entry2.GetHashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,121 +14,135 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
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";
|
||||
|
||||
public CorrelationContextTest()
|
||||
{
|
||||
DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance;
|
||||
}
|
||||
private const string V1 = "Value1";
|
||||
private const string V2 = "Value2";
|
||||
|
||||
[Fact]
|
||||
public void EmptyContext()
|
||||
{
|
||||
var dc = CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>());
|
||||
Assert.Empty(dc.Entries);
|
||||
Assert.Equal(CorrelationContext.Empty, dc);
|
||||
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]
|
||||
public void NonEmptyContext()
|
||||
{
|
||||
var list = new List<CorrelationContextEntry>(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();
|
||||
|
||||
[Fact]
|
||||
public void AddExtraKey()
|
||||
{
|
||||
var list = new List<CorrelationContextEntry>(1) { new CorrelationContextEntry(K1, V1) };
|
||||
var dc = CorrelationContextBuilder.CreateContext(list);
|
||||
Assert.Equal(list, dc.Entries);
|
||||
var list = new List<KeyValuePair<string, string>>(2)
|
||||
{
|
||||
new KeyValuePair<string, string>(K1, V1),
|
||||
new KeyValuePair<string, string>(K2, V2),
|
||||
};
|
||||
|
||||
list.Add(new CorrelationContextEntry(K2, V2));
|
||||
var dc1 = CorrelationContextBuilder.CreateContext(list);
|
||||
Assert.Equal(list, dc1.Entries);
|
||||
var cc = CorrelationContext.Current;
|
||||
|
||||
cc.AddCorrelation(K1, V1);
|
||||
cc.AddCorrelation(K2, V2);
|
||||
|
||||
Assert.NotEqual(CorrelationContext.Empty, cc);
|
||||
Assert.Equal(list, cc.Correlations);
|
||||
|
||||
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<CorrelationContextEntry>(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K1, V2) };
|
||||
var dc = CorrelationContextBuilder.CreateContext(list);
|
||||
Assert.Equal(new List<CorrelationContextEntry>(1) { new CorrelationContextEntry(K1, V2) }, dc.Entries);
|
||||
}
|
||||
using Activity activity = new Activity("TestActivity");
|
||||
activity.Start();
|
||||
|
||||
[Fact]
|
||||
public void UseDefaultEntry()
|
||||
{
|
||||
Assert.Equal(CorrelationContext.Empty, CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>(1) { default }));
|
||||
Assert.Equal(CorrelationContext.Empty, CorrelationContextBuilder.CreateContext(null));
|
||||
}
|
||||
var list = new List<KeyValuePair<string, string>>(2)
|
||||
{
|
||||
new KeyValuePair<string, string>(K1, V1),
|
||||
new KeyValuePair<string, string>(K1, V1),
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void RemoveExistingKey()
|
||||
{
|
||||
var list = new List<CorrelationContextEntry>(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) };
|
||||
var dc = CorrelationContextBuilder.CreateContext(list);
|
||||
Assert.Equal(list, dc.Entries);
|
||||
var cc = CorrelationContext.Current;
|
||||
|
||||
list.RemoveAt(0);
|
||||
cc.AddCorrelation(K1, V1);
|
||||
cc.AddCorrelation(K1, V1);
|
||||
|
||||
dc = CorrelationContextBuilder.CreateContext(list);
|
||||
Assert.Equal(list, dc.Entries);
|
||||
|
||||
list.Clear();
|
||||
dc = CorrelationContextBuilder.CreateContext(list);
|
||||
Assert.Equal(CorrelationContext.Empty, dc);
|
||||
Assert.Equal(list, cc.Correlations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestIterator()
|
||||
{
|
||||
var list = new List<CorrelationContextEntry>(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) };
|
||||
var dc = CorrelationContextBuilder.CreateContext(list);
|
||||
using Activity activity = new Activity("TestActivity");
|
||||
activity.Start();
|
||||
|
||||
var list = new List<KeyValuePair<string, string>>(2)
|
||||
{
|
||||
new KeyValuePair<string, string>(K1, V1),
|
||||
new KeyValuePair<string, string>(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<CorrelationContextEntry> { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) }, new List<CorrelationContextEntry> { tag1, tag2 });
|
||||
|
||||
Assert.Equal(list, new List<KeyValuePair<string, string>> { tag1, tag2 });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestEquals()
|
||||
{
|
||||
var dc1 = CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) });
|
||||
var dc2 = CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V2) });
|
||||
object odc2 = dc2;
|
||||
var dc3 = CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>(2) { new CorrelationContextEntry(K2, V2), new CorrelationContextEntry(K1, V1) });
|
||||
var dc4 = CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>(2) { new CorrelationContextEntry(K1, V1), new CorrelationContextEntry(K2, V1) });
|
||||
var dc5 = CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>(2) { new CorrelationContextEntry(K1, V2), new CorrelationContextEntry(K2, V1) });
|
||||
var dc6 = CorrelationContextBuilder.CreateContext(new List<CorrelationContextEntry>(3) { new CorrelationContextEntry(K1, V2) });
|
||||
var cc1 = CreateCorrelationContext(new KeyValuePair<string, string>(K1, V1), new KeyValuePair<string, string>(K2, V2));
|
||||
var cc2 = CreateCorrelationContext(new KeyValuePair<string, string>(K1, V1), new KeyValuePair<string, string>(K2, V2));
|
||||
var cc3 = CreateCorrelationContext(new KeyValuePair<string, string>(K2, V2), new KeyValuePair<string, string>(K1, V1));
|
||||
var cc4 = CreateCorrelationContext(new KeyValuePair<string, string>(K1, V1), new KeyValuePair<string, string>(K2, V1));
|
||||
var cc5 = CreateCorrelationContext(new KeyValuePair<string, string>(K1, V2), new KeyValuePair<string, string>(K2, V1));
|
||||
|
||||
Assert.True(dc1.Equals(dc2));
|
||||
Assert.True(dc1.Equals(odc2));
|
||||
Assert.True(dc1 == dc2);
|
||||
Assert.True(dc1.Equals(dc3));
|
||||
Assert.True(cc1.Equals(cc2));
|
||||
|
||||
Assert.False(dc1.Equals(dc4));
|
||||
Assert.True(dc1 != dc4);
|
||||
Assert.False(dc2.Equals(dc4));
|
||||
Assert.False(dc3.Equals(dc4));
|
||||
Assert.False(dc5.Equals(dc4));
|
||||
Assert.False(dc4.Equals(dc5));
|
||||
Assert.False(dc5.Equals(dc6));
|
||||
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<string, string>[] correlations)
|
||||
{
|
||||
using Activity activity = new Activity("TestActivity");
|
||||
activity.Start();
|
||||
|
||||
var cc = CorrelationContext.Current;
|
||||
|
||||
cc.AddCorrelation(correlations);
|
||||
|
||||
return cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
// <copyright file="DistributedContextsScopeTest.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Context.Tests
|
||||
{
|
||||
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<CorrelationContextEntry> list = new List<CorrelationContextEntry>(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<CorrelationContextEntry> list = new List<CorrelationContextEntry>(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<CorrelationContextEntry>(1) { new CorrelationContextEntry(Key1, Value1) };
|
||||
var list2 = new List<CorrelationContextEntry>(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
// <copyright file="DistributedContextDeserializationTest.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OpenTelemetry.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation.Tests
|
||||
{
|
||||
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<CorrelationContextEntry>(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<CorrelationContextEntry>(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<CorrelationContextEntry>(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<CorrelationContextEntry>(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 <version_id>(<tag_field_id><tag_encoding>)*
|
||||
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 }));
|
||||
}
|
||||
|
||||
// <tag_encoding> ==
|
||||
// <tag_key_len><tag_key><tag_val_len><tag_val>
|
||||
// <tag_key_len> == varint encoded integer
|
||||
// <tag_key> == tag_key_len bytes comprising tag key name
|
||||
// <tag_val_len> == varint encoded integer
|
||||
// <tag_val> == 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
// <copyright file="DistributedContextRoundtripTest.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation.Tests
|
||||
{
|
||||
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<CorrelationContextEntry>(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<CorrelationContextEntry>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
// <copyright file="DistributedContextSerializationTest.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenTelemetry.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation.Tests
|
||||
{
|
||||
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<CorrelationContextEntry>();
|
||||
|
||||
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<CorrelationContextEntry> sequence, int count)
|
||||
{
|
||||
var tmp = sequence[count - 1];
|
||||
sequence.RemoveAt(count - 1);
|
||||
sequence.Insert(0, tmp);
|
||||
}
|
||||
|
||||
private static IEnumerable<IList<CorrelationContextEntry>> Permutate(IList<CorrelationContextEntry> 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<CorrelationContextEntry>(tags);
|
||||
|
||||
var actual = this.serializer.ToByteArray(DistributedContextBuilder.CreateContext(list));
|
||||
var tagsList = tags.ToList();
|
||||
var tagPermutation = Permutate(tagsList, tagsList.Count);
|
||||
ISet<string> possibleOutPuts = new HashSet<string>();
|
||||
|
||||
foreach (var distributedContextEntries in tagPermutation)
|
||||
{
|
||||
var l = (List<CorrelationContextEntry>)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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -221,6 +221,8 @@ namespace OpenTelemetry.Trace.Tests
|
|||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
this.testProcessor.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ namespace OpenTelemetry.Trace.Tests
|
|||
{
|
||||
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 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[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 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[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 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[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 }))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
public void Serialize_SampledContext()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>();
|
||||
this.b3Format.Inject(new ActivityContext(TraceId, SpanId, TraceOptions), carrier, Setter);
|
||||
this.b3Format.Inject(new PropagationContext(new ActivityContext(TraceId, SpanId, TraceOptions), null), carrier, Setter);
|
||||
this.ContainsExactly(carrier, new Dictionary<string, string> { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 }, { B3Format.XB3Sampled, "1" } });
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
var carrier = new Dictionary<string, string>();
|
||||
var context = new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None);
|
||||
this.output.WriteLine(context.ToString());
|
||||
this.b3Format.Inject(context, carrier, Setter);
|
||||
this.b3Format.Inject(new PropagationContext(context, null), carrier, Setter);
|
||||
this.ContainsExactly(carrier, new Dictionary<string, string> { { B3Format.XB3TraceId, TraceIdBase16 }, { B3Format.XB3SpanId, SpanIdBase16 } });
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{ 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 PropagationContext(spanContext, null), this.b3Format.Extract(default, headersNotSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -88,7 +88,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3Format.Extract(default, headersSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -98,7 +99,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3Format.Extract(default, headersNotSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -108,7 +110,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3Format.Extract(default, headersFlagSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -118,7 +121,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3Format.Extract(default, headersFlagNotSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -130,7 +134,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{ 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 PropagationContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -140,7 +145,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3Format.Extract(default, headersEightBytes, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -202,7 +208,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
public void Serialize_SampledContext_SingleHeader()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>();
|
||||
this.b3FormatSingleHeader.Inject(new ActivityContext(TraceId, SpanId, TraceOptions), carrier, Setter);
|
||||
var activityContext = new ActivityContext(TraceId, SpanId, TraceOptions);
|
||||
this.b3FormatSingleHeader.Inject(new PropagationContext(activityContext, null), carrier, Setter);
|
||||
this.ContainsExactly(carrier, new Dictionary<string, string> { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-1" } });
|
||||
}
|
||||
|
||||
|
|
@ -210,9 +217,9 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
public void Serialize_NotSampledContext_SingleHeader()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>();
|
||||
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 PropagationContext(activityContext, null), carrier, Setter);
|
||||
this.ContainsExactly(carrier, new Dictionary<string, string> { { B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}" } });
|
||||
}
|
||||
|
||||
|
|
@ -223,8 +230,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -234,7 +241,10 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-1" },
|
||||
};
|
||||
Assert.Equal(new ActivityContext(TraceId, SpanId, TraceOptions), this.b3FormatSingleHeader.Extract(default, headersSampled, Getter));
|
||||
|
||||
Assert.Equal(
|
||||
new PropagationContext(new ActivityContext(TraceId, SpanId, TraceOptions), null),
|
||||
this.b3FormatSingleHeader.Extract(default, headersSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -244,7 +254,10 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ B3Format.XB3Combined, $"{TraceIdBase16}-{SpanIdBase16}-0" },
|
||||
};
|
||||
Assert.Equal(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter));
|
||||
|
||||
Assert.Equal(
|
||||
new PropagationContext(new ActivityContext(TraceId, SpanId, ActivityTraceFlags.None), null),
|
||||
this.b3FormatSingleHeader.Extract(default, headersNotSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -254,7 +267,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -264,7 +278,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersFlagNotSampled, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -274,7 +289,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -284,7 +300,8 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
{
|
||||
{ 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 PropagationContext(activityContext, null), this.b3FormatSingleHeader.Extract(default, headersEightBytes, Getter));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
// <copyright file="BaggageFormatTest.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Context.Propagation.Tests
|
||||
{
|
||||
public class BaggageFormatTest
|
||||
{
|
||||
private static readonly Func<IDictionary<string, string>, string, IEnumerable<string>> Getter =
|
||||
(d, k) =>
|
||||
{
|
||||
d.TryGetValue(k, out var v);
|
||||
return new string[] { v };
|
||||
};
|
||||
|
||||
private static readonly Func<IList<KeyValuePair<string, string>>, string, IEnumerable<string>> GetterList =
|
||||
(d, k) =>
|
||||
{
|
||||
return d.Where(i => i.Key == k).Select(i => i.Value);
|
||||
};
|
||||
|
||||
private static readonly Action<IDictionary<string, string>, string, string> Setter = (carrier, name, value) =>
|
||||
{
|
||||
carrier[name] = value;
|
||||
};
|
||||
|
||||
private readonly BaggageFormat baggage = new BaggageFormat();
|
||||
|
||||
[Fact]
|
||||
public void ValidateFieldsProperty()
|
||||
{
|
||||
Assert.Equal(new HashSet<string> { BaggageFormat.BaggageHeaderName }, this.baggage.Fields);
|
||||
Assert.Single(this.baggage.Fields);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateDefaultCarrierExtraction()
|
||||
{
|
||||
var propagationContext = this.baggage.Extract<string>(default, null, null);
|
||||
Assert.Equal(default, propagationContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateDefaultGetterExtraction()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>();
|
||||
var propagationContext = this.baggage.Extract(default, carrier, null);
|
||||
Assert.Equal(default, propagationContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateNoBaggageExtraction()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>();
|
||||
var propagationContext = this.baggage.Extract(default, carrier, Getter);
|
||||
Assert.Equal(default, propagationContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateOneBaggageExtraction()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>
|
||||
{
|
||||
{ BaggageFormat.BaggageHeaderName, "name=test" },
|
||||
};
|
||||
var propagationContext = this.baggage.Extract(default, carrier, Getter);
|
||||
Assert.False(propagationContext == default);
|
||||
Assert.Single(propagationContext.ActivityBaggage);
|
||||
|
||||
var array = propagationContext.ActivityBaggage.ToArray();
|
||||
|
||||
Assert.Equal("name", array[0].Key);
|
||||
Assert.Equal("test", array[0].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateMultipleBaggageExtraction()
|
||||
{
|
||||
var carrier = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(BaggageFormat.BaggageHeaderName, "name1=test1"),
|
||||
new KeyValuePair<string, string>(BaggageFormat.BaggageHeaderName, "name2=test2"),
|
||||
new KeyValuePair<string, string>(BaggageFormat.BaggageHeaderName, "name2=test2"),
|
||||
};
|
||||
|
||||
var propagationContext = this.baggage.Extract(default, carrier, GetterList);
|
||||
|
||||
Assert.False(propagationContext == default);
|
||||
Assert.True(propagationContext.ActivityContext == default);
|
||||
|
||||
Assert.Equal(2, propagationContext.ActivityBaggage.Count());
|
||||
|
||||
var array = propagationContext.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()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>
|
||||
{
|
||||
{ BaggageFormat.BaggageHeaderName, $"name={new string('x', 8186)},clientId=1234" },
|
||||
};
|
||||
var propagationContext = this.baggage.Extract(default, carrier, Getter);
|
||||
Assert.False(propagationContext == default);
|
||||
Assert.Single(propagationContext.ActivityBaggage);
|
||||
|
||||
var array = propagationContext.ActivityBaggage.ToArray();
|
||||
|
||||
Assert.Equal("name", array[0].Key);
|
||||
Assert.Equal(new string('x', 8186), array[0].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateEmptyBaggageInjection()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>();
|
||||
this.baggage.Inject(default, carrier, Setter);
|
||||
|
||||
Assert.Empty(carrier);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateBaggageInjection()
|
||||
{
|
||||
var carrier = new Dictionary<string, string>();
|
||||
var propagationContext = new PropagationContext(default, new Dictionary<string, string>
|
||||
{
|
||||
{ "key1", "value1" },
|
||||
{ "key2", "value2" },
|
||||
});
|
||||
|
||||
this.baggage.Inject(propagationContext, carrier, Setter);
|
||||
|
||||
Assert.Single(carrier);
|
||||
Assert.Equal("key1=value1,key2=value2", carrier[BaggageFormat.BaggageHeaderName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ 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<IDictionary<string, string>, string, IEnumerable<string>> Getter = (headers, name) =>
|
||||
{
|
||||
|
|
@ -63,14 +62,13 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
});
|
||||
|
||||
var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null);
|
||||
PropagationContext propagationContext = new PropagationContext(activityContext, null);
|
||||
var carrier = new Dictionary<string, string>();
|
||||
var activity = new Activity("test");
|
||||
|
||||
compositePropagator.Inject(activityContext, 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");
|
||||
|
||||
bool isInjected = compositePropagator.IsInjected(carrier, Getter);
|
||||
Assert.True(isInjected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -86,24 +84,55 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
});
|
||||
|
||||
var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null);
|
||||
PropagationContext propagationContext = new PropagationContext(activityContext, null);
|
||||
|
||||
var carrier = new Dictionary<string, string>();
|
||||
|
||||
compositePropagator.Inject(activityContext, 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.
|
||||
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);
|
||||
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
|
||||
Assert.Equal(2, count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompositePropagator_ActivityContext_Baggage()
|
||||
{
|
||||
var compositePropagator = new CompositePropagator(new List<ITextFormat>
|
||||
{
|
||||
new TraceContextFormat(),
|
||||
new BaggageFormat(),
|
||||
});
|
||||
|
||||
var activityContext = new ActivityContext(this.traceId, this.spanId, ActivityTraceFlags.Recorded, traceState: null, isRemote: true);
|
||||
var baggage = new Dictionary<string, string> { ["key1"] = "value1" };
|
||||
|
||||
PropagationContext propagationContextActivityOnly = new PropagationContext(activityContext, null);
|
||||
PropagationContext propagationContextBaggageOnly = new PropagationContext(default, baggage);
|
||||
PropagationContext propagationContextBoth = new PropagationContext(activityContext, baggage);
|
||||
|
||||
var carrier = new Dictionary<string, string>();
|
||||
compositePropagator.Inject(propagationContextActivityOnly, carrier, Setter);
|
||||
PropagationContext extractedContext = compositePropagator.Extract(default, carrier, Getter);
|
||||
Assert.Equal(propagationContextActivityOnly, extractedContext);
|
||||
|
||||
carrier = new Dictionary<string, string>();
|
||||
compositePropagator.Inject(propagationContextBaggageOnly, carrier, Setter);
|
||||
extractedContext = compositePropagator.Extract(default, carrier, Getter);
|
||||
Assert.Equal(propagationContextBaggageOnly, extractedContext);
|
||||
|
||||
carrier = new Dictionary<string, string>();
|
||||
compositePropagator.Inject(propagationContextBoth, carrier, Setter);
|
||||
extractedContext = compositePropagator.Extract(default, carrier, Getter);
|
||||
Assert.Equal(propagationContextBoth, extractedContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,23 +36,23 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
|
||||
public ISet<string> Fields => new HashSet<string>() { this.idHeaderName, this.stateHeaderName };
|
||||
|
||||
public ActivityContext Extract<T>(ActivityContext activityContext, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
public PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
if (this.defaultContext)
|
||||
{
|
||||
return activityContext;
|
||||
return context;
|
||||
}
|
||||
|
||||
IEnumerable<string> 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;
|
||||
|
|
@ -62,31 +62,25 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
TraceContextFormat.TryExtractTracestate(tracestateCollection.ToArray(), out tracestate);
|
||||
}
|
||||
|
||||
return new ActivityContext(traceId, spanId, traceoptions, tracestate);
|
||||
return new PropagationContext(
|
||||
new ActivityContext(traceId, spanId, traceoptions, tracestate),
|
||||
context.ActivityBaggage);
|
||||
}
|
||||
|
||||
public void Inject<T>(ActivityContext activityContext, T carrier, Action<T, string, string> setter)
|
||||
public void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter)
|
||||
{
|
||||
string headerNumber = this.stateHeaderName.Split('-').Last();
|
||||
|
||||
var traceparent = string.Concat("00-", activityContext.TraceId.ToHexString(), "-", activityContext.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 = activityContext.TraceState;
|
||||
string tracestateStr = context.ActivityContext.TraceState;
|
||||
if (tracestateStr?.Length > 0)
|
||||
{
|
||||
setter(carrier, this.stateHeaderName, tracestateStr);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInjected<T>(T carrier, Func<T, string, IEnumerable<string>> getter)
|
||||
{
|
||||
var traceparentCollection = getter(carrier, this.idHeaderName);
|
||||
|
||||
// There must be a single traceparent
|
||||
return traceparentCollection != null && traceparentCollection.Count() == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
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]
|
||||
|
|
@ -76,12 +76,12 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
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]
|
||||
|
|
@ -92,7 +92,7 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(default, headers, Getter);
|
||||
|
||||
Assert.False(ctx.IsValid());
|
||||
Assert.False(ctx.ActivityContext.IsValid());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -106,7 +106,7 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(default, headers, Getter);
|
||||
|
||||
Assert.False(ctx.IsValid());
|
||||
Assert.False(ctx.ActivityContext.IsValid());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -120,7 +120,7 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(default, headers, Getter);
|
||||
|
||||
Assert.Empty(ctx.TraceState);
|
||||
Assert.Null(ctx.ActivityContext.TraceState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -135,7 +135,7 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
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]
|
||||
|
|
@ -149,9 +149,10 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
};
|
||||
|
||||
var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, traceState: null);
|
||||
PropagationContext propagationContext = new PropagationContext(activityContext, null);
|
||||
var carrier = new Dictionary<string, string>();
|
||||
var f = new TraceContextFormat();
|
||||
f.Inject(activityContext, carrier, Setter);
|
||||
f.Inject(propagationContext, carrier, Setter);
|
||||
|
||||
Assert.Equal(expectedHeaders, carrier);
|
||||
}
|
||||
|
|
@ -168,9 +169,10 @@ namespace OpenTelemetry.Context.Propagation.Tests
|
|||
};
|
||||
|
||||
var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, expectedHeaders[TraceState]);
|
||||
PropagationContext propagationContext = new PropagationContext(activityContext, null);
|
||||
var carrier = new Dictionary<string, string>();
|
||||
var f = new TraceContextFormat();
|
||||
f.Inject(activityContext, carrier, Setter);
|
||||
f.Inject(propagationContext, carrier, Setter);
|
||||
|
||||
Assert.Equal(expectedHeaders, carrier);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ using Xunit;
|
|||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
{
|
||||
public class TracerProvideSdkTest
|
||||
public class TracerProvideSdkTest : IDisposable
|
||||
{
|
||||
private const string ActivitySourceName = "TraceSdkTest";
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ namespace OpenTelemetry.Trace.Tests
|
|||
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 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[Fact]
|
||||
public void TracerProvideSdkCreatesActivitySource()
|
||||
{
|
||||
TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
|
|
@ -277,6 +277,11 @@ namespace OpenTelemetry.Trace.Tests
|
|||
Assert.True(testInstrumentation.IsDisposed);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private class TestSampler : Sampler
|
||||
{
|
||||
public SamplingResult DesiredSamplingResult { get; set; } = new SamplingResult(SamplingDecision.RecordAndSampled);
|
||||
|
|
|
|||
|
|
@ -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 @@ namespace OpenTelemetry.Trace.Tests
|
|||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
this.tracerProvider.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static bool IsNoopSpan(TelemetrySpan span)
|
||||
|
|
|
|||
Loading…
Reference in New Issue