opentelemetry-dotnet/test/OpenTelemetry.Tests/Impl/Trace/SpanTest.cs

1016 lines
43 KiB
C#

// <copyright file="SpanTest.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 OpenTelemetry.Trace.Configuration;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Moq;
using OpenTelemetry.Abstractions.Utils;
using OpenTelemetry.Tests;
using OpenTelemetry.Trace.Export;
using Xunit;
namespace OpenTelemetry.Trace.Test
{
public class SpanTest : IDisposable
{
private const string SpanName = "MySpanName";
private const string EventDescription = "MyEvent";
private readonly IDictionary<string, object> attributes = new Dictionary<string, object>();
private readonly List<KeyValuePair<string, object>> expectedAttributes;
private readonly Mock<SpanProcessor> spanProcessorMock = new Mock<SpanProcessor>();
private readonly SpanProcessor spanProcessor;
private readonly TracerFactory tracerFactory;
public SpanTest()
{
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
Activity.ForceDefaultIdFormat = true;
spanProcessor = spanProcessorMock.Object;
tracerFactory = TracerFactory.Create(b => b.AddProcessorPipeline(p => p.AddProcessor(_ => spanProcessor)));
attributes.Add("MyStringAttributeKey", "MyStringAttributeValue");
attributes.Add("MyLongAttributeKey", 123L);
attributes.Add("MyBooleanAttributeKey", false);
expectedAttributes = new List<KeyValuePair<string, object>>(attributes)
{
new KeyValuePair<string, object>("MySingleStringAttributeKey", "MySingleStringAttributeValue"),
};
}
[Fact]
public void StartSpanFrom_Recorded_ParentSpan()
{
var tracer = (Tracer)tracerFactory.GetTracer("foo", "semver:1.0.0");
var traceState = new List<KeyValuePair<string, string>> {new KeyValuePair<string, string>("k1", "v1")};
var grandParentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded, false, traceState);
var parentSpan = (Span)tracer.StartSpan(SpanName, grandParentContext);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentSpan);
Assert.True(span.Context.IsValid);
Assert.Equal(parentSpan.Context.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentSpan.Context.SpanId, span.ParentSpanId);
Assert.Equal(parentSpan.Context.TraceOptions, span.Context.TraceOptions);
Assert.Same(traceState, span.Context.Tracestate);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_Recorded_ParentSpan_IgnoresCurrentParent()
{
var tracer = tracerFactory.GetTracer(null);
tracer.WithSpan(tracer.StartRootSpan("outer"));
{
var parentSpan = (Span)tracer.StartRootSpan(SpanName);
var span = (Span)tracer.StartSpan(SpanName, parentSpan);
Assert.True(span.Context.IsValid);
Assert.Equal(parentSpan.Context.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentSpan.Context.SpanId, span.ParentSpanId);
Assert.Equal(parentSpan.Context.TraceOptions, span.Context.TraceOptions);
}
}
[Fact]
public void StartSpanFrom_NotRecorded_ParentSpan()
{
var tracer = tracerFactory.GetTracer(null);
var grandParentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
var parentSpan = (Span)tracer.StartSpan(SpanName, grandParentContext);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentSpan);
Assert.True(span.Context.IsValid);
Assert.Equal(parentSpan.Context.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentSpan.Context.SpanId, span.ParentSpanId);
Assert.Equal(parentSpan.Context.TraceOptions, span.Context.TraceOptions);
Assert.Empty(span.Context.Tracestate);
Assert.False(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_Recorded_ParentSpan_Kind()
{
var tracer = tracerFactory.GetTracer(null);
var parentSpan = tracer.StartRootSpan(SpanName);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentSpan, SpanKind.Client);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Client, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_Recorded_ParentSpan_Kind_Timestamp()
{
var tracer = tracerFactory.GetTracer(null);
var parentSpan = tracer.StartRootSpan(SpanName);
var startTimestamp = DateTimeOffset.Now.AddSeconds(-1);
var span = (Span)tracer.StartSpan(SpanName, parentSpan, SpanKind.Server, new SpanCreationOptions { StartTimestamp = startTimestamp });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
Assert.Equal(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_Recorded_ParentSpan_Kind_Links_Func()
{
var tracer = tracerFactory.GetTracer(null);
var parentSpan = tracer.StartRootSpan(SpanName);
var linkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentSpan, SpanKind.Server, new SpanCreationOptions { LinksFactory = () => new[] { new Link(linkContext) } });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Single(span.Links);
Assert.Same(linkContext, span.Links.Single().Context);
}
[Fact]
public void StartSpanFrom_Recorded_ParentSpan_Kind_Links_Enumerable()
{
var tracer = tracerFactory.GetTracer(null);
var parentSpan = tracer.StartRootSpan(SpanName);
var linkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentSpan, SpanKind.Server, new SpanCreationOptions { Links = new[] { new Link(linkContext) } });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Single(span.Links);
Assert.Same(linkContext, span.Links.Single().Context);
}
[Fact]
public void StartRootSpan_IgnoresCurrentParent()
{
var tracer = tracerFactory.GetTracer(null);
tracer.WithSpan(tracer.StartRootSpan("outer"));
{
var span = (Span)tracer.StartRootSpan(SpanName);
Assert.True(span.Context.IsValid);
Assert.Equal(span.Activity.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(default, span.ParentSpanId);
Assert.Equal(span.Activity.ActivityTraceFlags, span.Context.TraceOptions);
}
}
[Fact]
public void StartRootSpan_IgnoresCurrentActivity()
{
var tracer = tracerFactory.GetTracer(null);
var outerActivity = new Activity("outer").SetIdFormat(ActivityIdFormat.W3C).Start();
var span = (Span)tracer.StartRootSpan(SpanName);
Assert.True(span.Context.IsValid);
Assert.Equal(span.Activity.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(default, span.ParentSpanId);
Assert.Equal(span.Activity.ActivityTraceFlags, span.Context.TraceOptions);
outerActivity.Stop();
}
[Fact]
public void StartRootSpan()
{
var tracer = tracerFactory.GetTracer(null);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartRootSpan(SpanName);
Assert.True(span.Context.IsValid);
Assert.Equal(span.Activity.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(default, span.ParentSpanId);
Assert.Equal(span.Activity.ActivityTraceFlags, span.Context.TraceOptions);
Assert.Empty(span.Context.Tracestate);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartRootSpanFrom_Kind()
{
var tracer = tracerFactory.GetTracer(null);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartRootSpan(SpanName, SpanKind.Consumer);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Consumer, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartRootSpanFrom_Kind_Timestamp()
{
var tracer = tracerFactory.GetTracer(null);
var startTimestamp = DateTimeOffset.Now.AddSeconds(-1);
var span = (Span)tracer.StartRootSpan(SpanName, SpanKind.Client, new SpanCreationOptions { StartTimestamp = startTimestamp });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Client, span.Kind);
Assert.Equal(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartRootSpanFrom_Kind_Timestamp_Links()
{
var tracer = tracerFactory.GetTracer(null);
var linkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartRootSpan(SpanName, SpanKind.Server, new SpanCreationOptions
{
StartTimestamp = startTimestamp,
Links = new[] { new Link(linkContext) },
});
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
Assert.Equal(startTimestamp, span.StartTimestamp);
Assert.Single(span.Links);
Assert.Same(linkContext, span.Links.Single().Context);
}
[Fact]
public void StartSpanFrom_Recorded_ParentContext()
{
var tracer = tracerFactory.GetTracer(null);
var traceState = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("k1", "v1") };
var parentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded, false, traceState);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentContext);
Assert.True(span.Context.IsValid);
Assert.Equal(parentContext.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentContext.SpanId, span.ParentSpanId);
Assert.Equal(parentContext.TraceOptions, span.Context.TraceOptions);
Assert.Same(traceState, span.Context.Tracestate);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_InvalidContext()
{
var tracer = tracerFactory.GetTracer(null);
var parentContext = SpanContext.Blank;
var span = (Span)tracer.StartSpan(SpanName, parentContext);
Assert.True(span.Context.IsValid);
Assert.NotEqual(default, span.Context.TraceId);
Assert.NotEqual(default, span.Context.SpanId);
Assert.Equal(default, span.ParentSpanId);
// always sample sampler
Assert.Equal(ActivityTraceFlags.Recorded, span.Context.TraceOptions);
Assert.True(span.IsRecording);
}
[Fact]
public void StartSpanFrom_Recorded_ParentContext_IgnoresCurrentParent()
{
var tracer = tracerFactory.GetTracer(null);
tracer.WithSpan(tracer.StartRootSpan("outer"));
{
var parentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(),
ActivityTraceFlags.Recorded);
var span = (Span)tracer.StartSpan(SpanName, parentContext);
Assert.True(span.Context.IsValid);
Assert.Equal(parentContext.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentContext.SpanId, span.ParentSpanId);
Assert.Equal(parentContext.TraceOptions, span.Context.TraceOptions);
}
}
[Fact]
public void StartSpanFrom_NotRecorded_ParentContext()
{
var tracer = tracerFactory.GetTracer(null);
var parentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentContext);
Assert.True(span.Context.IsValid);
Assert.Equal(parentContext.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentContext.SpanId, span.ParentSpanId);
Assert.Equal(parentContext.TraceOptions, span.Context.TraceOptions);
Assert.Empty(span.Context.Tracestate);
Assert.False(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_Recorded_ParentContext_Kind()
{
var tracer = tracerFactory.GetTracer(null);
var parentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentContext, SpanKind.Client);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Client, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_Recorded_ParentContext_Kind_Timestamp()
{
var tracer = tracerFactory.GetTracer(null);
var parentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var startTimestamp = DateTimeOffset.Now.AddSeconds(-1);
var span = (Span)tracer.StartSpan(SpanName, parentContext, SpanKind.Server, new SpanCreationOptions { StartTimestamp = startTimestamp } );
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
Assert.Equal(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpanFrom_Recorded_ParentContext_Kind_Links_Func()
{
var tracer = tracerFactory.GetTracer(null);
var parentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var linkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentContext, SpanKind.Server, new SpanCreationOptions { LinksFactory = () => new[] { new Link(linkContext) }} );
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Single(span.Links);
Assert.Same(linkContext, span.Links.Single().Context);
}
[Fact]
public void StartSpanFrom_Recorded_ParentContext_Kind_Links_Enumerable()
{
var tracer = tracerFactory.GetTracer(null);
var parentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var firstLinkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var secondLinkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, parentContext, SpanKind.Server, new SpanCreationOptions { Links = new[] { new Link(firstLinkContext), new Link(secondLinkContext) } });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Equal(2, span.Links.Count());
Assert.Same(firstLinkContext, span.Links.First().Context);
Assert.Same(secondLinkContext, span.Links.Last().Context);
}
[Fact]
public void StartSpan_Recorded_FromActivity()
{
var tracer = tracerFactory.GetTracer(null);
var activity = new Activity(SpanName).SetIdFormat(ActivityIdFormat.W3C).Start();
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
activity.TraceStateString = "k1=v1,k2=v2";
var span = (Span)tracer.StartSpanFromActivity(SpanName, activity);
Assert.Equal(activity.TraceId, span.Context.TraceId);
Assert.Equal(activity.SpanId, span.Context.SpanId);
Assert.Equal(activity.ParentSpanId, span.ParentSpanId);
Assert.Equal(activity.ActivityTraceFlags, span.Context.TraceOptions);
Assert.Equal(2, span.Context.Tracestate.Count());
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k1" && pair.Value == "v1");
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k2" && pair.Value == "v2");
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
Assert.Equal(activity.StartTimeUtc, span.StartTimestamp.DateTime);
Assert.Equal(activity, span.Activity);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpan_Recorded_FromActivity_IgnoresCurrentParent()
{
var tracer = tracerFactory.GetTracer(null);
tracer.WithSpan(tracer.StartRootSpan("outer"));
{
var activity = new Activity(SpanName).SetIdFormat(ActivityIdFormat.W3C).SetParentId(" ").Start();
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
var span = (Span)tracer.StartSpanFromActivity(SpanName, activity);
Assert.Equal(activity.TraceId, span.Context.TraceId);
Assert.Equal(activity.SpanId, span.Context.SpanId);
Assert.Equal(activity.ParentSpanId, span.ParentSpanId);
Assert.Equal(activity.ActivityTraceFlags, span.Context.TraceOptions);
}
}
[Fact]
public void StartSpan_NotRecorded_FromActivity()
{
var tracer = tracerFactory.GetTracer(null);
var activity = new Activity(SpanName).SetIdFormat(ActivityIdFormat.W3C).Start();
activity.ActivityTraceFlags = ActivityTraceFlags.None;
activity.TraceStateString = "k1=v1,k2=v2";
var span = (Span)tracer.StartSpanFromActivity(SpanName, activity);
Assert.Equal(activity.TraceId, span.Context.TraceId);
Assert.Equal(activity.SpanId, span.Context.SpanId);
Assert.Equal(activity.ParentSpanId, span.ParentSpanId);
Assert.Equal(activity.ActivityTraceFlags, span.Context.TraceOptions);
Assert.Equal(2, span.Context.Tracestate.Count());
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k1" && pair.Value == "v1");
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k2" && pair.Value == "v2");
// activity is not a parent and sampling decision is made
// based on sampler alone
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
Assert.Equal(activity.StartTimeUtc, span.StartTimestamp.DateTime);
Assert.Equal(activity, span.Activity);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpan_Recorded_FromActivity_Kind()
{
var tracer = tracerFactory.GetTracer(null);
var activity = new Activity(SpanName).SetIdFormat(ActivityIdFormat.W3C).Start();
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
var span = (Span)tracer.StartSpanFromActivity(SpanName, activity, SpanKind.Client);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Client, span.Kind);
Assert.Equal(activity.StartTimeUtc, span.StartTimestamp.DateTime);
Assert.Equal(activity, span.Activity);
Assert.Empty(span.Links);
}
[Fact]
public void StartSpan_Recorded_FromActivity_Kind_Links_Enumerable()
{
var tracer = tracerFactory.GetTracer(null);
var activity = new Activity(SpanName).SetIdFormat(ActivityIdFormat.W3C).Start();
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
var firstLinkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded);
var secondLinkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
var span = (Span)tracer.StartSpanFromActivity(SpanName, activity, SpanKind.Server, new[] { new Link(firstLinkContext), new Link(secondLinkContext), });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
Assert.Equal(activity.StartTimeUtc, span.StartTimestamp.DateTime);
Assert.Equal(activity, span.Activity);
Assert.Equal(2, span.Links.Count());
Assert.Same(firstLinkContext, span.Links.First().Context);
Assert.Same(secondLinkContext, span.Links.Last().Context);
}
[Fact]
public void StartSpanFrom_Recorded_ImplicitParentSpan()
{
var tracer = tracerFactory.GetTracer(null);
var traceState = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("k1", "v1") };
var grandParentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded, false, traceState);
using (tracer.WithSpan(tracer.StartSpan(SpanName, grandParentContext)))
{
var parentSpan = tracer.CurrentSpan;
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName);
Assert.True(span.Context.IsValid);
Assert.Equal(parentSpan.Context.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentSpan.Context.SpanId, span.ParentSpanId);
Assert.Equal(parentSpan.Context.TraceOptions, span.Context.TraceOptions);
Assert.Same(traceState, span.Context.Tracestate);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
}
[Fact]
public void StartSpanFrom_Recorded_ImplicitParentActivity()
{
var tracer = tracerFactory.GetTracer(null);
var parentActivity = new Activity("foo").SetIdFormat(ActivityIdFormat.W3C).Start();
parentActivity.TraceStateString = "k1=v1,k2=v2";
parentActivity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName);
Assert.True(span.Context.IsValid);
Assert.Equal(parentActivity.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentActivity.SpanId, span.ParentSpanId);
Assert.Equal(parentActivity.ActivityTraceFlags, span.Context.TraceOptions);
Assert.Equal(2, span.Context.Tracestate.Count());
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k1" && pair.Value == "v1");
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k2" && pair.Value == "v2");
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
parentActivity.Stop();
}
[Fact]
public void StartSpanFrom_NotRecorded_ImplicitParentActivity()
{
var tracer = tracerFactory.GetTracer(null);
var parentActivity = new Activity("foo").SetIdFormat(ActivityIdFormat.W3C).Start();
parentActivity.TraceStateString = "k1=v1,k2=v2";
parentActivity.ActivityTraceFlags = ActivityTraceFlags.None;
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName);
Assert.True(span.Context.IsValid);
Assert.Equal(parentActivity.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentActivity.SpanId, span.ParentSpanId);
Assert.Equal(parentActivity.ActivityTraceFlags, span.Context.TraceOptions);
Assert.Equal(2, span.Context.Tracestate.Count());
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k1" && pair.Value == "v1");
Assert.Contains(span.Context.Tracestate, pair => pair.Key == "k2" && pair.Value == "v2");
Assert.False(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
parentActivity.Stop();
}
[Fact]
public void StartSpanFrom_NotRecorded_ImplicitParentSpan()
{
var tracer = tracerFactory.GetTracer(null);
var grandParentContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
using (tracer.WithSpan(tracer.StartSpan(SpanName, grandParentContext)))
{
var parentSpan = tracer.CurrentSpan;
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName);
Assert.True(span.Context.IsValid);
Assert.Equal(parentSpan.Context.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(parentSpan.Context.SpanId, span.ParentSpanId);
Assert.Equal(parentSpan.Context.TraceOptions, span.Context.TraceOptions);
Assert.Empty(span.Context.Tracestate);
Assert.False(span.IsRecording);
Assert.Equal(SpanKind.Internal, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
}
[Fact]
public void StartSpanFrom_Recorded_ImplicitParentSpan_Kind()
{
var tracer = tracerFactory.GetTracer(null);
using (tracer.WithSpan(tracer.StartRootSpan(SpanName)))
{
var startTimestamp = PreciseTimestamp.GetUtcNow();
var span = (Span)tracer.StartSpan(SpanName, SpanKind.Producer);
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Producer, span.Kind);
AssertApproxSameTimestamp(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
}
[Fact]
public void StartSpanFrom_Recorded_ImplicitParentSpan_Kind_Timestamp()
{
var tracer = tracerFactory.GetTracer(null);
using (tracer.WithSpan(tracer.StartRootSpan(SpanName)))
{
var startTimestamp = DateTimeOffset.UtcNow.AddSeconds(-10);
var span = (Span)tracer.StartSpan(SpanName, SpanKind.Server, new SpanCreationOptions { StartTimestamp = startTimestamp });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
Assert.Equal(startTimestamp, span.StartTimestamp);
Assert.Empty(span.Links);
}
}
[Fact]
public void StartSpanFrom_Recorded_ImplicitParentSpan_Kind_Timestamp_Links_Func()
{
var tracer = tracerFactory.GetTracer(null);
using (tracer.WithSpan(tracer.StartRootSpan(SpanName)))
{
var linkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(),
ActivityTraceFlags.Recorded);
var startTimestamp = DateTimeOffset.UtcNow.AddSeconds(-10);
var span = (Span)tracer.StartSpan(SpanName, SpanKind.Server, new SpanCreationOptions { StartTimestamp = startTimestamp, LinksFactory = () => new[] { new Link(linkContext) } });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
Assert.Equal(startTimestamp, span.StartTimestamp);
Assert.Single(span.Links);
Assert.Same(linkContext, span.Links.Single().Context);
}
}
[Fact]
public void StartSpanFrom_Recorded_ImplicitParentSpan_Kind_Timestamp_Links_Enumerable()
{
var tracer = tracerFactory.GetTracer(null);
using (tracer.WithSpan(tracer.StartRootSpan(SpanName)))
{
var linkContext = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(),
ActivityTraceFlags.Recorded);
var startTimestamp = DateTimeOffset.UtcNow.AddSeconds(-10);
var span = (Span)tracer.StartSpan(SpanName, SpanKind.Server, new SpanCreationOptions { StartTimestamp = startTimestamp, Links = new[] { new Link(linkContext) } });
Assert.True(span.IsRecording);
Assert.Equal(SpanKind.Server, span.Kind);
Assert.Equal(startTimestamp, span.StartTimestamp);
Assert.Single(span.Links);
Assert.Same(linkContext, span.Links.Single().Context);
}
}
[Fact]
public void EndSpan_EventsNotRecorded()
{
var tracer = tracerFactory.GetTracer(null);
var span = (Span)tracer.StartRootSpan(SpanName, SpanKind.Client);
var spanEndTime = DateTimeOffset.UtcNow.AddSeconds(10);
span.End(spanEndTime);
// Check that adding trace events after Span#End() does not throw any exception and are not
// recorded.
foreach (var attribute in attributes)
{
span.SetAttribute(attribute);
}
span.SetAttribute(
"MySingleStringAttributeKey",
"MySingleStringAttributeValue");
span.AddEvent(new Event(EventDescription));
span.AddEvent(EventDescription, attributes);
Assert.NotEqual(default, span.StartTimestamp);
Assert.Empty(span.Attributes);
Assert.Empty(span.Events);
Assert.Empty(span.Links);
Assert.Equal(Status.Ok, span.Status);
Assert.Equal(spanEndTime, span.EndTimestamp);
}
[Fact]
public async Task StartSpan_PropertiesAccessible()
{
var contextLink = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(),
ActivityTraceFlags.None);
var link = new Link(contextLink);
var tracer = tracerFactory.GetTracer(null);
var startTime = DateTimeOffset.UtcNow.AddSeconds(-1);
var span = (Span)tracer.StartSpan(SpanName, SpanKind.Client, new SpanCreationOptions { StartTimestamp = startTime, LinksFactory = () => new[] { link } });
span.SetAttribute(
"MySingleStringAttributeKey",
"MySingleStringAttributeValue");
foreach (var attribute in attributes)
{
span.SetAttribute(attribute);
}
var firstEventTime = PreciseTimestamp.GetUtcNow();
span.AddEvent(new Event(EventDescription, firstEventTime));
await Task.Delay(TimeSpan.FromMilliseconds(100));
var secondEventTime = PreciseTimestamp.GetUtcNow();
span.AddEvent(EventDescription, attributes);
Assert.Equal(span.Activity.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(span.Activity.ParentSpanId, span.ParentSpanId);
Assert.Equal(span.Activity.ActivityTraceFlags, span.Context.TraceOptions);
Assert.Empty(span.Context.Tracestate);
Assert.Equal(SpanName, span.Name);
Assert.Equal(span.Activity.ParentSpanId, span.ParentSpanId);
span.Attributes.AssertAreSame(expectedAttributes);
Assert.Equal(2, span.Events.Count());
Assert.Equal(firstEventTime, span.Events.ToList()[0].Timestamp);
AssertApproxSameTimestamp(span.Events.ToList()[1].Timestamp, secondEventTime);
Assert.Equal(new Event(EventDescription, firstEventTime), span.Events.ToList()[0]);
Assert.Equal(EventDescription, span.Events.ToList()[1].Name);
Assert.Equal(attributes, span.Events.ToList()[1].Attributes);
Assert.Single(span.Links);
Assert.Equal(link, span.Links.First());
Assert.Equal(startTime, span.StartTimestamp);
Assert.True(span.Status.IsValid);
Assert.Equal(default, span.EndTimestamp);
var startEndMock = Mock.Get(spanProcessor);
spanProcessorMock.Verify(s => s.OnStart(span), Times.Once);
startEndMock.Verify(s => s.OnEnd(span), Times.Never);
}
[Fact]
public async Task EndSpan_Properties()
{
var contextLink = new SpanContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(),
ActivityTraceFlags.None);
var link = new Link(contextLink);
var tracer = tracerFactory.GetTracer(null);
var startTime = DateTimeOffset.UtcNow.AddSeconds(-1);
var span = (Span)tracer.StartRootSpan(SpanName, SpanKind.Client, new SpanCreationOptions { StartTimestamp = startTime, LinksFactory = () => new[] { link }});
span.SetAttribute(
"MySingleStringAttributeKey",
"MySingleStringAttributeValue");
foreach (var attribute in attributes)
{
span.SetAttribute(attribute);
}
await Task.Delay(TimeSpan.FromMilliseconds(100));
var firstEventTime = PreciseTimestamp.GetUtcNow();
span.AddEvent(new Event(EventDescription, firstEventTime));
await Task.Delay(TimeSpan.FromMilliseconds(100));
var secondEventTime = PreciseTimestamp.GetUtcNow();
span.AddEvent(EventDescription, attributes);
span.Status = Status.Cancelled;
var spanEndTime = PreciseTimestamp.GetUtcNow();
span.End();
Assert.Equal(span.Activity.TraceId, span.Context.TraceId);
Assert.Equal(span.Activity.SpanId, span.Context.SpanId);
Assert.Equal(span.Activity.ParentSpanId, span.ParentSpanId);
Assert.Equal(span.Activity.ActivityTraceFlags, span.Context.TraceOptions);
Assert.Equal(SpanName, span.Name);
Assert.Equal(span.Activity.ParentSpanId, span.ParentSpanId);
span.Attributes.AssertAreSame(expectedAttributes);
Assert.Equal(2, span.Events.Count());
Assert.Equal(firstEventTime, span.Events.ToList()[0].Timestamp);
AssertApproxSameTimestamp(span.Events.ToList()[1].Timestamp, secondEventTime);
Assert.Equal(new Event(EventDescription, firstEventTime), span.Events.ToList()[0]);
Assert.Single(span.Links);
Assert.Equal(link, span.Links.First());
Assert.Equal(startTime, span.StartTimestamp);
Assert.Equal(Status.Cancelled, span.Status);
AssertApproxSameTimestamp(spanEndTime, span.EndTimestamp);
spanProcessorMock.Verify(s => s.OnStart(span), Times.Once);
spanProcessorMock.Verify(s => s.OnEnd(span), Times.Once);
}
[Fact]
public void SetStatus()
{
var tracer = tracerFactory.GetTracer(null);
var span = (Span)tracer.StartRootSpan(SpanName);
Assert.Equal(Status.Ok, span.Status);
span.Status = Status.Cancelled;
Assert.Equal(Status.Cancelled, span.Status);
span.End();
Assert.Equal(Status.Cancelled, span.Status);
spanProcessorMock.Verify(s => s.OnStart(span), Times.Once);
spanProcessorMock.Verify(s => s.OnEnd(span), Times.Once);
}
[Fact]
public void BadArguments()
{
var tracer = tracerFactory.GetTracer(null);
var span = (Span)tracer.StartRootSpan(SpanName);
Assert.Throws<ArgumentException>(() => span.Status = new Status());
Assert.Throws<ArgumentNullException>(() => span.UpdateName(null));
Assert.Throws<ArgumentNullException>(() => span.SetAttribute(null, string.Empty));
Assert.Throws<ArgumentNullException>(() => span.SetAttribute(string.Empty, null));
Assert.Throws<ArgumentNullException>(() =>
span.SetAttribute(null, "foo"));
Assert.Throws<ArgumentNullException>(() => span.SetAttribute(null, 1L));
Assert.Throws<ArgumentNullException>(() => span.SetAttribute(null, 0.1d));
Assert.Throws<ArgumentNullException>(() => span.SetAttribute(null, true));
Assert.Throws<ArgumentNullException>(() => span.AddEvent((string)null));
Assert.Throws<ArgumentNullException>(() => span.AddEvent((Event)null));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void EndSpanStopsActivity(bool recordEvents)
{
var parentActivity = new Activity("parent").Start();
if (recordEvents)
{
parentActivity.ActivityTraceFlags = ActivityTraceFlags.Recorded;
}
var tracer = tracerFactory
.GetTracer(null);
var span = (Span)tracer.StartSpan(SpanName);
span.End();
Assert.Same(parentActivity, Activity.Current);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void EndSpanDoesNotStopActivityWhenDoesNotOwnIt(bool recordEvents)
{
var activity = new Activity("foo").Start();
if (recordEvents)
{
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
}
var tracer = tracerFactory
.GetTracer(null);
var span = (Span)tracer.StartSpanFromActivity(SpanName, activity);
span.End();
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();
if (recordEvents)
{
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
}
var tracer = tracerFactory.GetTracer(null);
Span span;
if (ownsActivity)
{
span = (Span)tracer.StartSpanFromActivity(SpanName, activity);
}
else
{
span = (Span)tracer.StartSpan(SpanName);
}
var anotherActivity = new Activity(SpanName).Start();
span.End();
Assert.Same(anotherActivity, Activity.Current);
}
private void AssertApproxSameTimestamp(DateTimeOffset one, DateTimeOffset two)
{
var timeShift = Math.Abs((one - two).TotalMilliseconds);
Assert.InRange(timeShift, 0, 20);
}
public void Dispose()
{
Activity.Current = null;
}
}
}