Support .NET Activities (#140)
* StartSpanWithActivity * Sampler builder tests * cleanup start span * span tests * optimize tracestate * public tracestate parsing * more tracestate tests * Scopes and scope tests * minor merge issues * Do not make Span's Activity Current until WithSpan is called * CurrentSpanUtils to static * Tracestate parsing is not public API, reuse code without exposing it * more tests and fixes * rename * rename FromCurrentActivity to SetCreateChild * fix rebase issues * fix build warning * undo separate test project for Abstractions.Tests to fix build * up
This commit is contained in:
parent
1ae543c575
commit
c5f33e1742
|
|
@ -20,9 +20,8 @@ namespace OpenTelemetry.Context.Propagation
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenTelemetry.Abstractions.Context.Propagation;
|
||||
using OpenTelemetry.Trace;
|
||||
using OpenTelemetry.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// W3C trace context text wire protocol formatter. See https://github.com/w3c/distributed-tracing/.
|
||||
|
|
@ -63,97 +62,13 @@ namespace OpenTelemetry.Context.Propagation
|
|||
return SpanContext.Blank;
|
||||
}
|
||||
|
||||
var tracestateResult = Tracestate.Empty;
|
||||
try
|
||||
var tracestate = Tracestate.Empty;
|
||||
if (tracestateCollection != null)
|
||||
{
|
||||
var entries = new List<KeyValuePair<string, string>>();
|
||||
var names = new HashSet<string>();
|
||||
var discardTracestate = false;
|
||||
if (tracestateCollection != null)
|
||||
{
|
||||
foreach (var tracestate in tracestateCollection)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tracestate))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// tracestate: rojo=00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01,congo=BleGNlZWRzIHRohbCBwbGVhc3VyZS4
|
||||
var keyStartIdx = 0;
|
||||
var length = tracestate.Length;
|
||||
while (keyStartIdx < length)
|
||||
{
|
||||
// first skip any prefix commas and OWS
|
||||
var c = tracestate[keyStartIdx];
|
||||
while (c == ' ' || c == '\t' || c == ',')
|
||||
{
|
||||
keyStartIdx++;
|
||||
if (keyStartIdx == length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
c = tracestate[keyStartIdx];
|
||||
}
|
||||
|
||||
if (keyStartIdx == length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var keyEndIdx = tracestate.IndexOf("=", keyStartIdx);
|
||||
|
||||
if (keyEndIdx == -1)
|
||||
{
|
||||
discardTracestate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var valueStartIdx = keyEndIdx + 1;
|
||||
|
||||
var valueEndIdx = tracestate.IndexOf(",", valueStartIdx);
|
||||
valueEndIdx = valueEndIdx == -1 ? length : valueEndIdx;
|
||||
|
||||
// this will throw for duplicated keys
|
||||
var key = tracestate.Substring(keyStartIdx, keyEndIdx - keyStartIdx).TrimStart();
|
||||
if (names.Add(key))
|
||||
{
|
||||
entries.Add(
|
||||
new KeyValuePair<string, string>(
|
||||
key,
|
||||
tracestate.Substring(valueStartIdx, valueEndIdx - valueStartIdx).TrimEnd()));
|
||||
}
|
||||
else
|
||||
{
|
||||
discardTracestate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
keyStartIdx = valueEndIdx + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!discardTracestate)
|
||||
{
|
||||
var tracestateBuilder = Tracestate.Builder;
|
||||
|
||||
entries.Reverse();
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
tracestateBuilder.Set(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
tracestateResult = tracestateBuilder.Build();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// failure to parse tracestate should not disregard traceparent
|
||||
// TODO: logging
|
||||
this.TryExtractTracestate(tracestateCollection.ToArray(), out tracestate);
|
||||
}
|
||||
|
||||
return SpanContext.Create(traceId, spanId, traceoptions, tracestateResult);
|
||||
return SpanContext.Create(traceId, spanId, traceoptions, tracestate);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
|
@ -172,26 +87,10 @@ namespace OpenTelemetry.Context.Propagation
|
|||
|
||||
setter(carrier, "traceparent", traceparent);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var isFirst = true;
|
||||
|
||||
foreach (var entry in spanContext.Tracestate.Entries)
|
||||
string tracestateStr = spanContext.Tracestate.ToString();
|
||||
if (tracestateStr.Length > 0)
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
isFirst = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(",");
|
||||
}
|
||||
|
||||
sb.Append(entry.Key).Append("=").Append(entry.Value);
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
setter(carrier, "tracestate", sb.ToString());
|
||||
setter(carrier, "tracestate", tracestateStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +109,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
return false;
|
||||
}
|
||||
|
||||
// if version does not end with delimeter
|
||||
// if version does not end with delimiter
|
||||
if (traceparent[VersionPrefixIdLength - 1] != '-')
|
||||
{
|
||||
return false;
|
||||
|
|
@ -268,7 +167,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
try
|
||||
{
|
||||
options0 = this.HexCharToByte(traceparent[VersionAndTraceIdAndSpanIdLength]);
|
||||
options1 = this.HexCharToByte(traceparent[VersionAndTraceIdAndSpanIdLength]);
|
||||
options1 = this.HexCharToByte(traceparent[VersionAndTraceIdAndSpanIdLength + 1]);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
|
|
@ -276,7 +175,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((options0 | 1) == 1)
|
||||
if ((options1 & 1) == 1)
|
||||
{
|
||||
traceoptions |= ActivityTraceFlags.Recorded;
|
||||
}
|
||||
|
|
@ -316,5 +215,37 @@ namespace OpenTelemetry.Context.Propagation
|
|||
|
||||
throw new ArgumentOutOfRangeException("Invalid character: " + c);
|
||||
}
|
||||
|
||||
private bool TryExtractTracestate(string[] tracestateCollection, out Tracestate tracestateResult)
|
||||
{
|
||||
tracestateResult = Tracestate.Empty;
|
||||
var tracestateBuilder = Tracestate.Builder;
|
||||
try
|
||||
{
|
||||
var names = new HashSet<string>();
|
||||
if (tracestateCollection != null)
|
||||
{
|
||||
// Iterate in reverse order because when call builder set the elements is added in the
|
||||
// front of the list.
|
||||
for (int i = tracestateCollection.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (!TracestateUtils.TryExtractTracestate(tracestateCollection[i], tracestateBuilder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tracestateResult = tracestateBuilder.Build();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// failure to parse tracestate should not disregard traceparent
|
||||
// TODO: logging
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
// <copyright file="TracestateUtils.cs" company="OpenTelemetry Authors">
|
||||
// Copyright 2018, 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 ABSTRACTIONS
|
||||
namespace OpenTelemetry.Abstractions.Context.Propagation
|
||||
#else
|
||||
namespace OpenTelemetry.Context.Propagation
|
||||
#endif
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods to extract Tracestate from string.
|
||||
/// </summary>
|
||||
internal static class TracestateUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Extracts <see cref="Tracestate"/> from the given string and sets it on provided <see cref="Tracestate.TracestateBuilder"/>.
|
||||
/// </summary>
|
||||
/// <param name="tracestateString">String with comma separated tracestate key value pairs.</param>
|
||||
/// <param name="tracestateBuilder"><see cref="Tracestate.TracestateBuilder"/> to set tracestate pairs on.</param>
|
||||
/// <returns>True if string was parsed successfully and tracestate was recognized, false otherwise.</returns>
|
||||
internal static bool TryExtractTracestate(string tracestateString, Tracestate.TracestateBuilder tracestateBuilder)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tracestateString))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var names = new HashSet<string>();
|
||||
|
||||
var tracestate = tracestateString.AsSpan().Trim(' ').Trim(',');
|
||||
do
|
||||
{
|
||||
// tracestate: rojo=00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01,congo=BleGNlZWRzIHRohbCBwbGVhc3VyZS4
|
||||
|
||||
// Iterate in reverse order because when call builder set the elements is added in the
|
||||
// front of the list.
|
||||
int pairStart = tracestate.LastIndexOf(',') + 1;
|
||||
|
||||
if (!TryParseKeyValue(tracestate.Slice(pairStart, tracestate.Length - pairStart), out var key, out var value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var keyStr = key.ToString();
|
||||
if (names.Add(keyStr))
|
||||
{
|
||||
tracestateBuilder.Set(keyStr, value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pairStart == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
tracestate = tracestate.Slice(0, pairStart - 1);
|
||||
}
|
||||
while (tracestate.Length > 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// failure to parse tracestate
|
||||
// TODO: logging
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryParseKeyValue(ReadOnlySpan<char> pair, out ReadOnlySpan<char> key, out ReadOnlySpan<char> value)
|
||||
{
|
||||
key = default;
|
||||
value = default;
|
||||
|
||||
var keyEndIdx = pair.IndexOf('=');
|
||||
if (keyEndIdx <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var valueStartIdx = keyEndIdx + 1;
|
||||
if (valueStartIdx >= pair.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
key = pair.Slice(0, keyEndIdx).Trim();
|
||||
value = pair.Slice(valueStartIdx, pair.Length - valueStartIdx).Trim();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
<TargetFrameworks Condition="$(OS) != 'Windows_NT'">netstandard2.0</TargetFrameworks>
|
||||
<Description>OpenTelemetry .NET API abstractions</Description>
|
||||
<RootNamespace>OpenTelemetry</RootNamespace>
|
||||
<DefineConstants>$(DefineConstants);ABSTRACTIONS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// Span builder.
|
||||
|
|
@ -38,6 +39,14 @@ namespace OpenTelemetry.Trace
|
|||
/// <returns>This span builder for chaining.</returns>
|
||||
ISpanBuilder SetParent(ISpan parent);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Activity"/> to use as a parent for the new span.
|
||||
/// Any parent that was set previously will be discarded.
|
||||
/// </summary>
|
||||
/// <param name="parent"><see cref="Activity"/> to set as parent.</param>
|
||||
/// <returns>This span builder for chaining.</returns>
|
||||
ISpanBuilder SetParent(Activity parent);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the remote <see cref="SpanContext"/> to use as a parent for the new span.
|
||||
/// Any parent that was set previously will be discarded.
|
||||
|
|
@ -53,6 +62,17 @@ namespace OpenTelemetry.Trace
|
|||
/// <returns>This span builder for chaining.</returns>
|
||||
ISpanBuilder SetNoParent();
|
||||
|
||||
/// <summary>
|
||||
/// Sets flag indicating that new span should become a child of implicit context (Activity.Current)
|
||||
/// or continue run in this context and inherit it
|
||||
/// Use this method with value 'false' in auto-collectors when library is instrumented with Activities.
|
||||
/// Any parent that was set previously will be discarded.
|
||||
/// </summary>
|
||||
/// <param name="createChild">If true, a new span will become a child of the existing implicit context.
|
||||
/// If false, new span will continue this context.</param>
|
||||
/// <returns>This span builder for chaining.</returns>
|
||||
ISpanBuilder SetCreateChild(bool createChild);
|
||||
|
||||
/// <summary>
|
||||
/// Set <see cref="SpanKind"/> on the span.
|
||||
/// </summary>
|
||||
|
|
@ -63,10 +83,17 @@ namespace OpenTelemetry.Trace
|
|||
/// <summary>
|
||||
/// Set the <see cref="Link"/> on the span.
|
||||
/// </summary>
|
||||
/// <param name="spanContext"><see cref="Link"/> context to set on span.</param>
|
||||
/// <param name="spanContext"><see cref="SpanContext"/> context to set on span.</param>
|
||||
/// <returns>This span builder for chaining.</returns>
|
||||
ISpanBuilder AddLink(SpanContext spanContext);
|
||||
|
||||
/// <summary>
|
||||
/// Set the <see cref="Link"/> on the span.
|
||||
/// </summary>
|
||||
/// <param name="activity"><see cref="Activity"/> context to set on span.</param>
|
||||
/// <returns>This span builder for chaining.</returns>
|
||||
ISpanBuilder AddLink(Activity activity);
|
||||
|
||||
/// <summary>
|
||||
/// Set the <see cref="Link"/> on the span.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ namespace OpenTelemetry.Trace
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using OpenTelemetry.Abstractions.Context.Propagation;
|
||||
using OpenTelemetry.Abstractions.Utils;
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -39,6 +41,25 @@ namespace OpenTelemetry.Trace
|
|||
/// <inheritdoc/>
|
||||
public IDictionary<string, object> Attributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ILink"/> from Activity.
|
||||
/// </summary>
|
||||
/// <param name="activity">Activity to create link from.</param>
|
||||
/// <returns>New <see cref="ILink"/> instance.</returns>
|
||||
public static ILink FromActivity(Activity activity)
|
||||
{
|
||||
var tracestate = Tracestate.Empty;
|
||||
var tracestateBuilder = Tracestate.Builder;
|
||||
if (TracestateUtils.TryExtractTracestate(activity.TraceStateString, tracestateBuilder))
|
||||
{
|
||||
tracestate = tracestateBuilder.Build();
|
||||
}
|
||||
|
||||
return new Link(
|
||||
SpanContext.Create(activity.TraceId, activity.SpanId, activity.ActivityTraceFlags, tracestate),
|
||||
EmptyAttributes);
|
||||
}
|
||||
|
||||
public static ILink FromSpanContext(SpanContext context)
|
||||
{
|
||||
return new Link(context, EmptyAttributes);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace OpenTelemetry.Trace
|
|||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// No-op span builder.
|
||||
|
|
@ -56,11 +57,22 @@ namespace OpenTelemetry.Trace
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetParent(ISpan parent)
|
||||
public ISpanBuilder SetParent(ISpan parentSpan)
|
||||
{
|
||||
if (parent == null)
|
||||
if (parentSpan == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parent));
|
||||
throw new ArgumentNullException(nameof(parentSpan));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetParent(Activity parentActivity)
|
||||
{
|
||||
if (parentActivity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parentActivity));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
@ -83,6 +95,33 @@ namespace OpenTelemetry.Trace
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetCreateChild(bool createChild)
|
||||
{
|
||||
if (!createChild)
|
||||
{
|
||||
var currentActivity = Activity.Current;
|
||||
|
||||
if (currentActivity == null)
|
||||
{
|
||||
throw new ArgumentException("Current Activity cannot be null");
|
||||
}
|
||||
|
||||
if (currentActivity.IdFormat != ActivityIdFormat.W3C)
|
||||
{
|
||||
throw new ArgumentException("Current Activity is not in W3C format");
|
||||
}
|
||||
|
||||
if (currentActivity.StartTimeUtc == default || currentActivity.Duration != default)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Current Activity is not running: it has not been started or has been stopped");
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetSpanKind(SpanKind spanKind)
|
||||
{
|
||||
|
|
@ -100,6 +139,17 @@ namespace OpenTelemetry.Trace
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder AddLink(Activity activity)
|
||||
{
|
||||
if (activity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activity));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder AddLink(ILink link)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ namespace OpenTelemetry.Trace
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Tracestate entries allowing different vendors to participate in a trace.
|
||||
|
|
@ -89,6 +90,35 @@ namespace OpenTelemetry.Trace
|
|||
return new TracestateBuilder(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.Entries.Any())
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
var isFirst = true;
|
||||
|
||||
foreach (var entry in this.Entries)
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
isFirst = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(",");
|
||||
}
|
||||
|
||||
sb.Append(entry.Key).Append("=").Append(entry.Value);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static bool ValidateKey(string key)
|
||||
{
|
||||
// Key is opaque string up to 256 characters printable. It MUST begin with a lowercase letter, and
|
||||
|
|
@ -189,7 +219,6 @@ namespace OpenTelemetry.Trace
|
|||
private static Tracestate Create(ICollection<Entry> entries)
|
||||
{
|
||||
// TODO: discard last entries instead of throwing
|
||||
|
||||
if (entries.Count > MaxKeyValuePairsCount)
|
||||
{
|
||||
throw new ArgumentException("Too many entries.", nameof(entries));
|
||||
|
|
@ -273,8 +302,12 @@ namespace OpenTelemetry.Trace
|
|||
/// <returns>Tracestate builder for chained calls.</returns>
|
||||
public TracestateBuilder Set(string key, string value)
|
||||
{
|
||||
// Initially create the Entry to validate input.
|
||||
if (this.entries != null && this.entries.Count > MaxKeyValuePairsCount)
|
||||
{
|
||||
throw new ArgumentException("Too many entries.");
|
||||
}
|
||||
|
||||
// Initially create the Entry to validate input.
|
||||
var entry = Entry.Create(key, value);
|
||||
|
||||
if (this.entries == null)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
<CodeAnalysisRuleSet>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'OpenTelemetry.sln'))/build/OpenTelemetry.prod.loose.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\OpenTelemetry.Abstractions\Context\Propagation\TracestateUtils.cs" Link="TracestateUtils.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenTelemetry.Abstractions\OpenTelemetry.Abstractions.csproj" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
|
||||
|
|
|
|||
|
|
@ -16,24 +16,62 @@
|
|||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using OpenTelemetry.Context;
|
||||
using OpenTelemetry.Trace.Internal;
|
||||
|
||||
internal static class CurrentSpanUtils
|
||||
{
|
||||
private static readonly AsyncLocal<ISpan> AsyncLocalContext = new AsyncLocal<ISpan>();
|
||||
private static readonly ConditionalWeakTable<Activity, ISpan> ActivitySpanTable = new ConditionalWeakTable<Activity, ISpan>();
|
||||
|
||||
public static ISpan CurrentSpan => AsyncLocalContext.Value;
|
||||
public static ISpan CurrentSpan
|
||||
{
|
||||
get
|
||||
{
|
||||
var currentActivity = Activity.Current;
|
||||
if (currentActivity == null)
|
||||
{
|
||||
return BlankSpan.Instance;
|
||||
}
|
||||
|
||||
if (ActivitySpanTable.TryGetValue(currentActivity, out var currentSpan))
|
||||
{
|
||||
return currentSpan;
|
||||
}
|
||||
|
||||
return BlankSpan.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static IScope WithSpan(ISpan span, bool endSpan)
|
||||
{
|
||||
return new ScopeInSpan(span, endSpan);
|
||||
}
|
||||
|
||||
private static void SetSpan(Span span)
|
||||
{
|
||||
if (span.Activity == null)
|
||||
{
|
||||
// log error
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActivitySpanTable.TryGetValue(span.Activity, out _))
|
||||
{
|
||||
// log warning
|
||||
return;
|
||||
}
|
||||
|
||||
ActivitySpanTable.Add(span.Activity, span);
|
||||
}
|
||||
|
||||
private static void DetachSpanFromActivity(Activity activity)
|
||||
{
|
||||
ActivitySpanTable.Remove(activity);
|
||||
}
|
||||
|
||||
private sealed class ScopeInSpan : IScope
|
||||
{
|
||||
private readonly ISpan origContext;
|
||||
private readonly ISpan span;
|
||||
private readonly bool endSpan;
|
||||
|
||||
|
|
@ -41,24 +79,42 @@ namespace OpenTelemetry.Trace
|
|||
{
|
||||
this.span = span;
|
||||
this.endSpan = endSpan;
|
||||
this.origContext = AsyncLocalContext.Value;
|
||||
AsyncLocalContext.Value = span;
|
||||
|
||||
if (span is Span spanImpl)
|
||||
{
|
||||
if (spanImpl.OwnsActivity)
|
||||
{
|
||||
Activity.Current = spanImpl.Activity;
|
||||
}
|
||||
|
||||
SetSpan(spanImpl);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var current = AsyncLocalContext.Value;
|
||||
AsyncLocalContext.Value = this.origContext;
|
||||
|
||||
if (current != this.origContext)
|
||||
bool safeToStopActivity = false;
|
||||
var current = (Span)this.span;
|
||||
if (current != null && current.Activity == Activity.Current)
|
||||
{
|
||||
// Log
|
||||
if (!current.OwnsActivity)
|
||||
{
|
||||
DetachSpanFromActivity(current.Activity);
|
||||
}
|
||||
else
|
||||
{
|
||||
safeToStopActivity = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.endSpan)
|
||||
{
|
||||
this.span.End();
|
||||
}
|
||||
else if (safeToStopActivity)
|
||||
{
|
||||
current.Activity.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ namespace OpenTelemetry.Trace
|
|||
/// </summary>
|
||||
public sealed class Span : ISpan, IElement<Span>
|
||||
{
|
||||
private readonly ActivitySpanId parentSpanId;
|
||||
private readonly ITraceParams traceParams;
|
||||
private readonly IStartEndHandler startEndHandler;
|
||||
private readonly DateTimeOffset startTime;
|
||||
|
|
@ -43,20 +42,26 @@ namespace OpenTelemetry.Trace
|
|||
private DateTimeOffset endTime;
|
||||
private bool hasBeenEnded;
|
||||
private bool sampleToLocalSpanStore;
|
||||
private Lazy<SpanContext> spanContext;
|
||||
|
||||
private Span(
|
||||
SpanContext context,
|
||||
Activity activity,
|
||||
Tracestate tracestate,
|
||||
SpanOptions options,
|
||||
string name,
|
||||
SpanKind spanKind,
|
||||
ActivitySpanId parentSpanId,
|
||||
ITraceParams traceParams,
|
||||
IStartEndHandler startEndHandler,
|
||||
Timer timestampConverter)
|
||||
Timer timestampConverter,
|
||||
bool stopActivity)
|
||||
{
|
||||
this.Context = context;
|
||||
this.Activity = activity;
|
||||
this.spanContext = new Lazy<SpanContext>(() => SpanContext.Create(
|
||||
this.Activity.TraceId,
|
||||
this.Activity.SpanId,
|
||||
this.Activity.ActivityTraceFlags,
|
||||
tracestate));
|
||||
this.Options = options;
|
||||
this.parentSpanId = parentSpanId;
|
||||
this.Name = name;
|
||||
this.traceParams = traceParams ?? throw new ArgumentNullException(nameof(traceParams));
|
||||
this.startEndHandler = startEndHandler;
|
||||
|
|
@ -81,9 +86,13 @@ namespace OpenTelemetry.Trace
|
|||
this.startTime = DateTimeOffset.MinValue;
|
||||
this.TimestampConverter = timestampConverter;
|
||||
}
|
||||
|
||||
this.OwnsActivity = stopActivity;
|
||||
}
|
||||
|
||||
public SpanContext Context { get; }
|
||||
public Activity Activity { get; }
|
||||
|
||||
public SpanContext Context => this.spanContext.Value;
|
||||
|
||||
public SpanOptions Options { get; }
|
||||
|
||||
|
|
@ -172,7 +181,7 @@ namespace OpenTelemetry.Trace
|
|||
}
|
||||
}
|
||||
|
||||
public ActivitySpanId ParentSpanId => this.parentSpanId;
|
||||
public ActivitySpanId ParentSpanId => this.Activity.ParentSpanId;
|
||||
|
||||
public bool HasEnded => this.hasBeenEnded;
|
||||
|
||||
|
|
@ -184,6 +193,8 @@ namespace OpenTelemetry.Trace
|
|||
/// </summary>
|
||||
internal SpanKind? Kind { get; set; }
|
||||
|
||||
internal bool OwnsActivity { get; }
|
||||
|
||||
internal Timer TimestampConverter { get; private set; }
|
||||
|
||||
private AttributesWithCapacity InitializedAttributes
|
||||
|
|
@ -377,6 +388,12 @@ namespace OpenTelemetry.Trace
|
|||
/// <inheritdoc/>
|
||||
public void End()
|
||||
{
|
||||
if (this.OwnsActivity && this.Activity == Activity.Current)
|
||||
{
|
||||
// TODO log if current is not span activity
|
||||
this.Activity.Stop();
|
||||
}
|
||||
|
||||
if (!this.IsRecordingEvents)
|
||||
{
|
||||
return;
|
||||
|
|
@ -411,8 +428,8 @@ namespace OpenTelemetry.Trace
|
|||
var linksSpanData = this.links == null ? LinkList.Create(new List<ILink>(), 0) : LinkList.Create(this.links.Events, this.links.NumberOfDroppedEvents);
|
||||
|
||||
return SpanData.Create(
|
||||
this.Context,
|
||||
this.parentSpanId,
|
||||
this.Context, // TODO avoid using context, use Activity instead
|
||||
this.ParentSpanId,
|
||||
Resource.Empty, // TODO: determine what to do with Resource in this context
|
||||
this.Name,
|
||||
Timestamp.FromDateTimeOffset(this.startTime),
|
||||
|
|
@ -495,24 +512,26 @@ namespace OpenTelemetry.Trace
|
|||
}
|
||||
|
||||
internal static ISpan StartSpan(
|
||||
SpanContext context,
|
||||
Activity activity,
|
||||
Tracestate tracestate,
|
||||
SpanOptions options,
|
||||
string name,
|
||||
SpanKind spanKind,
|
||||
ActivitySpanId parentSpanId,
|
||||
ITraceParams traceParams,
|
||||
IStartEndHandler startEndHandler,
|
||||
Timer timestampConverter)
|
||||
Timer timestampConverter,
|
||||
bool ownsActivity = true)
|
||||
{
|
||||
var span = new Span(
|
||||
context,
|
||||
activity,
|
||||
tracestate,
|
||||
options,
|
||||
name,
|
||||
spanKind,
|
||||
parentSpanId,
|
||||
traceParams,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
timestampConverter,
|
||||
ownsActivity);
|
||||
|
||||
// Call onStart here instead of calling in the constructor to make sure the span is completely
|
||||
// initialized.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ namespace OpenTelemetry.Trace
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Context.Propagation;
|
||||
using OpenTelemetry.Internal;
|
||||
using OpenTelemetry.Trace.Config;
|
||||
|
||||
|
|
@ -29,9 +30,11 @@ namespace OpenTelemetry.Trace
|
|||
private readonly string name;
|
||||
|
||||
private SpanKind kind;
|
||||
private ISpan parent;
|
||||
private ISpan parentSpan;
|
||||
private Activity parentActivity;
|
||||
private Activity fromActivity;
|
||||
private SpanContext parentSpanContext;
|
||||
private ParentType parentType = ParentType.CurrentSpan;
|
||||
private ContextSource contextSource = ContextSource.CurrentActivityParent;
|
||||
private ISampler sampler;
|
||||
private List<ILink> links;
|
||||
private bool recordEvents;
|
||||
|
|
@ -39,14 +42,20 @@ namespace OpenTelemetry.Trace
|
|||
|
||||
internal SpanBuilder(string name, SpanBuilderOptions options)
|
||||
{
|
||||
// TODO: remove with next DiagnosticSource preview, switch to Activity setidformat
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
|
||||
this.name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
this.options = options;
|
||||
this.options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
private enum ParentType
|
||||
private enum ContextSource
|
||||
{
|
||||
CurrentSpan,
|
||||
ExplicitParent,
|
||||
CurrentActivityParent,
|
||||
Activity,
|
||||
ExplicitActivityParent,
|
||||
ExplicitSpanParent,
|
||||
ExplicitRemoteParent,
|
||||
NoParent,
|
||||
}
|
||||
|
|
@ -59,12 +68,27 @@ namespace OpenTelemetry.Trace
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetParent(ISpan parent)
|
||||
public ISpanBuilder SetParent(ISpan parentSpan)
|
||||
{
|
||||
this.parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
this.parentType = ParentType.ExplicitParent;
|
||||
this.timestampConverter = ((Span)parent)?.TimestampConverter;
|
||||
this.parentSpan = parentSpan ?? throw new ArgumentNullException(nameof(parentSpan));
|
||||
this.contextSource = ContextSource.ExplicitSpanParent;
|
||||
if (parentSpan is Span parentSpanImpl)
|
||||
{
|
||||
this.timestampConverter = parentSpanImpl.TimestampConverter;
|
||||
}
|
||||
|
||||
this.parentSpanContext = null;
|
||||
this.parentActivity = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetParent(Activity parentActivity)
|
||||
{
|
||||
this.parentActivity = parentActivity ?? throw new ArgumentNullException(nameof(parentActivity));
|
||||
this.contextSource = ContextSource.ExplicitActivityParent;
|
||||
this.parentSpanContext = null;
|
||||
this.parentSpan = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -72,17 +96,56 @@ namespace OpenTelemetry.Trace
|
|||
public ISpanBuilder SetParent(SpanContext remoteParent)
|
||||
{
|
||||
this.parentSpanContext = remoteParent ?? throw new ArgumentNullException(nameof(remoteParent));
|
||||
this.parent = null;
|
||||
this.parentType = ParentType.ExplicitRemoteParent;
|
||||
this.parentSpan = null;
|
||||
this.parentActivity = null;
|
||||
this.contextSource = ContextSource.ExplicitRemoteParent;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetNoParent()
|
||||
{
|
||||
this.parentType = ParentType.NoParent;
|
||||
this.contextSource = ContextSource.NoParent;
|
||||
this.parentSpanContext = null;
|
||||
this.parentSpanContext = null;
|
||||
this.parentActivity = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ISpanBuilder SetCreateChild(bool createChild)
|
||||
{
|
||||
if (!createChild)
|
||||
{
|
||||
var currentActivity = Activity.Current;
|
||||
|
||||
if (currentActivity == null)
|
||||
{
|
||||
throw new ArgumentException("Current Activity cannot be null");
|
||||
}
|
||||
|
||||
if (currentActivity.IdFormat != ActivityIdFormat.W3C)
|
||||
{
|
||||
throw new ArgumentException("Current Activity is not in W3C format");
|
||||
}
|
||||
|
||||
if (currentActivity.StartTimeUtc == default || currentActivity.Duration != default)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Current Activity is not running: it has not been started or has been stopped");
|
||||
}
|
||||
|
||||
this.fromActivity = currentActivity;
|
||||
this.contextSource = ContextSource.Activity;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.contextSource = ContextSource.CurrentActivityParent;
|
||||
}
|
||||
|
||||
this.parentSpanContext = null;
|
||||
this.parentSpanContext = null;
|
||||
this.parentActivity = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -138,6 +201,17 @@ namespace OpenTelemetry.Trace
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder AddLink(Activity activity)
|
||||
{
|
||||
if (activity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activity));
|
||||
}
|
||||
|
||||
return this.AddLink(Link.FromActivity(activity));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpanBuilder SetRecordEvents(bool recordEvents)
|
||||
{
|
||||
|
|
@ -148,56 +222,47 @@ namespace OpenTelemetry.Trace
|
|||
/// <inheritdoc/>
|
||||
public ISpan StartSpan()
|
||||
{
|
||||
SpanContext parentContext = FindParent(this.parentType, this.parent, this.parentSpanContext);
|
||||
var activityForSpan = this.CreateActivityForSpan(this.contextSource, this.parentSpan,
|
||||
this.parentSpanContext, this.parentActivity, this.fromActivity);
|
||||
var activeTraceParams = this.options.TraceConfig.ActiveTraceParams;
|
||||
ActivityTraceId traceId;
|
||||
var spanId = ActivitySpanId.CreateRandom();
|
||||
|
||||
ActivitySpanId parentSpanId = default;
|
||||
ActivityTraceFlags traceOptions = ActivityTraceFlags.None;
|
||||
|
||||
if (parentContext == null || !parentContext.IsValid)
|
||||
{
|
||||
// New root span.
|
||||
traceId = ActivityTraceId.CreateRandom();
|
||||
}
|
||||
else
|
||||
{
|
||||
// New child span.
|
||||
traceId = parentContext.TraceId;
|
||||
parentSpanId = parentContext.SpanId;
|
||||
traceOptions = parentContext.TraceOptions;
|
||||
}
|
||||
|
||||
bool sampledIn = MakeSamplingDecision(
|
||||
parentContext,
|
||||
this.name,
|
||||
this.sampler,
|
||||
this.links,
|
||||
traceId,
|
||||
spanId,
|
||||
activeTraceParams);
|
||||
this.parentSpanContext, // it is updated in CreateActivityForSpan
|
||||
this.name,
|
||||
this.sampler,
|
||||
this.links,
|
||||
activityForSpan.TraceId,
|
||||
activityForSpan.SpanId,
|
||||
activeTraceParams);
|
||||
|
||||
var spanOptions = SpanOptions.None;
|
||||
if (sampledIn || this.recordEvents)
|
||||
{
|
||||
traceOptions |= ActivityTraceFlags.Recorded;
|
||||
activityForSpan.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
spanOptions = SpanOptions.RecordEvents;
|
||||
}
|
||||
else
|
||||
{
|
||||
traceOptions &= ~ActivityTraceFlags.Recorded;
|
||||
activityForSpan.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
|
||||
}
|
||||
|
||||
var childTracestate = Tracestate.Empty;
|
||||
|
||||
if (this.parentSpanContext?.Tracestate != null && this.parentSpanContext.Tracestate != Tracestate.Empty)
|
||||
{
|
||||
childTracestate = this.parentSpanContext.Tracestate.ToBuilder().Build();
|
||||
}
|
||||
|
||||
var span = Span.StartSpan(
|
||||
SpanContext.Create(traceId, spanId, traceOptions, parentContext?.Tracestate ?? Tracestate.Empty),
|
||||
activityForSpan,
|
||||
childTracestate, // it is updated in CreateActivityForSpan,
|
||||
spanOptions,
|
||||
this.name,
|
||||
this.kind,
|
||||
parentSpanId,
|
||||
activeTraceParams,
|
||||
this.options.StartEndHandler,
|
||||
this.timestampConverter);
|
||||
this.timestampConverter,
|
||||
ownsActivity: this.contextSource != ContextSource.Activity);
|
||||
LinkSpans(span, this.links);
|
||||
return span;
|
||||
}
|
||||
|
|
@ -257,22 +322,124 @@ namespace OpenTelemetry.Trace
|
|||
return (parent.TraceOptions & ActivityTraceFlags.Recorded) != 0 || IsAnyParentLinkSampled(parentLinks);
|
||||
}
|
||||
|
||||
private static SpanContext FindParent(ParentType parentType, ISpan explicitParent, SpanContext remoteParent)
|
||||
private static SpanContext ParentContextFromActivity(Activity activity)
|
||||
{
|
||||
switch (parentType)
|
||||
var tracestate = Tracestate.Empty;
|
||||
var tracestateBuilder = Tracestate.Builder;
|
||||
if (TracestateUtils.TryExtractTracestate(activity.TraceStateString, tracestateBuilder))
|
||||
{
|
||||
case ParentType.NoParent:
|
||||
return null;
|
||||
case ParentType.CurrentSpan:
|
||||
ISpan currentSpan = CurrentSpanUtils.CurrentSpan;
|
||||
return currentSpan?.Context;
|
||||
case ParentType.ExplicitParent:
|
||||
return explicitParent?.Context;
|
||||
case ParentType.ExplicitRemoteParent:
|
||||
return remoteParent;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown parentType {parentType}");
|
||||
tracestate = tracestateBuilder.Build();
|
||||
}
|
||||
|
||||
return SpanContext.Create(
|
||||
activity.TraceId,
|
||||
activity.ParentSpanId,
|
||||
ActivityTraceFlags.Recorded,
|
||||
tracestate);
|
||||
}
|
||||
|
||||
private Activity CreateActivityForSpan(ContextSource contextSource, ISpan explicitParent, SpanContext remoteParent, Activity explicitParentActivity, Activity fromActivity)
|
||||
{
|
||||
Activity spanActivity = null;
|
||||
Activity originalActivity = Activity.Current;
|
||||
bool needRestoreOriginal = true;
|
||||
|
||||
switch (contextSource)
|
||||
{
|
||||
case ContextSource.CurrentActivityParent:
|
||||
{
|
||||
// Activity will figure out its parent
|
||||
spanActivity = new Activity(this.name).Start();
|
||||
|
||||
// chances are, Activity.Current has span attached
|
||||
if (CurrentSpanUtils.CurrentSpan is Span currentSpan)
|
||||
{
|
||||
this.parentSpanContext = currentSpan.Context;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.parentSpanContext = ParentContextFromActivity(spanActivity);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ContextSource.ExplicitActivityParent:
|
||||
{
|
||||
spanActivity = new Activity(this.name)
|
||||
.SetParentId(this.parentActivity.TraceId,
|
||||
this.parentActivity.SpanId,
|
||||
this.parentActivity.ActivityTraceFlags)
|
||||
.Start();
|
||||
spanActivity.TraceStateString = this.parentActivity.TraceStateString;
|
||||
this.parentSpanContext = ParentContextFromActivity(spanActivity);
|
||||
break;
|
||||
}
|
||||
|
||||
case ContextSource.NoParent:
|
||||
{
|
||||
// TODO fix after next DiagnosticSource preview comes out - this is a hack to force activity to become orphan
|
||||
spanActivity = new Activity(this.name).SetParentId(" ").Start();
|
||||
this.parentSpanContext = null;
|
||||
break;
|
||||
}
|
||||
|
||||
case ContextSource.Activity:
|
||||
{
|
||||
this.parentSpanContext = ParentContextFromActivity(this.fromActivity);
|
||||
spanActivity = this.fromActivity;
|
||||
needRestoreOriginal = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case ContextSource.ExplicitRemoteParent:
|
||||
{
|
||||
spanActivity = new Activity(this.name);
|
||||
if (this.parentSpanContext.IsValid)
|
||||
{
|
||||
spanActivity.SetParentId(this.parentSpanContext.TraceId,
|
||||
this.parentSpanContext.SpanId,
|
||||
this.parentSpanContext.TraceOptions);
|
||||
}
|
||||
|
||||
spanActivity.TraceStateString = this.parentSpanContext.Tracestate.ToString();
|
||||
spanActivity.Start();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ContextSource.ExplicitSpanParent:
|
||||
{
|
||||
spanActivity = new Activity(this.name);
|
||||
if (this.parentSpan.Context.IsValid)
|
||||
{
|
||||
spanActivity.SetParentId(this.parentSpan.Context.TraceId,
|
||||
this.parentSpan.Context.SpanId,
|
||||
this.parentSpan.Context.TraceOptions);
|
||||
}
|
||||
|
||||
spanActivity.TraceStateString = this.parentSpan.Context.Tracestate.ToString();
|
||||
spanActivity.Start();
|
||||
|
||||
this.parentSpanContext = this.parentSpan.Context;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unknown parentType {contextSource}");
|
||||
}
|
||||
|
||||
if (needRestoreOriginal)
|
||||
{
|
||||
// Activity Start always puts Activity on top of Current stack
|
||||
// in OpenTelemetry we ask users to enable implicit propagation by calling WithSpan
|
||||
// it will set Current Activity and attach span to it.
|
||||
// we need to work with .NET team to allow starting Activities without updating Current
|
||||
// As a workaround here we are undoing updating Current
|
||||
Activity.Current = originalActivity;
|
||||
}
|
||||
|
||||
return spanActivity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ namespace OpenTelemetry.Trace
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISpan CurrentSpan => CurrentSpanUtils.CurrentSpan ?? BlankSpan.Instance;
|
||||
public ISpan CurrentSpan => CurrentSpanUtils.CurrentSpan;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IBinaryFormat BinaryFormat { get; }
|
||||
|
|
@ -89,7 +89,7 @@ namespace OpenTelemetry.Trace
|
|||
throw new ArgumentNullException(nameof(span));
|
||||
}
|
||||
|
||||
return CurrentSpanUtils.WithSpan(span, false);
|
||||
return CurrentSpanUtils.WithSpan(span, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
<ProjectReference Include="..\..\src\OpenTelemetry.Collector.AspNetCore\OpenTelemetry.Collector.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\src\OpenTelemetry\OpenTelemetry.csproj" />
|
||||
<ProjectReference Include="..\TestApp.AspNetCore.2.0\TestApp.AspNetCore.2.0.csproj" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -16,51 +16,268 @@
|
|||
|
||||
namespace OpenTelemetry.Trace.Test
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Moq;
|
||||
using OpenTelemetry.Trace.Internal;
|
||||
using OpenTelemetry.Trace.Config;
|
||||
using Xunit;
|
||||
|
||||
public class CurrentSpanUtilsTest
|
||||
public class CurrentSpanUtilsTest: IDisposable
|
||||
{
|
||||
private ISpan span;
|
||||
private SpanContext spanContext;
|
||||
private SpanOptions spanOptions;
|
||||
private readonly IStartEndHandler startEndHandler = Mock.Of<IStartEndHandler>();
|
||||
|
||||
public CurrentSpanUtilsTest()
|
||||
{
|
||||
spanContext =
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.Recorded,
|
||||
Tracestate.Empty);
|
||||
|
||||
spanOptions = SpanOptions.RecordEvents;
|
||||
var mockSpan = new Mock<TestSpan>() { CallBase = true };
|
||||
span = mockSpan.Object;
|
||||
// TODO: remove with next DiagnosticSource preview, switch to Activity setidformat
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CurrentSpan_WhenNoContext()
|
||||
{
|
||||
Assert.Null(CurrentSpanUtils.CurrentSpan);
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithSpan_CloseDetaches()
|
||||
public void CurrentSpan_WhenNoSpanOnActivity()
|
||||
{
|
||||
Assert.Null(CurrentSpanUtils.CurrentSpan);
|
||||
var ws = CurrentSpanUtils.WithSpan(span, false);
|
||||
try
|
||||
var a = new Activity("foo").Start();
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public void WithSpan_CloseDetaches(bool stopSpan, bool recordEvents)
|
||||
{
|
||||
var activity = new Activity("foo").Start();
|
||||
var span = Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? SpanOptions.RecordEvents : SpanOptions.None,
|
||||
"foo",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
using (CurrentSpanUtils.WithSpan(span, stopSpan))
|
||||
{
|
||||
Assert.Same(activity, Activity.Current);
|
||||
Assert.Same(span, CurrentSpanUtils.CurrentSpan);
|
||||
}
|
||||
finally
|
||||
|
||||
Assert.Equal(stopSpan & recordEvents, span.HasEnded);
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
Assert.Null(Activity.Current);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public void WithSpan_NotOwningActivity(bool stopSpan, bool recordEvents)
|
||||
{
|
||||
var activity = new Activity("foo").Start();
|
||||
var span = Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? SpanOptions.RecordEvents : SpanOptions.None,
|
||||
"foo",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null,
|
||||
false);
|
||||
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
using (CurrentSpanUtils.WithSpan(span, stopSpan))
|
||||
{
|
||||
ws.Dispose();
|
||||
Assert.Same(activity, Activity.Current);
|
||||
Assert.Same(span, CurrentSpanUtils.CurrentSpan);
|
||||
}
|
||||
Assert.Null(CurrentSpanUtils.CurrentSpan);
|
||||
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
Assert.Equal(activity, Activity.Current);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public void WithSpan_NoopOnBrokenScope(bool stopSpan, bool recordEvents)
|
||||
{
|
||||
var parentActivity = new Activity("parent").Start();
|
||||
var parentSpan = Span.StartSpan(
|
||||
parentActivity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? SpanOptions.RecordEvents : SpanOptions.None,
|
||||
"parent",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
var parentScope = CurrentSpanUtils.WithSpan(parentSpan, stopSpan);
|
||||
|
||||
var childActivity = new Activity("child").Start();
|
||||
var childSpan = Span.StartSpan(
|
||||
childActivity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? SpanOptions.RecordEvents : SpanOptions.None,
|
||||
"child",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
|
||||
var childScope = CurrentSpanUtils.WithSpan(childSpan, stopSpan);
|
||||
|
||||
parentScope.Dispose();
|
||||
|
||||
Assert.Equal(stopSpan & recordEvents, parentSpan.HasEnded);
|
||||
Assert.False(childSpan.HasEnded);
|
||||
Assert.Same(childSpan, CurrentSpanUtils.CurrentSpan);
|
||||
Assert.Equal(childActivity, Activity.Current);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public void WithSpan_RestoresParentScope(bool stopSpan, bool recordEvents)
|
||||
{
|
||||
var parentActivity = new Activity("parent").Start();
|
||||
var parentSpan = Span.StartSpan(
|
||||
parentActivity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? SpanOptions.RecordEvents : SpanOptions.None,
|
||||
"parent",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
var parentScope = CurrentSpanUtils.WithSpan(parentSpan, stopSpan);
|
||||
|
||||
var childActivity = new Activity("child").Start();
|
||||
var childSpan = Span.StartSpan(
|
||||
childActivity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? SpanOptions.RecordEvents : SpanOptions.None,
|
||||
"child",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
|
||||
var childScope = CurrentSpanUtils.WithSpan(childSpan, stopSpan);
|
||||
|
||||
childScope.Dispose();
|
||||
|
||||
Assert.Equal(stopSpan & recordEvents, childSpan.HasEnded);
|
||||
Assert.False(parentSpan.HasEnded);
|
||||
|
||||
Assert.Same(parentSpan, CurrentSpanUtils.CurrentSpan);
|
||||
Assert.Equal(parentActivity, Activity.Current);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithSpan_SameActivityCreateScopeTwice()
|
||||
{
|
||||
var activity = new Activity("foo").Start();
|
||||
var span = Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
SpanOptions.RecordEvents,
|
||||
"foo",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
||||
using (CurrentSpanUtils.WithSpan(span, true))
|
||||
using(CurrentSpanUtils.WithSpan(span, true))
|
||||
{
|
||||
Assert.Same(activity, Activity.Current);
|
||||
Assert.Same(span, CurrentSpanUtils.CurrentSpan);
|
||||
}
|
||||
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
Assert.Null(Activity.Current);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithSpan_NullActivity()
|
||||
{
|
||||
var activity = new Activity("foo").Start();
|
||||
var span = Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
SpanOptions.RecordEvents,
|
||||
"foo",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
||||
activity.Stop();
|
||||
|
||||
using (CurrentSpanUtils.WithSpan(span, true))
|
||||
{
|
||||
Assert.Null(Activity.Current);
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
}
|
||||
|
||||
Assert.Null(Activity.Current);
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public void WithSpan_WrongActivity(bool stopSpan, bool recordEvents)
|
||||
{
|
||||
var activity = new Activity("foo").Start();
|
||||
var span = Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? SpanOptions.RecordEvents : SpanOptions.None,
|
||||
"foo",
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
using (CurrentSpanUtils.WithSpan(span, stopSpan))
|
||||
{
|
||||
Assert.Same(activity, Activity.Current);
|
||||
Assert.Same(span, CurrentSpanUtils.CurrentSpan);
|
||||
|
||||
var anotherActivity = new Activity("foo").Start();
|
||||
}
|
||||
|
||||
Assert.Equal(stopSpan & recordEvents, span.HasEnded);
|
||||
Assert.Same(BlankSpan.Instance, CurrentSpanUtils.CurrentSpan);
|
||||
Assert.NotSame(activity, Activity.Current);
|
||||
Assert.NotNull(Activity.Current);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
namespace OpenTelemetry.Trace.Test
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using OpenTelemetry.Utils;
|
||||
using Xunit;
|
||||
|
|
@ -99,7 +100,7 @@ namespace OpenTelemetry.Trace.Test
|
|||
"MyStringAttributeKey", "MyStringAttributeValue");
|
||||
@event = Event.Create("MyEventText2", attributes);
|
||||
Assert.Contains("MyEventText2", @event.ToString());
|
||||
Assert.Contains(Collections.ToString(attributes), @event.ToString());
|
||||
Assert.Contains(string.Join(",", attributes.Select(kvp => $"{kvp.Key}={kvp.Value}")), @event.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenTelemetry.Trace.Export.Test
|
||||
{
|
||||
using System.Diagnostics;
|
||||
|
|
@ -23,7 +25,7 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
using OpenTelemetry.Trace.Internal;
|
||||
using Xunit;
|
||||
|
||||
public class InProcessRunningSpanStoreTest
|
||||
public class InProcessRunningSpanStoreTest : IDisposable
|
||||
{
|
||||
private const string SpanName1 = "MySpanName/1";
|
||||
private const string SpanName2 = "MySpanName/2";
|
||||
|
|
@ -39,17 +41,13 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
|
||||
private ISpan CreateSpan(string spanName)
|
||||
{
|
||||
var spanContext =
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
var activity = new Activity(spanName).Start();
|
||||
return Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
spanName,
|
||||
SpanKind.Internal,
|
||||
ActivitySpanId.CreateRandom(),
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
|
@ -125,6 +123,11 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
span2.End();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
}
|
||||
|
||||
// [Fact]
|
||||
// public void getActiveSpans_SpansWithSameName()
|
||||
// {
|
||||
|
|
|
|||
|
|
@ -26,13 +26,10 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
using OpenTelemetry.Utils;
|
||||
using Xunit;
|
||||
|
||||
public class InProcessSampledSpanStoreTest
|
||||
public class InProcessSampledSpanStoreTest : IDisposable
|
||||
{
|
||||
private static readonly string REGISTERED_SPAN_NAME = "MySpanName/1";
|
||||
private static readonly string NOT_REGISTERED_SPAN_NAME = "MySpanName/2";
|
||||
private readonly SpanContext sampledSpanContext;
|
||||
|
||||
private readonly SpanContext notSampledSpanContext;
|
||||
private const string RegisteredSpanName = "MySpanName/1";
|
||||
private const string NotRegisteredSpanName = "MySpanName/2";
|
||||
|
||||
private readonly ActivitySpanId parentSpanId;
|
||||
private readonly SpanOptions recordSpanOptions = SpanOptions.RecordEvents;
|
||||
|
|
@ -50,20 +47,18 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
{
|
||||
timestamp = Timestamp.FromDateTimeOffset(startTime);
|
||||
timestampConverter = Timer.StartNew(startTime, () => interval);
|
||||
sampledSpanContext = SpanContext.Create(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded, Tracestate.Empty);
|
||||
notSampledSpanContext = SpanContext.Create(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, Tracestate.Empty);
|
||||
parentSpanId = ActivitySpanId.CreateRandom();
|
||||
startEndHandler = new TestStartEndHandler(sampleStore);
|
||||
sampleStore.RegisterSpanNamesForCollection(new List<string>() { REGISTERED_SPAN_NAME });
|
||||
sampleStore.RegisterSpanNamesForCollection(new List<string>() { RegisteredSpanName });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddSpansWithRegisteredNamesInAllLatencyBuckets()
|
||||
{
|
||||
AddSpanNameToAllLatencyBuckets(REGISTERED_SPAN_NAME);
|
||||
AddSpanNameToAllLatencyBuckets(RegisteredSpanName);
|
||||
var perSpanNameSummary = sampleStore.Summary.PerSpanNameSummary;
|
||||
Assert.Equal(1, perSpanNameSummary.Count);
|
||||
var latencyBucketsSummaries = perSpanNameSummary[REGISTERED_SPAN_NAME].NumbersOfLatencySampledSpans;
|
||||
var latencyBucketsSummaries = perSpanNameSummary[RegisteredSpanName].NumbersOfLatencySampledSpans;
|
||||
Assert.Equal(LatencyBucketBoundaries.Values.Count, latencyBucketsSummaries.Count);
|
||||
foreach (var it in latencyBucketsSummaries)
|
||||
{
|
||||
|
|
@ -74,42 +69,42 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void AddSpansWithoutRegisteredNamesInAllLatencyBuckets()
|
||||
{
|
||||
AddSpanNameToAllLatencyBuckets(NOT_REGISTERED_SPAN_NAME);
|
||||
AddSpanNameToAllLatencyBuckets(NotRegisteredSpanName);
|
||||
var perSpanNameSummary = sampleStore.Summary.PerSpanNameSummary;
|
||||
Assert.Equal(1, perSpanNameSummary.Count);
|
||||
Assert.False(perSpanNameSummary.ContainsKey(NOT_REGISTERED_SPAN_NAME));
|
||||
Assert.False(perSpanNameSummary.ContainsKey(NotRegisteredSpanName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisterUnregisterAndListSpanNames()
|
||||
{
|
||||
Assert.Contains(REGISTERED_SPAN_NAME, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(RegisteredSpanName, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Equal(1, sampleStore.RegisteredSpanNamesForCollection.Count);
|
||||
|
||||
sampleStore.RegisterSpanNamesForCollection(new List<string>() { NOT_REGISTERED_SPAN_NAME });
|
||||
sampleStore.RegisterSpanNamesForCollection(new List<string>() { NotRegisteredSpanName });
|
||||
|
||||
Assert.Contains(REGISTERED_SPAN_NAME, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(NOT_REGISTERED_SPAN_NAME, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(RegisteredSpanName, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(NotRegisteredSpanName, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Equal(2, sampleStore.RegisteredSpanNamesForCollection.Count);
|
||||
|
||||
sampleStore.UnregisterSpanNamesForCollection(new List<string>() { NOT_REGISTERED_SPAN_NAME });
|
||||
sampleStore.UnregisterSpanNamesForCollection(new List<string>() { NotRegisteredSpanName });
|
||||
|
||||
Assert.Contains(REGISTERED_SPAN_NAME, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(RegisteredSpanName, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Equal(1, sampleStore.RegisteredSpanNamesForCollection.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisterSpanNamesViaSpanBuilderOption()
|
||||
{
|
||||
Assert.Contains(REGISTERED_SPAN_NAME, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(RegisteredSpanName, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Equal(1, sampleStore.RegisteredSpanNamesForCollection.Count);
|
||||
|
||||
var span = CreateSampledSpan(NOT_REGISTERED_SPAN_NAME);
|
||||
var span = CreateSampledSpan(NotRegisteredSpanName);
|
||||
span.IsSampleToLocalSpanStore = true;
|
||||
span.End();
|
||||
|
||||
Assert.Contains(REGISTERED_SPAN_NAME, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(NOT_REGISTERED_SPAN_NAME, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(RegisteredSpanName, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Contains(NotRegisteredSpanName, sampleStore.RegisteredSpanNamesForCollection);
|
||||
Assert.Equal(2, sampleStore.RegisteredSpanNamesForCollection.Count);
|
||||
|
||||
}
|
||||
|
|
@ -117,10 +112,10 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void AddSpansWithRegisteredNamesInAllErrorBuckets()
|
||||
{
|
||||
AddSpanNameToAllErrorBuckets(REGISTERED_SPAN_NAME);
|
||||
AddSpanNameToAllErrorBuckets(RegisteredSpanName);
|
||||
var perSpanNameSummary = sampleStore.Summary.PerSpanNameSummary;
|
||||
Assert.Equal(1, perSpanNameSummary.Count);
|
||||
var errorBucketsSummaries = perSpanNameSummary[REGISTERED_SPAN_NAME].NumbersOfErrorSampledSpans;
|
||||
var errorBucketsSummaries = perSpanNameSummary[RegisteredSpanName].NumbersOfErrorSampledSpans;
|
||||
var ccCount = Enum.GetValues(typeof(CanonicalCode)).Cast<CanonicalCode>().Count();
|
||||
Assert.Equal(ccCount - 1, errorBucketsSummaries.Count);
|
||||
foreach (var it in errorBucketsSummaries)
|
||||
|
|
@ -132,23 +127,23 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void AddSpansWithoutRegisteredNamesInAllErrorBuckets()
|
||||
{
|
||||
AddSpanNameToAllErrorBuckets(NOT_REGISTERED_SPAN_NAME);
|
||||
AddSpanNameToAllErrorBuckets(NotRegisteredSpanName);
|
||||
var perSpanNameSummary = sampleStore.Summary.PerSpanNameSummary;
|
||||
Assert.Equal(1, perSpanNameSummary.Count);
|
||||
Assert.False(perSpanNameSummary.ContainsKey(NOT_REGISTERED_SPAN_NAME));
|
||||
Assert.False(perSpanNameSummary.ContainsKey(NotRegisteredSpanName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetErrorSampledSpans()
|
||||
{
|
||||
var span = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(10);
|
||||
span.Status = Status.Cancelled;
|
||||
span.End();
|
||||
|
||||
var samples =
|
||||
sampleStore.GetErrorSampledSpans(
|
||||
SampledSpanStoreErrorFilter.Create(REGISTERED_SPAN_NAME, CanonicalCode.Cancelled, 0));
|
||||
SampledSpanStoreErrorFilter.Create(RegisteredSpanName, CanonicalCode.Cancelled, 0));
|
||||
Assert.Single(samples);
|
||||
Assert.Contains(span.ToSpanData(), samples);
|
||||
}
|
||||
|
|
@ -156,21 +151,21 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetErrorSampledSpans_MaxSpansToReturn()
|
||||
{
|
||||
var span1 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span1 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(10);
|
||||
span1.Status = Status.Cancelled;
|
||||
span1.End();
|
||||
|
||||
// Advance time to allow other spans to be sampled.
|
||||
interval += TimeSpan.FromSeconds(5);
|
||||
var span2 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span2 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(10);
|
||||
span2.Status = Status.Cancelled;
|
||||
span2.End();
|
||||
|
||||
var samples =
|
||||
sampleStore.GetErrorSampledSpans(
|
||||
SampledSpanStoreErrorFilter.Create(REGISTERED_SPAN_NAME, CanonicalCode.Cancelled, 1));
|
||||
SampledSpanStoreErrorFilter.Create(RegisteredSpanName, CanonicalCode.Cancelled, 1));
|
||||
Assert.Single(samples);
|
||||
// No order guaranteed so one of the spans should be in the list.
|
||||
Assert.True(samples.Contains(span1.ToSpanData()) || samples.Contains(span2.ToSpanData()));
|
||||
|
|
@ -179,19 +174,19 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetErrorSampledSpans_NullCode()
|
||||
{
|
||||
var span1 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span1 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(10);
|
||||
|
||||
span1.Status = Status.Cancelled;;
|
||||
span1.End();
|
||||
|
||||
var span2 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span2 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(10);
|
||||
span2.Status = Status.Unknown;
|
||||
span2.End();
|
||||
|
||||
var samples =
|
||||
sampleStore.GetErrorSampledSpans(SampledSpanStoreErrorFilter.Create(REGISTERED_SPAN_NAME, null, 0));
|
||||
sampleStore.GetErrorSampledSpans(SampledSpanStoreErrorFilter.Create(RegisteredSpanName, null, 0));
|
||||
Assert.Equal(2, samples.Count());
|
||||
Assert.Contains(span1.ToSpanData(), samples);
|
||||
Assert.Contains(span2.ToSpanData(), samples);
|
||||
|
|
@ -200,17 +195,17 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetErrorSampledSpans_NullCode_MaxSpansToReturn()
|
||||
{
|
||||
var span1 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span1 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(10);
|
||||
span1.Status = Status.Cancelled;
|
||||
span1.End();
|
||||
var span2 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span2 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(10);
|
||||
span2.Status = Status.Unknown;
|
||||
span2.End();
|
||||
|
||||
var samples =
|
||||
sampleStore.GetErrorSampledSpans(SampledSpanStoreErrorFilter.Create(REGISTERED_SPAN_NAME, null, 1));
|
||||
sampleStore.GetErrorSampledSpans(SampledSpanStoreErrorFilter.Create(RegisteredSpanName, null, 1));
|
||||
Assert.Single(samples);
|
||||
Assert.True(samples.Contains(span1.ToSpanData()) || samples.Contains(span2.ToSpanData()));
|
||||
}
|
||||
|
|
@ -218,13 +213,13 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetLatencySampledSpans()
|
||||
{
|
||||
var span = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(200); // 20 microseconds
|
||||
span.End();
|
||||
var samples =
|
||||
sampleStore.GetLatencySampledSpans(
|
||||
SampledSpanStoreLatencyFilter.Create(
|
||||
REGISTERED_SPAN_NAME,
|
||||
RegisteredSpanName,
|
||||
TimeSpan.FromTicks(150),
|
||||
TimeSpan.FromTicks(250),
|
||||
0));
|
||||
|
|
@ -235,13 +230,13 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetLatencySampledSpans_ExclusiveUpperBound()
|
||||
{
|
||||
var span = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(200); // 20 microseconds
|
||||
span.End();
|
||||
var samples =
|
||||
sampleStore.GetLatencySampledSpans(
|
||||
SampledSpanStoreLatencyFilter.Create(
|
||||
REGISTERED_SPAN_NAME,
|
||||
RegisteredSpanName,
|
||||
TimeSpan.FromTicks(150),
|
||||
TimeSpan.FromTicks(200),
|
||||
0));
|
||||
|
|
@ -251,13 +246,13 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetLatencySampledSpans_InclusiveLowerBound()
|
||||
{
|
||||
var span = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(200); // 20 microseconds
|
||||
span.End();
|
||||
var samples =
|
||||
sampleStore.GetLatencySampledSpans(
|
||||
SampledSpanStoreLatencyFilter.Create(
|
||||
REGISTERED_SPAN_NAME,
|
||||
RegisteredSpanName,
|
||||
TimeSpan.FromTicks(150),
|
||||
TimeSpan.FromTicks(250),
|
||||
0));
|
||||
|
|
@ -268,18 +263,18 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetLatencySampledSpans_QueryBetweenMultipleBuckets()
|
||||
{
|
||||
var span1 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span1 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(200); // 20 microseconds
|
||||
span1.End();
|
||||
// Advance time to allow other spans to be sampled.
|
||||
interval += TimeSpan.FromSeconds(5);
|
||||
var span2 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span2 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(2000); // 200 microseconds
|
||||
span2.End();
|
||||
var samples =
|
||||
sampleStore.GetLatencySampledSpans(
|
||||
SampledSpanStoreLatencyFilter.Create(
|
||||
REGISTERED_SPAN_NAME,
|
||||
RegisteredSpanName,
|
||||
TimeSpan.FromTicks(150),
|
||||
TimeSpan.FromTicks(2500),
|
||||
0));
|
||||
|
|
@ -291,18 +286,18 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void GetLatencySampledSpans_MaxSpansToReturn()
|
||||
{
|
||||
var span1 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span1 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(200); // 20 microseconds
|
||||
span1.End();
|
||||
// Advance time to allow other spans to be sampled.
|
||||
interval += TimeSpan.FromSeconds(5);
|
||||
var span2 = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span2 = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval += TimeSpan.FromTicks(2000); // 200 microseconds
|
||||
span2.End();
|
||||
var samples =
|
||||
sampleStore.GetLatencySampledSpans(
|
||||
SampledSpanStoreLatencyFilter.Create(
|
||||
REGISTERED_SPAN_NAME,
|
||||
RegisteredSpanName,
|
||||
TimeSpan.FromTicks(150),
|
||||
TimeSpan.FromTicks(2500),
|
||||
1));
|
||||
|
|
@ -313,23 +308,26 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void IgnoreNegativeSpanLatency()
|
||||
{
|
||||
var span = CreateSampledSpan(REGISTERED_SPAN_NAME) as Span;
|
||||
var span = CreateSampledSpan(RegisteredSpanName) as Span;
|
||||
interval -= TimeSpan.FromTicks(200); // 20 microseconds
|
||||
span.End();
|
||||
var samples =
|
||||
sampleStore.GetLatencySampledSpans(
|
||||
SampledSpanStoreLatencyFilter.Create(REGISTERED_SPAN_NAME, TimeSpan.Zero, TimeSpan.MaxValue, 0));
|
||||
SampledSpanStoreLatencyFilter.Create(RegisteredSpanName, TimeSpan.Zero, TimeSpan.MaxValue, 0));
|
||||
Assert.Empty(samples);
|
||||
}
|
||||
|
||||
private Span CreateSampledSpan(string spanName)
|
||||
{
|
||||
var activity = new Activity(spanName).Start();
|
||||
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
|
||||
return (Span)Span.StartSpan(
|
||||
sampledSpanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
spanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -337,12 +335,15 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
|
||||
private Span CreateNotSampledSpan(string spanName)
|
||||
{
|
||||
var activity = new Activity(spanName).Start();
|
||||
activity.ActivityTraceFlags = ActivityTraceFlags.None;
|
||||
|
||||
return (Span)Span.StartSpan(
|
||||
notSampledSpanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
spanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -374,6 +375,8 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
notSampledSpan.Status = code.ToStatus();
|
||||
sampledSpan.End();
|
||||
notSampledSpan.End();
|
||||
sampledSpan.Activity.Stop();
|
||||
notSampledSpan.Activity.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -397,5 +400,10 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
sampleStore.ConsiderForSampling(span);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
using OpenTelemetry.Trace.Internal;
|
||||
using Xunit;
|
||||
|
||||
public class SpanExporterTest
|
||||
public class SpanExporterTest : IDisposable
|
||||
{
|
||||
private const String SPAN_NAME_1 = "MySpanName/1";
|
||||
private const String SPAN_NAME_2 = "MySpanName/2";
|
||||
private readonly SpanContext sampledSpanContext;
|
||||
private readonly SpanContext notSampledSpanContext;
|
||||
private const string SpanName1 = "MySpanName/1";
|
||||
private const string SpanName2 = "MySpanName/2";
|
||||
private readonly Activity sampledActivity;
|
||||
private readonly Activity notSampledActivity;
|
||||
private readonly ISpanExporter spanExporter = SpanExporter.Create(4, Duration.Create(1, 0));
|
||||
private readonly IRunningSpanStore runningSpanStore = new InProcessRunningSpanStore();
|
||||
private readonly IStartEndHandler startEndHandler;
|
||||
|
|
@ -45,8 +45,10 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
|
||||
public SpanExporterTest()
|
||||
{
|
||||
sampledSpanContext = SpanContext.Create(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded, Tracestate.Empty);
|
||||
notSampledSpanContext = SpanContext.Create(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, Tracestate.Empty);
|
||||
sampledActivity = new Activity("foo");
|
||||
sampledActivity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
|
||||
notSampledActivity = new Activity("foo");
|
||||
startEndHandler = new StartEndHandler(spanExporter, runningSpanStore, null, new SimpleEventQueue());
|
||||
|
||||
spanExporter.RegisterHandler("test.service", serviceHandler);
|
||||
|
|
@ -54,13 +56,14 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
|
||||
private Span CreateSampledEndedSpan(string spanName)
|
||||
{
|
||||
sampledActivity.Start();
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
sampledSpanContext,
|
||||
sampledActivity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
spanName,
|
||||
SpanKind.Internal,
|
||||
default,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
|
@ -70,13 +73,14 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
|
||||
private Span CreateNotSampledEndedSpan(string spanName)
|
||||
{
|
||||
notSampledActivity.Start();
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
notSampledSpanContext,
|
||||
notSampledActivity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
spanName,
|
||||
SpanKind.Internal,
|
||||
default,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
null);
|
||||
|
|
@ -87,8 +91,8 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void ExportDifferentSampledSpans()
|
||||
{
|
||||
var span1 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span2 = CreateSampledEndedSpan(SPAN_NAME_2);
|
||||
var span1 = CreateSampledEndedSpan(SpanName1);
|
||||
var span2 = CreateSampledEndedSpan(SpanName2);
|
||||
var exported = serviceHandler.WaitForExport(2);
|
||||
Assert.Equal(2, exported.Count());
|
||||
Assert.Contains(span1.ToSpanData(), exported);
|
||||
|
|
@ -98,12 +102,12 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void ExportMoreSpansThanTheBufferSize()
|
||||
{
|
||||
var span1 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span2 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span3 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span4 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span5 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span6 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span1 = CreateSampledEndedSpan(SpanName1);
|
||||
var span2 = CreateSampledEndedSpan(SpanName1);
|
||||
var span3 = CreateSampledEndedSpan(SpanName1);
|
||||
var span4 = CreateSampledEndedSpan(SpanName1);
|
||||
var span5 = CreateSampledEndedSpan(SpanName1);
|
||||
var span6 = CreateSampledEndedSpan(SpanName1);
|
||||
var exported = serviceHandler.WaitForExport(6);
|
||||
Assert.Equal(6, exported.Count());
|
||||
Assert.Contains(span1.ToSpanData(), exported);
|
||||
|
|
@ -135,13 +139,13 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
// .when(mockServiceHandler)
|
||||
// .export(anyListOf(SpanData));
|
||||
spanExporter.RegisterHandler("mock.service", mockServiceHandler);
|
||||
var span1 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span1 = CreateSampledEndedSpan(SpanName1);
|
||||
var exported = serviceHandler.WaitForExport(1);
|
||||
Assert.Single(exported);
|
||||
Assert.Contains(span1.ToSpanData(), exported);
|
||||
// assertThat(exported).containsExactly(span1.toSpanData());
|
||||
// Continue to export after the exception was received.
|
||||
var span2 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span2 = CreateSampledEndedSpan(SpanName1);
|
||||
exported = serviceHandler.WaitForExport(1);
|
||||
Assert.Single(exported);
|
||||
Assert.Contains(span2.ToSpanData(), exported);
|
||||
|
|
@ -153,8 +157,8 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
{
|
||||
var serviceHandler2 = new TestHandler();
|
||||
spanExporter.RegisterHandler("test.service2", serviceHandler2);
|
||||
var span1 = CreateSampledEndedSpan(SPAN_NAME_1);
|
||||
var span2 = CreateSampledEndedSpan(SPAN_NAME_2);
|
||||
var span1 = CreateSampledEndedSpan(SpanName1);
|
||||
var span2 = CreateSampledEndedSpan(SpanName2);
|
||||
var exported1 = serviceHandler.WaitForExport(2);
|
||||
var exported2 = serviceHandler2.WaitForExport(2);
|
||||
Assert.Equal(2, exported1.Count());
|
||||
|
|
@ -168,8 +172,8 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
[Fact]
|
||||
public void ExportNotSampledSpans()
|
||||
{
|
||||
var span1 = CreateNotSampledEndedSpan(SPAN_NAME_1);
|
||||
var span2 = CreateSampledEndedSpan(SPAN_NAME_2);
|
||||
var span1 = CreateNotSampledEndedSpan(SpanName1);
|
||||
var span2 = CreateSampledEndedSpan(SpanName2);
|
||||
// Spans are recorded and exported in the same order as they are ended, we test that a non
|
||||
// sampled span is not exported by creating and ending a sampled span after a non sampled span
|
||||
// and checking that the first exported span is the sampled span (the non sampled did not get
|
||||
|
|
@ -191,7 +195,7 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
|
||||
exporter.RegisterHandler("first", handler1.Object);
|
||||
|
||||
var span1 = CreateNotSampledEndedSpan(SPAN_NAME_1).ToSpanData();
|
||||
var span1 = CreateNotSampledEndedSpan(SpanName1).ToSpanData();
|
||||
|
||||
await exporter.ExportAsync(span1, CancellationToken.None);
|
||||
|
||||
|
|
@ -202,6 +206,12 @@ namespace OpenTelemetry.Trace.Export.Test
|
|||
(x) => x.Where((s) => s == span1).Count() > 0 &&
|
||||
x.Count() == 1)));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
spanExporter?.Dispose();
|
||||
Activity.Current = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenTelemetry.Trace.Test
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Trace.Internal;
|
||||
using OpenTelemetry.Utils;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -30,6 +31,10 @@ namespace OpenTelemetry.Trace.Test
|
|||
|
||||
public LinkTest()
|
||||
{
|
||||
// TODO: remove with next DiagnosticSource preview, switch to Activity setidformat
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
|
||||
spanContext = SpanContext.Create(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, Tracestate.Empty); ;
|
||||
attributesMap.Add("MyAttributeKey0", AttributeValue<string>.Create("MyStringAttribute"));
|
||||
attributesMap.Add("MyAttributeKey1", AttributeValue<long>.Create(10));
|
||||
|
|
@ -71,38 +76,35 @@ namespace OpenTelemetry.Trace.Test
|
|||
Assert.Equal(attributesMap, link.Attributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Link_EqualsAndHashCode()
|
||||
{
|
||||
// EqualsTester tester = new EqualsTester();
|
||||
// tester
|
||||
// .addEqualityGroup(
|
||||
// Link.fromSpanContext(spanContext, Type.PARENT_LINKED_SPAN),
|
||||
// Link.fromSpanContext(spanContext, Type.PARENT_LINKED_SPAN))
|
||||
// .addEqualityGroup(
|
||||
// Link.fromSpanContext(spanContext, Type.CHILD_LINKED_SPAN),
|
||||
// Link.fromSpanContext(spanContext, Type.CHILD_LINKED_SPAN))
|
||||
// .addEqualityGroup(Link.fromSpanContext(SpanContext.INVALID, Type.CHILD_LINKED_SPAN))
|
||||
// .addEqualityGroup(Link.fromSpanContext(SpanContext.INVALID, Type.PARENT_LINKED_SPAN))
|
||||
// .addEqualityGroup(
|
||||
// Link.fromSpanContext(spanContext, Type.PARENT_LINKED_SPAN, attributesMap),
|
||||
// Link.fromSpanContext(spanContext, Type.PARENT_LINKED_SPAN, attributesMap));
|
||||
// tester.testEquals();
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Link_ToString()
|
||||
{
|
||||
var link = Link.FromSpanContext(spanContext, attributesMap);
|
||||
Assert.Contains(spanContext.TraceId.ToString(), link.ToString());
|
||||
Assert.Contains(spanContext.SpanId.ToString(), link.ToString());
|
||||
Assert.Contains(Collections.ToString(attributesMap), link.ToString());
|
||||
Assert.Contains(string.Join(" ", attributesMap.Select(kvp => $"{kvp.Key}={kvp.Value}")), link.ToString());
|
||||
link = Link.FromSpanContext(spanContext, attributesMap);
|
||||
Assert.Contains(spanContext.TraceId.ToString(), link.ToString());
|
||||
Assert.Contains(spanContext.SpanId.ToString(), spanContext.SpanId.ToString());
|
||||
Assert.Contains(Collections.ToString(attributesMap), link.ToString());
|
||||
Assert.Contains(string.Join(" ", attributesMap.Select(kvp => $"{kvp.Key}={kvp.Value}")), link.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromSpanContext_FromActivity()
|
||||
{
|
||||
var activity = new Activity("foo").Start();
|
||||
activity.TraceStateString = "k1=v1, k2=v2";
|
||||
|
||||
var link = Link.FromActivity(activity);
|
||||
Assert.Equal(activity.TraceId, link.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, link.Context.SpanId);
|
||||
|
||||
var entries = link.Context.Tracestate.Entries.ToArray();
|
||||
Assert.Equal(2, entries.Length);
|
||||
Assert.Equal("k1", entries[0].Key);
|
||||
Assert.Equal("v1", entries[0].Value);
|
||||
Assert.Equal("k2", entries[1].Key);
|
||||
Assert.Equal("v2", entries[1].Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenTelemetry.Tests.Impl.Trace
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Trace;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -31,7 +31,20 @@ namespace OpenTelemetry.Tests.Impl.Trace
|
|||
var spanBuilder = new NoopSpanBuilder("foo");
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetParent((ISpan)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetParent((SpanContext)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetParent((Activity)null));
|
||||
|
||||
// no Activity.Current
|
||||
Assert.Throws<ArgumentException>(() => spanBuilder.SetCreateChild(false));
|
||||
|
||||
// Activity.Current wrong format
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.Hierarchical;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
var a = new Activity("foo").Start(); // TODO SetIdFormat
|
||||
Assert.Throws<ArgumentException>(() => spanBuilder.SetCreateChild(false));
|
||||
a.Stop();
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetSampler(null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink((Activity)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink((ILink)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink((SpanContext)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink(null, null));
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenTelemetry.Impl.Trace.Propagation
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
|
@ -27,6 +26,17 @@ namespace OpenTelemetry.Impl.Trace.Propagation
|
|||
|
||||
public class TraceContextTest
|
||||
{
|
||||
private static readonly string[] empty = new string[0];
|
||||
private static readonly Func<IDictionary<string, string>, string, IEnumerable<string>> getter = (headers, name) =>
|
||||
{
|
||||
if (headers.TryGetValue(name, out var value))
|
||||
{
|
||||
return new [] { value };
|
||||
}
|
||||
|
||||
return empty;
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void TraceContextFormatCanParseExampleFromSpec()
|
||||
{
|
||||
|
|
@ -40,7 +50,7 @@ namespace OpenTelemetry.Impl.Trace.Propagation
|
|||
};
|
||||
|
||||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(headers, (h, n) => new string[] {h[n]});
|
||||
var ctx = f.Extract(headers, getter);
|
||||
|
||||
Assert.Equal(ActivityTraceId.CreateFromString("0af7651916cd43dd8448eb211c80319c".AsSpan()), ctx.TraceId);
|
||||
Assert.Equal(ActivitySpanId.CreateFromString("b9c7c989f97918e1".AsSpan()), ctx.SpanId);
|
||||
|
|
@ -59,13 +69,29 @@ namespace OpenTelemetry.Impl.Trace.Propagation
|
|||
Assert.Equal("00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01", last.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraceContextFormatNotSampled()
|
||||
{
|
||||
var headers = new Dictionary<string, string>()
|
||||
{
|
||||
{"traceparent", "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-00"},
|
||||
};
|
||||
|
||||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(headers, getter);
|
||||
|
||||
Assert.Equal(ActivityTraceId.CreateFromString("0af7651916cd43dd8448eb211c80319c".AsSpan()), ctx.TraceId);
|
||||
Assert.Equal(ActivitySpanId.CreateFromString("b9c7c989f97918e1".AsSpan()), ctx.SpanId);
|
||||
Assert.True((ctx.TraceOptions & ActivityTraceFlags.Recorded) == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraceContextFormat_IsBlankIfNoHeader()
|
||||
{
|
||||
var headers = new Dictionary<string, string>();
|
||||
|
||||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(headers, (h, n) => new string[] { h[n] });
|
||||
var ctx = f.Extract(headers, getter);
|
||||
|
||||
Assert.Same(SpanContext.Blank, ctx);
|
||||
}
|
||||
|
|
@ -79,9 +105,48 @@ namespace OpenTelemetry.Impl.Trace.Propagation
|
|||
};
|
||||
|
||||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(headers, (h, n) => new string[] { h[n] });
|
||||
var ctx = f.Extract(headers, getter);
|
||||
|
||||
Assert.Same(SpanContext.Blank, ctx);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraceContextFormat_TracestateToStringEmpty()
|
||||
{
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
{"traceparent", "00-abc7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01"},
|
||||
};
|
||||
|
||||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(headers, getter);
|
||||
|
||||
Assert.Empty(ctx.Tracestate.Entries);
|
||||
Assert.Equal(string.Empty, ctx.Tracestate.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraceContextFormat_TracestateToString()
|
||||
{
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
{"traceparent", "00-abc7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01"},
|
||||
{"tracestate", "k1=v1,k2=v2,k3=v3" },
|
||||
};
|
||||
|
||||
var f = new TraceContextFormat();
|
||||
var ctx = f.Extract(headers, getter);
|
||||
|
||||
var entries = ctx.Tracestate.Entries.ToArray();
|
||||
Assert.Equal(3, entries.Length);
|
||||
Assert.Equal("k1", entries[0].Key);
|
||||
Assert.Equal("v1", entries[0].Value);
|
||||
Assert.Equal("k2", entries[1].Key);
|
||||
Assert.Equal("v2", entries[1].Value);
|
||||
Assert.Equal("k3", entries[2].Key);
|
||||
Assert.Equal("v3", entries[2].Value);
|
||||
|
||||
Assert.Equal("k1=v1,k2=v2,k3=v3", ctx.Tracestate.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
// <copyright file="StringExtensionsTests.cs" company="OpenTelemetry Authors">
|
||||
// Copyright 2018, 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.Linq;
|
||||
using OpenTelemetry.Context.Propagation;
|
||||
using OpenTelemetry.Trace;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Tests.Impl.Trace.Propagation
|
||||
{
|
||||
public class TracestateUtilsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(null)]
|
||||
[InlineData(" ")]
|
||||
[InlineData("\t")]
|
||||
public void EmptyTracestate(string tracestate)
|
||||
{
|
||||
var builder = Tracestate.Builder;
|
||||
Assert.True(TracestateUtils.TryExtractTracestate(tracestate, builder));
|
||||
Assert.Empty(builder.Build().Entries);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("k=", 0)]
|
||||
[InlineData("=v", 0)]
|
||||
[InlineData("kv", 0)]
|
||||
[InlineData("k=v,k=v", 1)]
|
||||
[InlineData("k1=v1,,,k2=v2", 1)]
|
||||
[InlineData("k=morethan256......................................................................................................................................................................................................................................................", 0)]
|
||||
[InlineData("v=morethan256......................................................................................................................................................................................................................................................", 0)]
|
||||
public void InvalidTracestate(string tracestate, int validEntriesCount)
|
||||
{
|
||||
var builder = Tracestate.Builder;
|
||||
Assert.False(TracestateUtils.TryExtractTracestate(tracestate, builder));
|
||||
Assert.Equal(validEntriesCount, builder.Build().Entries.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TooManyEntries()
|
||||
{
|
||||
var builder = Tracestate.Builder;
|
||||
var tracestate =
|
||||
"k0=v,k1=v,k2=v,k3=v,k4=v,k5=v,k6=v,k7=v1,k8=v,k9=v,k10=v,k11=v,k12=v,k13=v,k14=v,k15=v,k16=v,k17=v,k18=v,k19=v,k20=v,k21=v,k22=v,k23=v,k24=v,k25=v,k26=v,k27=v1,k28=v,k29=v,k30=v,k31=v,k32=v,k33=v";
|
||||
Assert.False(TracestateUtils.TryExtractTracestate(tracestate, builder));
|
||||
Assert.Throws<ArgumentException>(() => builder.Build());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("k=v")]
|
||||
[InlineData(" k=v ")]
|
||||
[InlineData(" k = v ")]
|
||||
[InlineData("\tk\t=\tv\t")]
|
||||
[InlineData(",k=v,")]
|
||||
[InlineData(", k = v, ")]
|
||||
public void ValidPair(string pair)
|
||||
{
|
||||
var builder = Tracestate.Builder;
|
||||
Assert.True(TracestateUtils.TryExtractTracestate(pair, builder));
|
||||
Assert.Equal("k=v", builder.Build().ToString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("k1=v1,k2=v2")]
|
||||
[InlineData(" k1=v1 , k2=v2")]
|
||||
[InlineData(" ,k1=v1,k2=v2")]
|
||||
[InlineData("k1=v1,k2=v2, ")]
|
||||
public void ValidPairs(string tracestate)
|
||||
{
|
||||
var builder = Tracestate.Builder;
|
||||
Assert.True(TracestateUtils.TryExtractTracestate(tracestate, builder));
|
||||
Assert.Equal("k1=v1,k2=v2", builder.Build().ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,9 @@ namespace OpenTelemetry.Trace.Test
|
|||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using OpenTelemetry.Common;
|
||||
using OpenTelemetry.Trace.Config;
|
||||
|
|
@ -25,7 +28,7 @@ namespace OpenTelemetry.Trace.Test
|
|||
using OpenTelemetry.Trace.Sampler;
|
||||
using Xunit;
|
||||
|
||||
public class SpanBuilderTest
|
||||
public class SpanBuilderTest : IDisposable
|
||||
{
|
||||
private static readonly string SpanName = "MySpanName";
|
||||
private readonly SpanBuilderOptions spanBuilderOptions;
|
||||
|
|
@ -36,14 +39,21 @@ namespace OpenTelemetry.Trace.Test
|
|||
private readonly IStartEndHandler startEndHandler = Mock.Of<IStartEndHandler>();
|
||||
private readonly ITraceConfig traceConfig = Mock.Of<ITraceConfig>();
|
||||
|
||||
private readonly ITracer tracer;
|
||||
public SpanBuilderTest()
|
||||
{
|
||||
// TODO: remove with next DiagnosticSource preview, switch to Activity setidformat
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
|
||||
// MockitoAnnotations.initMocks(this);
|
||||
spanBuilderOptions =
|
||||
new SpanBuilderOptions(startEndHandler, traceConfig);
|
||||
var configMock = Mock.Get<ITraceConfig>(traceConfig);
|
||||
configMock.Setup((c) => c.ActiveTraceParams).Returns(alwaysSampleTraceParams);
|
||||
// when(traceConfig.getActiveTraceParams()).thenReturn(alwaysSampleTraceParams);
|
||||
|
||||
startEndHandler = Mock.Of<IStartEndHandler>();
|
||||
tracer = new Tracer(startEndHandler, traceConfig);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -62,6 +72,11 @@ namespace OpenTelemetry.Trace.Test
|
|||
Timestamp.FromDateTimeOffset(DateTimeOffset.Now).AddDuration(Duration.Create(-1, 0)),
|
||||
Timestamp.FromDateTimeOffset(DateTimeOffset.Now).AddDuration(Duration.Create(1, 0)));
|
||||
Assert.Equal(SpanName, spanData.Name);
|
||||
|
||||
var activity = ((Span)span).Activity;
|
||||
Assert.Null(Activity.Current);
|
||||
Assert.Equal(activity.TraceId, span.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, span.Context.SpanId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -144,6 +159,66 @@ namespace OpenTelemetry.Trace.Test
|
|||
Assert.Equal(spanContext.SpanId, childSpan.ParentSpanId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanLastParentWins5()
|
||||
{
|
||||
var spanContext =
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
var activity = new Activity("foo").Start();
|
||||
|
||||
var childSpan = (Span)new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetParent(spanContext)
|
||||
.SetParent(activity)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(activity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, childSpan.ParentSpanId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanLastParentWins6()
|
||||
{
|
||||
var spanContext =
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
var activity = new Activity("foo").Start();
|
||||
|
||||
var childSpan = (Span)new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetParent(spanContext)
|
||||
.SetCreateChild(false)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(activity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, childSpan.Context.SpanId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanLastParentWins7()
|
||||
{
|
||||
var spanContext =
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
var activity = new Activity("foo").Start();
|
||||
|
||||
var childSpan = (Span)new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetCreateChild(false)
|
||||
.SetParent(spanContext)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(spanContext.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(spanContext.SpanId, childSpan.ParentSpanId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanNullParentWithRecordEvents()
|
||||
{
|
||||
|
|
@ -180,6 +255,7 @@ namespace OpenTelemetry.Trace.Test
|
|||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetNoParent()
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(rootSpan.Context.IsValid);
|
||||
Assert.True(rootSpan.IsRecordingEvents);
|
||||
Assert.True((rootSpan.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
|
|
@ -195,6 +271,150 @@ namespace OpenTelemetry.Trace.Test
|
|||
Assert.Equal(((Span)rootSpan).TimestampConverter, ((Span)childSpan).TimestampConverter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanInScopeOfCurrentActivity()
|
||||
{
|
||||
var parentActivity = new Activity(SpanName).Start();
|
||||
parentActivity.TraceStateString = "k1=v1,k2=v2";
|
||||
|
||||
var childSpan = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(parentActivity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(parentActivity.SpanId, ((Span)childSpan).ParentSpanId);
|
||||
|
||||
var activity = ((Span)childSpan).Activity;
|
||||
Assert.Equal(parentActivity, Activity.Current);
|
||||
Assert.Equal(activity.Parent, parentActivity);
|
||||
|
||||
Assert.Equal("k1=v1,k2=v2", childSpan.Context.Tracestate.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanInScopeOfCurrentActivityRecorded()
|
||||
{
|
||||
var parentActivity = new Activity(SpanName).Start();
|
||||
parentActivity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
|
||||
var childSpan = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(parentActivity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(parentActivity.SpanId, ((Span)childSpan).ParentSpanId);
|
||||
Assert.True((childSpan.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
parentActivity.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanInScopeOfCurrentActivityNoParent()
|
||||
{
|
||||
var parentActivity = new Activity(SpanName).Start();
|
||||
parentActivity.TraceStateString = "k1=v1,k2=v2";
|
||||
|
||||
var childSpan = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetNoParent()
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.NotEqual(parentActivity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.True(((Span)childSpan).ToSpanData().ParentSpanId == default);
|
||||
|
||||
var activity = ((Span)childSpan).Activity;
|
||||
Assert.Equal(parentActivity, Activity.Current);
|
||||
Assert.Null(activity.Parent);
|
||||
Assert.Equal(activity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, childSpan.Context.SpanId);
|
||||
Assert.Empty(childSpan.Context.Tracestate.Entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanFromExplicitActivity()
|
||||
{
|
||||
var parentActivity = new Activity(SpanName).Start();
|
||||
parentActivity.TraceStateString = "k1=v1,k2=v2";
|
||||
parentActivity.Stop();
|
||||
|
||||
var childSpan = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetParent(parentActivity)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(parentActivity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(parentActivity.SpanId, ((Span)childSpan).ParentSpanId);
|
||||
|
||||
var activity = ((Span)childSpan).Activity;
|
||||
Assert.NotNull(activity);
|
||||
Assert.Null(Activity.Current);
|
||||
Assert.Equal(activity.TraceId, parentActivity.TraceId);
|
||||
Assert.Equal(activity.ParentSpanId, parentActivity.SpanId);
|
||||
Assert.Equal(activity.SpanId, childSpan.Context.SpanId);
|
||||
Assert.Equal("k1=v1,k2=v2", childSpan.Context.Tracestate.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanFromExplicitRecordedActivity()
|
||||
{
|
||||
var parentActivity = new Activity(SpanName).Start();
|
||||
parentActivity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
parentActivity.Stop();
|
||||
|
||||
var childSpan = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetParent(parentActivity)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(parentActivity.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(parentActivity.SpanId, ((Span)childSpan).ParentSpanId);
|
||||
Assert.True((childSpan.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanFromCurrentActivity()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
activity.TraceStateString = "k1=v1,k2=v2";
|
||||
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetCreateChild(false)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(span.Context.IsValid);
|
||||
Assert.Equal(activity.TraceId, span.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, span.Context.SpanId);
|
||||
Assert.True(((Span)span).ParentSpanId == default);
|
||||
|
||||
Assert.NotNull(Activity.Current);
|
||||
Assert.Equal(Activity.Current, activity);
|
||||
Assert.Equal("k1=v1,k2=v2", span.Context.Tracestate.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpanFromCurrentRecordedActivity()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetCreateChild(false)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(span.Context.IsValid);
|
||||
Assert.Equal(activity.TraceId, span.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, span.Context.SpanId);
|
||||
Assert.True(((Span)span).ParentSpanId == default);
|
||||
Assert.True((span.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_ExplicitNoParent()
|
||||
{
|
||||
|
|
@ -208,6 +428,12 @@ namespace OpenTelemetry.Trace.Test
|
|||
Assert.True((span.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
Assert.True(spanData.ParentSpanId == default);
|
||||
|
||||
var activity = ((Span)span).Activity;
|
||||
Assert.Null(Activity.Current);
|
||||
Assert.Equal(activity.TraceId, span.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, span.Context.SpanId);
|
||||
Assert.Empty(span.Context.Tracestate.Entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -224,12 +450,50 @@ namespace OpenTelemetry.Trace.Test
|
|||
Assert.True(spanData.ParentSpanId == default);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_BlankSpanParent()
|
||||
{
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetParent(BlankSpan.Instance)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(span.Context.IsValid);
|
||||
Assert.True(span.IsRecordingEvents);
|
||||
Assert.True((span.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
Assert.True(spanData.ParentSpanId == default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_BlankSpanContextParent()
|
||||
{
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.SetParent(SpanContext.Blank)
|
||||
.StartSpan();
|
||||
|
||||
Assert.True(span.Context.IsValid);
|
||||
Assert.True(span.IsRecordingEvents);
|
||||
Assert.True((span.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
Assert.True(spanData.ParentSpanId == default);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_CurrentSpanParent()
|
||||
{
|
||||
var rootSpan = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetParent(
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None,
|
||||
Tracestate.Builder.Set("k1", "v1").Build()))
|
||||
.StartSpan();
|
||||
using (CurrentSpanUtils.WithSpan(rootSpan, true))
|
||||
using (tracer.WithSpan(rootSpan))
|
||||
{
|
||||
var childSpan = (Span)new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.StartSpan();
|
||||
|
|
@ -237,6 +501,7 @@ namespace OpenTelemetry.Trace.Test
|
|||
Assert.True(childSpan.Context.IsValid);
|
||||
Assert.Equal(rootSpan.Context.TraceId, childSpan.Context.TraceId);
|
||||
Assert.Equal(rootSpan.Context.SpanId, childSpan.ParentSpanId);
|
||||
Assert.Equal("k1=v1", childSpan.Context.Tracestate.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +510,7 @@ namespace OpenTelemetry.Trace.Test
|
|||
{
|
||||
var rootSpan = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.StartSpan();
|
||||
using (CurrentSpanUtils.WithSpan(rootSpan, true))
|
||||
using (tracer.WithSpan(rootSpan))
|
||||
{
|
||||
var childSpan = (Span)new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetNoParent()
|
||||
|
|
@ -278,7 +543,8 @@ namespace OpenTelemetry.Trace.Test
|
|||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
ActivityTraceFlags.None,
|
||||
Tracestate.Builder.Set("k1", "v1").Build());
|
||||
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
|
|
@ -291,6 +557,109 @@ namespace OpenTelemetry.Trace.Test
|
|||
Assert.True((span.Context.TraceOptions & ActivityTraceFlags.Recorded) != 0);
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
Assert.Equal(spanContext.SpanId, spanData.ParentSpanId);
|
||||
Assert.Equal("k1=v1", span.Context.Tracestate.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_WithLink()
|
||||
{
|
||||
var link = Link.FromSpanContext(
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty));
|
||||
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.AddLink(link)
|
||||
.StartSpan();
|
||||
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
var links = spanData.Links.Links.ToArray();
|
||||
|
||||
Assert.Single(links);
|
||||
|
||||
Assert.Equal(link.Context.TraceId, links[0].Context.TraceId);
|
||||
Assert.Equal(link.Context.SpanId, links[0].Context.SpanId);
|
||||
Assert.Equal(link.Context.TraceOptions, links[0].Context.TraceOptions);
|
||||
Assert.Equal(link.Context.Tracestate, links[0].Context.Tracestate);
|
||||
Assert.Equal(0, links[0].Attributes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_WithLinkFromActivity()
|
||||
{
|
||||
var activityLink = new Activity("foo").Start();
|
||||
activityLink.Stop();
|
||||
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.AddLink(activityLink)
|
||||
.StartSpan();
|
||||
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
var links = spanData.Links.Links.ToArray();
|
||||
|
||||
Assert.Single(links);
|
||||
|
||||
Assert.Equal(activityLink.TraceId, links[0].Context.TraceId);
|
||||
Assert.Equal(activityLink.SpanId, links[0].Context.SpanId);
|
||||
Assert.Equal(activityLink.ActivityTraceFlags, links[0].Context.TraceOptions);
|
||||
Assert.Empty(links[0].Context.Tracestate.Entries);
|
||||
Assert.Equal(0, links[0].Attributes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_WithLinkFromSpanContextAndAttributes()
|
||||
{
|
||||
var linkContext =
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.AddLink(linkContext, new Dictionary<string, object> { ["k"] = "v", })
|
||||
.StartSpan();
|
||||
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
var links = spanData.Links.Links.ToArray();
|
||||
|
||||
Assert.Single(links);
|
||||
|
||||
Assert.Equal(linkContext.TraceId, links[0].Context.TraceId);
|
||||
Assert.Equal(linkContext.SpanId, links[0].Context.SpanId);
|
||||
Assert.Equal(linkContext.TraceOptions, links[0].Context.TraceOptions);
|
||||
Assert.Equal(linkContext.Tracestate, links[0].Context.Tracestate);
|
||||
Assert.Equal(1, links[0].Attributes.Count);
|
||||
Assert.True(links[0].Attributes.ContainsKey("k"));
|
||||
Assert.Equal("v", links[0].Attributes["k"].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartSpan_WithLinkFromSpanContext()
|
||||
{
|
||||
var linkContext =
|
||||
SpanContext.Create(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
|
||||
var span = new SpanBuilder(SpanName, spanBuilderOptions)
|
||||
.SetSpanKind(SpanKind.Internal)
|
||||
.AddLink(linkContext)
|
||||
.StartSpan();
|
||||
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
var links = spanData.Links.Links.ToArray();
|
||||
|
||||
Assert.Single(links);
|
||||
|
||||
Assert.Equal(linkContext.TraceId, links[0].Context.TraceId);
|
||||
Assert.Equal(linkContext.SpanId, links[0].Context.SpanId);
|
||||
Assert.Equal(linkContext.TraceOptions, links[0].Context.TraceOptions);
|
||||
Assert.Equal(linkContext.Tracestate, links[0].Context.Tracestate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -491,15 +860,35 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void SpanBuilder_BadArguments()
|
||||
{
|
||||
var spanBuilder = new SpanBuilder(SpanName, spanBuilderOptions);
|
||||
Assert.Throws<ArgumentNullException>(() => new SpanBuilder(null, spanBuilderOptions));
|
||||
Assert.Throws<ArgumentNullException>(() => new SpanBuilder(SpanName, null));
|
||||
|
||||
var spanBuilder = new SpanBuilder(SpanName, spanBuilderOptions);
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetParent((ISpan)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetParent((SpanContext)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetParent((Activity)null));
|
||||
|
||||
// no Activity.Current
|
||||
Assert.Throws<ArgumentException>(() => spanBuilder.SetCreateChild(false));
|
||||
|
||||
// Activity.Current wrong format
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.Hierarchical;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
var a = new Activity("foo").Start(); // TODO SetIdFormat
|
||||
Assert.Throws<ArgumentException>(() => spanBuilder.SetCreateChild(false));
|
||||
a.Stop();
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.SetSampler(null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink((Activity)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink((ILink)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink((SpanContext)null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink(null, null));
|
||||
Assert.Throws<ArgumentNullException>(() => spanBuilder.AddLink(SpanContext.Blank, null));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,32 +25,28 @@ namespace OpenTelemetry.Trace.Test
|
|||
using OpenTelemetry.Internal;
|
||||
using OpenTelemetry.Trace;
|
||||
using OpenTelemetry.Trace.Config;
|
||||
using OpenTelemetry.Trace.Internal;
|
||||
using Xunit;
|
||||
|
||||
public class SpanTest
|
||||
public class SpanTest : IDisposable
|
||||
{
|
||||
private const string SpanName = "MySpanName";
|
||||
private const string EventDescription = "MyEvent";
|
||||
private readonly SpanContext spanContext;
|
||||
private readonly ActivitySpanId parentSpanId;
|
||||
|
||||
private TimeSpan interval = TimeSpan.FromMilliseconds(0);
|
||||
private readonly DateTimeOffset startTime = DateTimeOffset.Now;
|
||||
private readonly Timestamp timestamp;
|
||||
private readonly Timer timestampConverter;
|
||||
private readonly SpanOptions noRecordSpanOptions = SpanOptions.None;
|
||||
private readonly SpanOptions recordSpanOptions = SpanOptions.RecordEvents;
|
||||
private readonly IDictionary<String, object> attributes = new Dictionary<String, object>();
|
||||
private readonly IDictionary<String, object> expectedAttributes;
|
||||
private readonly IDictionary<string, object> attributes = new Dictionary<String, object>();
|
||||
private readonly IDictionary<string, object> expectedAttributes;
|
||||
private readonly IStartEndHandler startEndHandler = Mock.Of<IStartEndHandler>();
|
||||
|
||||
public SpanTest()
|
||||
{
|
||||
timestamp = Timestamp.FromDateTimeOffset(startTime);
|
||||
timestampConverter = Timer.StartNew(startTime, () => interval);
|
||||
spanContext = SpanContext.Create(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.None, Tracestate.Empty);
|
||||
parentSpanId = ActivitySpanId.CreateRandom();
|
||||
|
||||
attributes.Add(
|
||||
"MyStringAttributeKey", AttributeValue.StringAttributeValue("MyStringAttributeValue"));
|
||||
attributes.Add("MyLongAttributeKey", AttributeValue.LongAttributeValue(123L));
|
||||
|
|
@ -64,13 +60,18 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void ToSpanData_NoRecordEvents()
|
||||
{
|
||||
var activityLink = new Activity(SpanName).Start();
|
||||
activityLink.Stop();
|
||||
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
noRecordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -82,22 +83,78 @@ namespace OpenTelemetry.Trace.Test
|
|||
|
||||
span.AddEvent(Event.Create(EventDescription));
|
||||
span.AddEvent(EventDescription, attributes);
|
||||
span.AddLink(Link.FromSpanContext(spanContext));
|
||||
span.AddLink(Link.FromActivity(activityLink));
|
||||
span.End();
|
||||
// exception.expect(IllegalStateException);
|
||||
Assert.Throws<InvalidOperationException>(() => ((Span)span).ToSpanData());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoEventsRecordedAfterEnd()
|
||||
public void GetSpanContextFromActivity()
|
||||
{
|
||||
var tracestate = Tracestate.Builder.Set("k1", "v1").Build();
|
||||
var activity = new Activity(SpanName).Start();
|
||||
activity.TraceStateString = tracestate.ToString();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
tracestate,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
Assert.NotNull(span.Context);
|
||||
Assert.Equal(activity.TraceId, span.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, span.Context.SpanId);
|
||||
Assert.Equal(activity.ParentSpanId, ((Span)span).ParentSpanId);
|
||||
Assert.Equal(activity.ActivityTraceFlags, span.Context.TraceOptions);
|
||||
Assert.Same(tracestate, span.Context.Tracestate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSpanContextFromActivityRecordedWithParent()
|
||||
{
|
||||
var tracestate = Tracestate.Builder.Set("k1", "v1").Build();
|
||||
var parent = new Activity(SpanName).Start();
|
||||
var activity = new Activity(SpanName).Start();
|
||||
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
activity,
|
||||
tracestate,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
Assert.NotNull(span.Context);
|
||||
Assert.Equal(activity.TraceId, span.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, span.Context.SpanId);
|
||||
Assert.Equal(activity.ParentSpanId, ((Span)span).ParentSpanId);
|
||||
Assert.Equal(activity.ActivityTraceFlags, span.Context.TraceOptions);
|
||||
Assert.Same(tracestate, span.Context.Tracestate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoEventsRecordedAfterEnd()
|
||||
{
|
||||
var activityLink = new Activity(SpanName).Start();
|
||||
activityLink.Stop();
|
||||
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -114,7 +171,7 @@ namespace OpenTelemetry.Trace.Test
|
|||
"MySingleStringAttributeValue");
|
||||
span.AddEvent(Event.Create(EventDescription));
|
||||
span.AddEvent(EventDescription, attributes);
|
||||
span.AddLink(Link.FromSpanContext(spanContext));
|
||||
span.AddLink(Link.FromActivity(activityLink));
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
Assert.Equal(timestamp, spanData.StartTimestamp);
|
||||
Assert.Empty(spanData.Attributes.AttributeMap);
|
||||
|
|
@ -127,13 +184,21 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void ToSpanData_ActiveSpan()
|
||||
{
|
||||
|
||||
var activityLink = new Activity(SpanName);
|
||||
activityLink.Stop();
|
||||
|
||||
var activity = new Activity(SpanName)
|
||||
.SetParentId(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom())
|
||||
.Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -152,12 +217,17 @@ namespace OpenTelemetry.Trace.Test
|
|||
span.AddEvent(EventDescription, attributes);
|
||||
interval = TimeSpan.FromMilliseconds(300);
|
||||
interval = TimeSpan.FromMilliseconds(400);
|
||||
var link = Link.FromSpanContext(spanContext);
|
||||
var link = Link.FromActivity(activityLink);
|
||||
span.AddLink(link);
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
Assert.Equal(spanContext, spanData.Context);
|
||||
Assert.Equal(activity.TraceId, spanData.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, spanData.Context.SpanId);
|
||||
Assert.Equal(activity.ParentSpanId, spanData.ParentSpanId);
|
||||
Assert.Equal(activity.ActivityTraceFlags, spanData.Context.TraceOptions);
|
||||
Assert.Same(Tracestate.Empty, spanData.Context.Tracestate);
|
||||
|
||||
Assert.Equal(SpanName, spanData.Name);
|
||||
Assert.Equal(parentSpanId, spanData.ParentSpanId);
|
||||
Assert.Equal(activity.ParentSpanId, spanData.ParentSpanId);
|
||||
Assert.Equal(0, spanData.Attributes.DroppedAttributesCount);
|
||||
Assert.Equal(expectedAttributes, spanData.Attributes.AttributeMap);
|
||||
Assert.Equal(0, spanData.Events.DroppedEventsCount);
|
||||
|
|
@ -182,13 +252,20 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void GoSpanData_EndedSpan()
|
||||
{
|
||||
var activityLink = new Activity(SpanName).Start();
|
||||
activityLink.Stop();
|
||||
|
||||
var activity = new Activity(SpanName)
|
||||
.SetParentId(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom())
|
||||
.Start();
|
||||
|
||||
var span =
|
||||
(Span)Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -206,16 +283,20 @@ namespace OpenTelemetry.Trace.Test
|
|||
interval = TimeSpan.FromMilliseconds(200);
|
||||
span.AddEvent(EventDescription, attributes);
|
||||
interval = TimeSpan.FromMilliseconds(300);
|
||||
var link = Link.FromSpanContext(spanContext);
|
||||
var link = Link.FromActivity(activityLink);
|
||||
span.AddLink(link);
|
||||
interval = TimeSpan.FromMilliseconds(400);
|
||||
span.Status = Status.Cancelled;
|
||||
span.End();
|
||||
|
||||
var spanData = ((Span)span).ToSpanData();
|
||||
Assert.Equal(spanContext, spanData.Context);
|
||||
Assert.Equal(activity.TraceId, spanData.Context.TraceId);
|
||||
Assert.Equal(activity.SpanId, spanData.Context.SpanId);
|
||||
Assert.Equal(activity.ParentSpanId, spanData.ParentSpanId);
|
||||
Assert.Equal(activity.ActivityTraceFlags, spanData.Context.TraceOptions);
|
||||
|
||||
Assert.Equal(SpanName, spanData.Name);
|
||||
Assert.Equal(parentSpanId, spanData.ParentSpanId);
|
||||
Assert.Equal(activity.ParentSpanId, spanData.ParentSpanId);
|
||||
Assert.Equal(0, spanData.Attributes.DroppedAttributesCount);
|
||||
Assert.Equal(expectedAttributes, spanData.Attributes.AttributeMap);
|
||||
Assert.Equal(0, spanData.Events.DroppedEventsCount);
|
||||
|
|
@ -241,13 +322,15 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void Status_ViaSetStatus()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
(Span)Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -265,13 +348,15 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void status_ViaEndSpanOptions()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
(Span)Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -290,16 +375,18 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void DroppingAttributes()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var maxNumberOfAttributes = 8;
|
||||
var traceParams =
|
||||
TraceParams.Default.ToBuilder().SetMaxNumberOfAttributes(maxNumberOfAttributes).Build();
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
traceParams,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -343,16 +430,18 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void DroppingAndAddingAttributes()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var maxNumberOfAttributes = 8;
|
||||
var traceParams =
|
||||
TraceParams.Default.ToBuilder().SetMaxNumberOfAttributes(maxNumberOfAttributes).Build();
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
traceParams,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -414,16 +503,18 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void DroppingEvents()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var maxNumberOfEvents = 8;
|
||||
var traceParams =
|
||||
TraceParams.Default.ToBuilder().SetMaxNumberOfEvents(maxNumberOfEvents).Build();
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
traceParams,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -466,20 +557,25 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void DroppingLinks()
|
||||
{
|
||||
var activityLink = new Activity(SpanName).Start();
|
||||
activityLink.Stop();
|
||||
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var maxNumberOfLinks = 8;
|
||||
var traceParams =
|
||||
TraceParams.Default.ToBuilder().SetMaxNumberOfLinks(maxNumberOfLinks).Build();
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
traceParams,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
var link = Link.FromSpanContext(spanContext);
|
||||
var link = Link.FromActivity(activityLink);
|
||||
for (var i = 0; i < 2 * maxNumberOfLinks; i++)
|
||||
{
|
||||
span.AddLink(link);
|
||||
|
|
@ -506,13 +602,15 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void SampleToLocalSpanStore()
|
||||
{
|
||||
var activity1 = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
(Span)Span.StartSpan(
|
||||
spanContext,
|
||||
activity1,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -520,13 +618,15 @@ namespace OpenTelemetry.Trace.Test
|
|||
span.End();
|
||||
|
||||
Assert.True(((Span)span).IsSampleToLocalSpanStore);
|
||||
|
||||
var activity2 = new Activity(SpanName).Start();
|
||||
var span2 =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity2,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -543,13 +643,15 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void SampleToLocalSpanStore_RunningSpan()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -560,13 +662,15 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void BadArguments()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -588,12 +692,14 @@ namespace OpenTelemetry.Trace.Test
|
|||
[Fact]
|
||||
public void SetSampleTo()
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span = (Span)Span.StartSpan(
|
||||
spanContext,
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
parentSpanId,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter);
|
||||
|
|
@ -602,5 +708,85 @@ namespace OpenTelemetry.Trace.Test
|
|||
span.End();
|
||||
Assert.True(span.IsSampleToLocalSpanStore);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void EndSpanStopsActivity(bool recordEvents)
|
||||
{
|
||||
var parentActivity = new Activity(SpanName).Start();
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? recordSpanOptions : noRecordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter,
|
||||
ownsActivity: true);
|
||||
|
||||
span.End();
|
||||
Assert.Same(parentActivity, Activity.Current);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void EndSpanDoesNotStopActivityWhenDoesNotOwnIt(bool recordEvents)
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? recordSpanOptions : noRecordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter,
|
||||
ownsActivity: false);
|
||||
|
||||
span.End();
|
||||
Assert.Equal(recordEvents, span.HasEnded);
|
||||
Assert.Same(activity, Activity.Current);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public void EndSpanStopActivity_NotCurrentActivity(bool recordEvents, bool ownsActivity)
|
||||
{
|
||||
var activity = new Activity(SpanName).Start();
|
||||
|
||||
var span =
|
||||
Span.StartSpan(
|
||||
activity,
|
||||
Tracestate.Empty,
|
||||
recordEvents ? recordSpanOptions : noRecordSpanOptions,
|
||||
SpanName,
|
||||
SpanKind.Internal,
|
||||
TraceParams.Default,
|
||||
startEndHandler,
|
||||
timestampConverter,
|
||||
ownsActivity: ownsActivity);
|
||||
|
||||
var anotherActivity = new Activity(SpanName).Start();
|
||||
span.End();
|
||||
Assert.Equal(recordEvents, span.HasEnded);
|
||||
Assert.Same(anotherActivity, Activity.Current);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue