private static void AssertOtlpAttributeValue(object originalValue, OtlpCommon.AttributeKeyValue akv)
        {
            switch (originalValue)
            {
            case string s:
                Assert.Equal(akv.StringValue, s);
                break;

            case bool b:
                Assert.Equal(akv.BoolValue, b);
                break;

            case long l:
                Assert.Equal(akv.IntValue, l);
                break;

            case double d:
                Assert.Equal(akv.DoubleValue, d);
                break;

            default:
                Assert.Equal(akv.StringValue, originalValue?.ToString());
                break;
            }
        }
예제 #2
0
        private static void AssertOtlpAttributeValue(object originalValue, OtlpCommon.AttributeKeyValue akv)
        {
            switch (originalValue)
            {
            case string s:
                Assert.Equal(s, akv.StringValue);
                Assert.Equal(OtlpCommon.AttributeKeyValue.Types.ValueType.String, akv.Type);
                break;

            case bool b:
                Assert.Equal(b, akv.BoolValue);
                Assert.Equal(OtlpCommon.AttributeKeyValue.Types.ValueType.Bool, akv.Type);
                break;

            case long l:
                Assert.Equal(l, akv.IntValue);
                Assert.Equal(OtlpCommon.AttributeKeyValue.Types.ValueType.Int, akv.Type);
                break;

            case double d:
                Assert.Equal(d, akv.DoubleValue);
                Assert.Equal(OtlpCommon.AttributeKeyValue.Types.ValueType.Double, akv.Type);
                break;

            default:
                Assert.Equal(originalValue.ToString(), akv.StringValue);
                Assert.Equal(OtlpCommon.AttributeKeyValue.Types.ValueType.String, akv.Type);
                break;
            }
        }
        private static OtlpCommon.AttributeKeyValue ToOtlpAttribute(KeyValuePair <string, object> kvp)
        {
            if (kvp.Value == null)
            {
                return(null);
            }

            var attrib = new OtlpCommon.AttributeKeyValue {
                Key = kvp.Key
            };

            switch (kvp.Value)
            {
            case string s:
                attrib.Type        = OtlpCommon.AttributeKeyValue.Types.ValueType.String;
                attrib.StringValue = s;
                break;

            case bool b:
                attrib.Type      = OtlpCommon.AttributeKeyValue.Types.ValueType.Bool;
                attrib.BoolValue = b;
                break;

            case int i:
                attrib.Type     = OtlpCommon.AttributeKeyValue.Types.ValueType.Int;
                attrib.IntValue = i;
                break;

            case long l:
                attrib.Type     = OtlpCommon.AttributeKeyValue.Types.ValueType.Int;
                attrib.IntValue = l;
                break;

            case double d:
                attrib.Type        = OtlpCommon.AttributeKeyValue.Types.ValueType.Double;
                attrib.DoubleValue = d;
                break;

            default:
                attrib.Type        = OtlpCommon.AttributeKeyValue.Types.ValueType.String;
                attrib.StringValue = kvp.Value.ToString();
                break;
            }

            return(attrib);
        }
        private static OtlpCommon.AttributeKeyValue ToOtlpAttribute(KeyValuePair <string, string> kvp)
        {
            // TODO: enforce no duplicate keys?
            // TODO: reverse?
            // To maintain full fidelity to downstream receivers convert to the proper attribute types

            if (kvp.Value == null)
            {
                return(null);
            }

            var attrib = new OtlpCommon.AttributeKeyValue {
                Key = kvp.Key
            };

            if (long.TryParse(kvp.Value, out var longValue))
            {
                attrib.Type     = OtlpCommon.AttributeKeyValue.Types.ValueType.Int;
                attrib.IntValue = longValue;
            }
            else if (double.TryParse(kvp.Value, out var doubleValue))
            {
                attrib.Type        = OtlpCommon.AttributeKeyValue.Types.ValueType.Double;
                attrib.DoubleValue = doubleValue;
            }
            else if (bool.TryParse(kvp.Value, out var boolValue))
            {
                attrib.Type      = OtlpCommon.AttributeKeyValue.Types.ValueType.Bool;
                attrib.BoolValue = boolValue;
            }
            else
            {
                attrib.Type        = OtlpCommon.AttributeKeyValue.Types.ValueType.String;
                attrib.StringValue = kvp.Value;
            }

            return(attrib);
        }
        public void ToOtlpResourceSpansTest()
        {
            var spanProcessor = new Mock <SpanProcessor>();

            var evenResource = new Resource(new [] { new KeyValuePair <string, object>("k0", "v0") });
            var oddResource  = new Resource(new [] { new KeyValuePair <string, object>("k1", "v1") });
            var tracers      = new[]
            {
                TracerFactory.Create(b => b.SetResource(evenResource)
                                     .AddProcessorPipeline(p => p.AddProcessor(_ => spanProcessor.Object)))
                .GetTracer("even", "2.4.6"),
                TracerFactory.Create(b => b.SetResource(oddResource)
                                     .AddProcessorPipeline(p => p.AddProcessor(_ => spanProcessor.Object)))
                .GetTracer("odd", "1.3.5"),
            };

            TelemetrySpan span       = null;
            const int     numOfSpans = 10;

            for (var i = 0; i < numOfSpans; i++)
            {
                var isEven   = i % 2 == 0;
                var tracer   = tracers[i % 2];
                var spanKind = isEven ? SpanKind.Client : SpanKind.Server;
                if (span == null)
                {
                    span = tracer.StartRootSpan("span-0", spanKind, null);
                }
                else
                {
                    span = tracer.StartSpan($"span-{i}", span.Context, spanKind, null);
                }

                span.End();
            }

            var spanDataList = new List <SpanData>();
            var invocations  = spanProcessor.Invocations;

            for (var i = 0; i < invocations.Count; i += 2) // Just want one of the OnStart/OnEnd pair.
            {
                spanDataList.Add((SpanData)invocations[i].Arguments[0]);
            }

            spanDataList.Reverse();

            var otlpResourceSpans = spanDataList.ToOtlpResourceSpans();

            Assert.Equal(2, otlpResourceSpans.Count());

            var evenAttribKeyValue = new OtlpCommon.AttributeKeyValue {
                Key = "k0"
            };

            evenAttribKeyValue.StringValue = "v0";
            foreach (var resourceSpans in otlpResourceSpans)
            {
                Assert.Single(resourceSpans.InstrumentationLibrarySpans);
                Assert.Equal(numOfSpans / 2, resourceSpans.InstrumentationLibrarySpans[0].Spans.Count);
                Assert.NotNull(resourceSpans.Resource);

                var expectedSpanNames = new List <string>();
                var start             = resourceSpans.Resource.Attributes.Contains(evenAttribKeyValue) ? 0 : 1;
                for (var i = start; i < numOfSpans; i += 2)
                {
                    expectedSpanNames.Add($"span-{i}");
                }

                var otlpSpans = resourceSpans.InstrumentationLibrarySpans[0].Spans;
                Assert.Equal(expectedSpanNames.Count, otlpSpans.Count);
                foreach (var otlpSpan in otlpSpans)
                {
                    Assert.Contains(otlpSpan.Name, expectedSpanNames);
                }
            }
        }
        internal static OtlpTrace.Span ToOtlpSpan(this Activity activity)
        {
            if (activity.IdFormat != ActivityIdFormat.W3C)
            {
                // Only ActivityIdFormat.W3C is supported, in principle this should never be
                // hit under the OpenTelemetry SDK.
                return(null);
            }

            // protobuf doesn't understand Span<T> yet: https://github.com/protocolbuffers/protobuf/issues/3431
            Span <byte> traceIdBytes = stackalloc byte[16];
            Span <byte> spanIdBytes  = stackalloc byte[8];

            activity.TraceId.CopyTo(traceIdBytes);
            activity.SpanId.CopyTo(spanIdBytes);

            var parentSpanIdString = ByteString.Empty;

            if (activity.ParentSpanId != default)
            {
                Span <byte> parentSpanIdBytes = stackalloc byte[8];
                activity.ParentSpanId.CopyTo(parentSpanIdBytes);
                parentSpanIdString = ByteString.CopyFrom(parentSpanIdBytes.ToArray());
            }

            var startTimeUnixNano = activity.StartTimeUtc.ToUnixTimeNanoseconds();
            var otlpSpan          = new OtlpTrace.Span
            {
                Name = activity.DisplayName,

                Kind = (OtlpTrace.Span.Types.SpanKind)(activity.Kind + 1), // TODO: there is an offset of 1 on the enum.

                TraceId      = ByteString.CopyFrom(traceIdBytes.ToArray()),
                SpanId       = ByteString.CopyFrom(spanIdBytes.ToArray()),
                ParentSpanId = parentSpanIdString,

                // TODO: Status is still pending, need to pursue OTEL spec change.

                StartTimeUnixNano = (ulong)startTimeUnixNano,
                EndTimeUnixNano   = (ulong)(startTimeUnixNano + activity.Duration.ToNanoseconds()),
            };

            foreach (var kvp in activity.Tags)
            {
                // TODO: attempt to convert to supported types?
                // TODO: enforce no duplicate keys?
                // TODO: drop if Value is null?
                // TODO: reverse?
                var attrib = new OtlpCommon.AttributeKeyValue {
                    Key = kvp.Key, StringValue = kvp.Value
                };
                otlpSpan.Attributes.Add(attrib);
            }

            otlpSpan.Events.AddRange(activity.Events.Select(ToOtlpEvent));
            otlpSpan.Links.AddRange(activity.Links.Select(ToOtlpLink));

            // Activity does not limit number of attributes, events, links, etc so drop counts are always zero.

            return(otlpSpan);
        }