Пример #1
0
        public async Task Invoke(HttpContext context)
        {
            if (!_hasProvider)
            {
                await _next.Invoke(context);

                return;
            }

            try
            {
                // Add Telemetry.
                using (var activity = _activitySource?.StartActivity("Create Arc4u Principal", ActivityKind.Producer))
                {
                    var clientSecret = GetClientSecretIfExist(context, _option.SecretKey);

                    bool basicAuthenticationSecret = !String.IsNullOrWhiteSpace(clientSecret);

                    bool certificateAuthenticationSecret = false;
                    // Check for a secret key encrypted.
                    if (_hasCertificate && !basicAuthenticationSecret)
                    {
                        clientSecret = GetClientSecretIfExist(context, _option.Certificate.SecretKey);
                        certificateAuthenticationSecret = !String.IsNullOrWhiteSpace(clientSecret);
                    }

                    if (!String.IsNullOrWhiteSpace(clientSecret))
                    {
                        CredentialsResult credential = null;

                        if (basicAuthenticationSecret)
                        {
                            credential = GetCredential(clientSecret);
                        }

                        if (certificateAuthenticationSecret)
                        {
                            credential = GetCertificateCredential(clientSecret);
                        }

                        if (null != credential)
                        {
                            // Get an Access Token.
                            var tokenInfo = await _provider.GetTokenAsync(_option.Settings, credential);

                            // Replace the Basic Authorization by the access token in the header.
                            var authorization = new AuthenticationHeaderValue("Bearer", tokenInfo.AccessToken).ToString();
                            context.Request.Headers.Remove("Authorization");
                            context.Request.Headers.Add("Authorization", authorization);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Technical().Exception(ex).Log();
            }

            await _next(context);
        }
Пример #2
0
        public async Task Invoke(HttpContext context)
        {
            if (context.User.Identity.IsAuthenticated && context.User.Identity.AuthenticationType.Equals(_options.OpenIdSettings.Values[TokenKeys.AuthenticationTypeKey], StringComparison.InvariantCultureIgnoreCase))
            {
                if (context.User is AppPrincipal principal)
                {
                    context.Request?.Headers?.Add("activityid", principal?.ActivityID.ToString() ?? Guid.NewGuid().ToString());
                    if (null != principal?.Profile?.CurrentCulture)
                    {
                        context.Request?.Headers?.Add("culture", principal.Profile.CurrentCulture.TwoLetterISOLanguageName);
                    }
                }

                IContainerResolve container = (IContainerResolve)context.RequestServices.GetService(typeof(IContainerResolve));

                // Not thread safe => can call activity source factory more than one but factory implementation is thread safe => so few calls on the start of the service is a possibility.
                if (!hasAlreadyTriedToResolve)
                {
                    hasAlreadyTriedToResolve = true;
                    _activitySource          = container.Resolve <IActivitySourceFactory>()?.GetArc4u();
                }

                using (var activity = _activitySource?.StartActivity("Inject bearer token in header", ActivityKind.Producer))
                {
                    ITokenProvider provider = container.Resolve <ITokenProvider>(_options.OpenIdSettings.Values[TokenKeys.ProviderIdKey]);

                    var tokenInfo = await provider.GetTokenAsync(_options.OpenIdSettings, context.User.Identity);

                    var authorization = new AuthenticationHeaderValue("Bearer", tokenInfo.AccessToken).ToString();
                    context.Request.Headers.Remove("Authorization");
                    context.Request.Headers.Add("Authorization", authorization);
                }
            }
            await _next.Invoke(context);
        }
Пример #3
0
        public async Task CompleteAsync()
        {
            var messages     = QueueMessages;
            var pubSubEvents = PubSubEvents;

            if (messages.Count > 0 || pubSubEvents.Count > 0)
            {
                using (var activity = _activitySource?.StartActivity("Send to KubeMQ"))
                {
                    if (messages.Count > 0)
                    {
                        activity?.AddTag("QueueMessages", messages.Count);
                        _logger.Technical().System($"{messages.Count} messages will be sent to queues.").Log();
                        _queueMessageSender.SendBatch(messages);
                    }



                    if (pubSubEvents.Count > 0)
                    {
                        activity?.AddTag("PubSubMessages", pubSubEvents.Count);
                        _logger.Technical().System($"{pubSubEvents.Count} event(s) will be sent to publishers.").Log();
                        await _publisher.PublishBatchAsync(pubSubEvents);
                    }

                    // Clear because messages have been processed.
                    _messagesToPublish.Clear();
                }
            }
        }
        public async Task Invoke(HttpContext context)
        {
            if (!_hasProvider)
            {
                await _next(context);

                return;
            }

            try
            {
                TokenInfo tokenInfo;

                if (context.Request.Headers.ContainsKey("Authorization"))
                {
                    if (context.Request.Headers.TryGetValue("Authorization", out var authzValue) && authzValue.Any(value => value.Contains("Basic")))
                    {
                        // Add Telemetry.
                        using (var activity = _activitySource?.StartActivity("BasicAuthentication", ActivityKind.Producer))
                        {
                            var cacheKey = BuildKey(authzValue);

                            tokenInfo = _tokenCache?.Get <TokenInfo>(cacheKey);
                            if (null == tokenInfo || tokenInfo.ExpiresOnUtc < DateTime.UtcNow.AddMinutes(1))
                            {
                                var credential = GetCredential(authzValue);

                                if (null != credential && credential.CredentialsEntered)
                                {
                                    // Get an Access Token.
                                    tokenInfo = await _provider.GetTokenAsync(_option.Settings, credential);

                                    _tokenCache?.Put(cacheKey, tokenInfo);
                                }
                            }

                            if (null != tokenInfo)
                            {
                                // Replace the Basic Authorization by the access token in the header.
                                var authorization = new AuthenticationHeaderValue("Bearer", tokenInfo.AccessToken).ToString();
                                context.Request.Headers.Remove("Authorization");
                                context.Request.Headers.Add("Authorization", authorization);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Technical().Exception(ex).Log();
            }

            await _next(context);
        }
Пример #5
0
        public void Publish <TEvent>(TEvent @event, Dictionary <string, string> tags = null) where TEvent : notnull
        {
            using (var activity = _activitySource?.StartActivity("Send PubSub event to KubeMQ", ActivityKind.Producer))
            {
                if (typeof(TEvent) is IEnumerable)
                {
                    throw new ArgumentException("Collection are not accepted!");
                }

                var definition = GetDefiniton(typeof(TEvent));

                var channel = CreateChannel(definition);

                var serializer = GetSerializer(definition);

                string activityId = _applicationContext?.Principal?.ActivityID.ToString() ?? Guid.NewGuid().ToString();

                KubeMQEvent message = null;
                using (var serializerActivity = _activitySource?.StartActivity("Serialize.", ActivityKind.Producer))
                    message = new KubeMQEvent(null, string.Empty, serializer.Serialize(@event).ToArray(), BuildTags(typeof(TEvent), tags, activityId));

                using (var sendActivity = _activitySource?.StartActivity("Send to KubeMQ", ActivityKind.Producer))
                    channel.SendEvent(message);
            }
        }
Пример #6
0
        public void Send <TMessage>(TMessage message, Dictionary <string, string> tags = null, uint?sendAfterSeconds = null, uint?expireAfterSeconds = null) where TMessage : notnull
        {
            using (var activity = _activitySource?.StartActivity("Send message to KubeMQ", ActivityKind.Producer))
            {
                activity?.AddTag("Delay message [s]", sendAfterSeconds);
                activity?.AddTag("Expire message after [s]", expireAfterSeconds);

                if (message is IEnumerable)
                {
                    throw new ArgumentException("Collection are not accepted!");
                }

                var messageType = typeof(TMessage);

                var definition = GetDefiniton(messageType);

                var policy = definition.RetryPolicy(Policy.Handle <Exception>());

                QueueStream queue = null;
                policy.Execute(() => queue = _queueStreamManager.Get(definition));

                if (null == queue)
                {
                    throw new NullReferenceException($"No queue was created for Address: {definition.Address} and Identitfier {definition.Identifier}");
                }

                var serializer = GetSerializer(definition);

                string activityId = _applicationContext?.Principal?.ActivityID.ToString() ?? Guid.NewGuid().ToString();

                Message _message = null;
                using (var serializerActivity = _activitySource?.StartActivity("Serialize.", ActivityKind.Producer))
                    _message = new Message(definition.Namespace, serializer.Serialize(message), String.Empty, Guid.NewGuid().ToString(), BuildTags(messageType, tags, activityId))
                    {
                        Policy = BuildPolicy(definition, sendAfterSeconds, expireAfterSeconds)
                    };

                using (var sendActivity = _activitySource?.StartActivity("Send to KubeMQ", ActivityKind.Producer))
                    queue.Send(new SendRequest(new List <Message>(1)
                    {
                        _message
                    }));
            }
        }
Пример #7
0
        internal static object Run()
        {
            System.Console.WriteLine("Hello World!");

            using var openTelemetry = Sdk.CreateTracerProviderBuilder()
                                      .AddHttpClientInstrumentation()
                                      .SetResource(Resources.CreateServiceResource("http-service-example"))
                                      .AddSource("http-client-test")
                                      .AddConsoleExporter()
                                      .Build();

            var source = new ActivitySource("http-client-test");

            using (var parent = source.StartActivity("incoming request", ActivityKind.Server))
            {
                using var client = new HttpClient();
                client.GetStringAsync("http://bing.com").GetAwaiter().GetResult();
            }

            System.Console.ReadLine();

            return(null);
        }
        public void GeneratePartAEnvelope_Activity_WithResource()
        {
            using ActivitySource activitySource = new ActivitySource(ActivitySourceName);
            using var activity = activitySource.StartActivity(
                      ActivityName,
                      ActivityKind.Client,
                      parentContext: default,
                      startTime: DateTime.UtcNow);

            var resource = CreateTestResource(serviceName: "my-service", serviceInstance: "my-instance");

            var monitorTags = AzureMonitorConverter.EnumerateActivityTags(activity);

            var telemetryItem = TelemetryPartA.GetTelemetryItem(activity, ref monitorTags, resource, null);

            Assert.Equal("RemoteDependency", telemetryItem.Name);
            Assert.Equal(TelemetryPartA.FormatUtcTimestamp(activity.StartTimeUtc), telemetryItem.Time);
            Assert.Equal("my-service", telemetryItem.Tags[ContextTagKeys.AiCloudRole.ToString()]);
            Assert.Equal("my-instance", telemetryItem.Tags[ContextTagKeys.AiCloudRoleInstance.ToString()]);
            Assert.Equal(activity.TraceId.ToHexString(), telemetryItem.Tags[ContextTagKeys.AiOperationId.ToString()]);
            Assert.Equal(SdkVersionUtils.SdkVersion, telemetryItem.Tags[ContextTagKeys.AiInternalSdkVersion.ToString()]);
            Assert.Throws <KeyNotFoundException>(() => telemetryItem.Tags[ContextTagKeys.AiOperationParentId.ToString()]);
        }
        public void SetErrorStatusOnExceptionDefault()
        {
            using var activitySource = new ActivitySource(ActivitySourceName);
            using var tracerProvider = OpenTelemetrySdk.CreateTracerProviderBuilder()
                                       .AddSource(ActivitySourceName)
                                       .SetSampler(new AlwaysOnSampler())
                                       .Build();

            Activity activity = null;

            try
            {
                using (activity = activitySource.StartActivity("Activity"))
                {
                    throw new Exception("Oops!");
                }
            }
            catch (Exception)
            {
            }

            Assert.Equal(StatusCode.Unset, activity.GetStatus().StatusCode);
        }
        public void StackdriverExporter_TraceClientThrows_ExportResultFailure()
        {
            Exception    exception          = null;
            ExportResult result             = ExportResult.Success;
            const string ActivitySourceName = "stackdriver.test";
            var          source             = new ActivitySource(ActivitySourceName);
            var          traceClientMock    = new Mock <TraceServiceClient>(MockBehavior.Strict);

            traceClientMock.Setup(x =>
                                  x.BatchWriteSpans(It.IsAny <BatchWriteSpansRequest>(), It.IsAny <CallSettings>()))
            .Throws(new RpcException(Status.DefaultCancelled))
            .Verifiable($"{nameof(TraceServiceClient.BatchWriteSpans)} was never called");
            var activityExporter = new StackdriverTraceExporter("test", traceClientMock.Object);
            var testExporter     = new TestExporter <Activity>(RunTest);

            var processor = new BatchActivityExportProcessor(testExporter);

            for (int i = 0; i < 10; i++)
            {
                using Activity activity = source.StartActivity("Test Activity");
                processor.OnEnd(activity);
            }

            processor.Shutdown();

            void RunTest(Batch <Activity> batch)
            {
                exception = Record.Exception(() =>
                {
                    result = activityExporter.Export(batch);
                });
            }

            Assert.Null(exception);
            Assert.StrictEqual(ExportResult.Failure, result);
            traceClientMock.VerifyAll();
        }
        private static Activity OnFunctionStart(ILambdaContext context = null)
        {
            Activity activity = null;

            var parentContext = AWSLambdaUtils.GetParentContext();

            if (parentContext != default)
            {
                var activityName = AWSLambdaUtils.GetFunctionName(context);
                activity = AWSLambdaActivitySource.StartActivity(activityName, ActivityKind.Server, parentContext);

                if (activity != null && context != null)
                {
                    if (activity.IsAllDataRequested)
                    {
                        if (context.AwsRequestId != null)
                        {
                            activity.SetTag(AWSLambdaSemanticConventions.AttributeFaasExecution, context.AwsRequestId);
                        }

                        var functionArn = context.InvokedFunctionArn;
                        if (functionArn != null)
                        {
                            activity.SetTag(AWSLambdaSemanticConventions.AttributeFaasID, functionArn);

                            var accountId = AWSLambdaUtils.GetAccountId(functionArn);
                            if (accountId != null)
                            {
                                activity.SetTag(AWSLambdaSemanticConventions.AttributeCloudAccountID, accountId);
                            }
                        }
                    }
                }
            }

            return(activity);
        }
Пример #12
0
        public void ExportResultIsSuccess()
        {
#if NETCOREAPP3_1
            // Adding the OtlpExporter creates a GrpcChannel.
            // This switch must be set before creating a GrpcChannel/HttpClient when calling an insecure gRPC service.
            // See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client
            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
#endif

            var exporterOptions = new OtlpExporterOptions
            {
#if NETCOREAPP3_1 || NET5_0
                Endpoint = new System.Uri($"http://{CollectorEndpoint}"),
#else
                Endpoint = CollectorEndpoint,
#endif
            };

            var otlpExporter            = new OtlpExporter(exporterOptions);
            var delegatingExporter      = new DelegatingTestExporter <Activity>(otlpExporter);
            var exportActivityProcessor = new SimpleActivityExportProcessor(delegatingExporter);

            var activitySourceName = "otlp.collector.test";

            var builder = Sdk.CreateTracerProviderBuilder()
                          .AddSource(activitySourceName)
                          .AddProcessor(exportActivityProcessor);

            using var tracerProvider = builder.Build();

            var source   = new ActivitySource(activitySourceName);
            var activity = source.StartActivity("Test Activity");
            activity?.Stop();

            Assert.Single(delegatingExporter.ExportResults);
            Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]);
        }
Пример #13
0
        public async Task Run()
        {
            using var act = source.StartActivity(GetType().Name);
            act?.SetParentId(Tracer.CurrentSpan.Context.TraceId, Tracer.CurrentSpan.Context.SpanId);

            // If a tasks tag is inlucded in the tag list, we can either have included or excluded it.
            if (Config.ExludeTags)
            {
                if (Config.WithTags.Any(t => t.Equals(Tag, StringComparison.InvariantCultureIgnoreCase)))
                {
                    l($"Skipping task {GetType().Name} because it's negated with tags (NOT {String.Join(',', Config.WithTags)})");
                    return;
                }
            }
            else if (Config.WithTags.Count > 0 && !Config.WithTags.Any(t => t.Equals(Tag, StringComparison.InvariantCultureIgnoreCase)))
            {
                l($"Skipping task {GetType().Name} (with tag {Tag}) because it's not included in to run tags ({String.Join(',', Config.WithTags)})");
                return;
            }

            // Should we run the validation checks?
            var hasValidation = typeof(IValidation).IsAssignableFrom(this.GetType());

            if (hasValidation && Config.Validate)
            {
                act?.SetTag("validated", true);
                await((IValidation)this).Validate();
            }

            // If we're in a run to only validate the config, don't change things
            if (!Config.OnlyValidate)
            {
                await Do();
            }

            ReportRateLimits();
        }
        private static void ProcessRequest(HttpWebRequest request)
        {
            if (!WebRequestActivitySource.HasListeners() || IsRequestInstrumented(request))
            {
                // No subscribers to the ActivitySource or this request was instrumented by previous
                // ProcessRequest, such is the case with redirect responses where the same request is sent again.
                return;
            }

            var activity = WebRequestActivitySource.StartActivity(ActivityName, ActivityKind.Client);

            if (activity == null)
            {
                // There is a listener but it decided not to sample the current request.
                return;
            }

            IAsyncResult asyncContext = writeAResultAccessor(request);

            if (asyncContext != null)
            {
                // Flow here is for [Begin]GetRequestStream[Async].

                AsyncCallbackWrapper callback = new AsyncCallbackWrapper(request, activity, asyncCallbackAccessor(asyncContext));
                asyncCallbackModifier(asyncContext, callback.AsyncCallback);
            }
            else
            {
                // Flow here is for [Begin]GetResponse[Async] without a prior call to [Begin]GetRequestStream[Async].

                asyncContext = readAResultAccessor(request);
                AsyncCallbackWrapper callback = new AsyncCallbackWrapper(request, activity, asyncCallbackAccessor(asyncContext));
                asyncCallbackModifier(asyncContext, callback.AsyncCallback);
            }

            AddRequestTagsAndInstrumentRequest(request, activity);
        }
    public void IHttpActivityFeatureIsAssignedToIfItExists()
    {
        var testSource  = new ActivitySource(Path.GetRandomFileName());
        var dummySource = new ActivitySource(Path.GetRandomFileName());

        using var listener = new ActivityListener
              {
                  ShouldListenTo = activitySource => (ReferenceEquals(activitySource, testSource) ||
                                                      ReferenceEquals(activitySource, dummySource)),
                  Sample = (ref ActivityCreationOptions <ActivityContext> _) => ActivitySamplingResult.AllData
              };
        ActivitySource.AddActivityListener(listener);

        var hostingApplication = CreateApplication(activitySource: testSource);
        var httpContext        = new DefaultHttpContext();

        httpContext.Features.Set <IHttpActivityFeature>(new TestHttpActivityFeature());
        var context = hostingApplication.CreateContext(httpContext.Features);

        var activityFeature = context.HttpContext.Features.Get <IHttpActivityFeature>();

        Assert.NotNull(activityFeature);
        Assert.IsType <TestHttpActivityFeature>(activityFeature);
        Assert.NotNull(activityFeature.Activity);
        Assert.Equal(HostingApplicationDiagnostics.ActivityName, activityFeature.Activity.DisplayName);
        var initialActivity = Activity.Current;

        // Create nested dummy Activity
        using var _ = dummySource.StartActivity("DummyActivity");

        Assert.Same(initialActivity, activityFeature.Activity);
        Assert.NotEqual(Activity.Current, activityFeature.Activity);

        // Act/Assert
        hostingApplication.DisposeContext(context, null);
    }
        public void ProcessorDoesNotBlockOnExporter()
        {
            var activityExporter = new TestActivityExporter(async _ => await Task.Delay(500));

            using var openTelemetry = Sdk.CreateTracerProviderBuilder()
                                      .AddSource("random")
                                      .AddProcessor(new SimpleActivityProcessor(activityExporter))
                                      .Build();

            ActivitySource source   = new ActivitySource("random");
            var            activity = source.StartActivity("somename");

            // does not block
            var sw = Stopwatch.StartNew();

            activity.Stop();
            sw.Stop();

            Assert.InRange(sw.Elapsed, TimeSpan.Zero, TimeSpan.FromMilliseconds(100));

            var exported = this.WaitForSpans(activityExporter, 1, TimeSpan.FromMilliseconds(600));

            Assert.Single(exported);
        }
Пример #17
0
        public void CheckingCustomActivityProcessor()
        {
            const string          ActivitySourceName    = "zpages.test";
            Guid                  requestId             = Guid.NewGuid();
            TestActivityProcessor testActivityProcessor = new TestActivityProcessor();

            bool startCalled = false;
            bool endCalled   = false;

            testActivityProcessor.StartAction =
                (a) =>
            {
                startCalled = true;
            };

            testActivityProcessor.EndAction =
                (a) =>
            {
                endCalled = true;
            };

            using var tracerProvider = Sdk.CreateTracerProviderBuilder()
                                       .AddSource(ActivitySourceName)
                                       .AddProcessor(testActivityProcessor)
                                       .AddZPagesExporter()
                                       .Build();

            using var source   = new ActivitySource(ActivitySourceName);
            using var activity = source.StartActivity("Test Zipkin Activity");
            activity?.Stop();

            Assert.True(startCalled);
            Assert.True(endCalled);

            ZPagesActivityTracker.Reset();
        }
Пример #18
0
        public void TestActivitySourceAttachedObject()
        {
            RemoteExecutor.Invoke(() => {
                // All Activities created through the constructor should have same source.
                Assert.True(object.ReferenceEquals(new Activity("a1").Source, new Activity("a2").Source));
                Assert.Equal("", new Activity("a3").Source.Name);
                Assert.Equal(string.Empty, new Activity("a4").Source.Version);

                using ActivitySource aSource = new ActivitySource("SourceToTest", "1.2.3.4");

                // Ensure at least we have a listener to allow Activity creation
                using ActivityListener listener        = new ActivityListener();
                listener.ActivityStarted               = activity => Assert.NotNull(activity);
                listener.ActivityStopped               = activity => Assert.NotNull(activity);
                listener.ShouldListenTo                = (activitySource) => object.ReferenceEquals(aSource, activitySource);
                listener.GetRequestedDataUsingParentId = (ref ActivityCreationOptions <string> activityOptions) => ActivityDataRequest.AllData;
                listener.GetRequestedDataUsingContext  = (ref ActivityCreationOptions <ActivityContext> activityOptions) => ActivityDataRequest.AllData;

                ActivitySource.AddActivityListener(listener);

                using Activity activity = aSource.StartActivity("ActivityToTest");
                Assert.True(object.ReferenceEquals(aSource, activity.Source));
            }).Dispose();
        }
        public async Task SendMessage <T> (T messageBody) where T : class
        {
            var producerTopic = configuration.GetSection("ProducerTopic").Value;
            var activityName  = $"{producerTopic} send";
            var props         = new BasicProperties();

            using (var activity = ActivitySource.StartActivity(activityName, ActivityKind.Producer)) {
                if (activity != null)
                {
                    TextFormat.Inject(activity.Context, props, this.InjectTraceContextIntoBasicProperties);
                    AddMessagingTags(activity);

                    using (var producer = new ProducerBuilder <Null, T> (producerConfig).Build()) {
                        try {
                            var dr = await producer.ProduceAsync(producerTopic, new Message <Null, T> {
                                Value = messageBody, Headers = props.Headers
                            });
                        } catch (ProduceException <Null, string> e) {
                            Console.WriteLine($"Delivery failed: {e.Error.Reason}");
                        }
                    }
                }
            }
        }
        public void ProcessorDoesNotBlockOnExporter()
        {
            this.activityExporter = new TestActivityExporter(async _ => await Task.Delay(500));
            this.openTelemetry    = OpenTelemetrySdk.CreateTracerProvider(b => b
                                                                          .AddActivitySource("cijo")
                                                                          .AddProcessorPipeline(p => p
                                                                                                .SetExporter(this.activityExporter)
                                                                                                .SetExportingProcessor(e => new SimpleActivityProcessor(e))));

            ActivitySource source   = new ActivitySource("cijo");
            var            activity = source.StartActivity("somename");

            // does not block
            var sw = Stopwatch.StartNew();

            activity.Stop();
            sw.Stop();

            Assert.InRange(sw.Elapsed, TimeSpan.Zero, TimeSpan.FromMilliseconds(100));

            var exported = this.WaitForSpans(this.activityExporter, 1, TimeSpan.FromMilliseconds(600));

            Assert.Single(exported);
        }
Пример #21
0
    public static void Main()
    {
        using var tracerProvider = Sdk.CreateTracerProviderBuilder()
                                   .AddSource("OTel.Demo")
                                   .Build();

        using var loggerFactory = LoggerFactory.Create(builder =>
        {
            builder
            .AddJsonConsole(options => { options.IncludeScopes = true; })
            .Configure(options => options.ActivityTrackingOptions =
                           ActivityTrackingOptions.TraceId |
                           ActivityTrackingOptions.SpanId);
        });

        var logger = loggerFactory.CreateLogger <Program>();

        logger.LogInformation("Hello, World!");

        using (var activity = DemoSource.StartActivity("Foo"))
        {
            logger.LogInformation("Hello, World!");
        }
    }
Пример #22
0
        public async Task Invoke(HttpContext context)
        {
            // Get the scoped instance of the container!
            IContainerResolve container = (IContainerResolve)context.RequestServices.GetService(typeof(IContainerResolve));
            var logger = container.Resolve <ILogger <ClaimsPrincipalMiddleware> >();

            try
            {
                // if we have some part of the site not in MVC (like swagger) and we need to force
                // authentication. We can add the start of the path to check and in this case we force a login!
                if (null != context.User && !context.User.Identity.IsAuthenticated)
                {
                    if (_option.OpenIdOptions.ForceAuthenticationForPaths.Any(r =>
                    {
                        return(r.Last().Equals('*') ?
                               context.Request.Path.Value.StartsWith(r.Remove(r.Length - 1), StringComparison.InvariantCultureIgnoreCase)
                            :
                               context.Request.Path.Value.Equals(r, StringComparison.InvariantCultureIgnoreCase));
                    }))
                    {
                        logger.Technical().System("Force an OpenId connection.").Log();
                        var cleanUri = new Uri(new Uri(context.Request.GetEncodedUrl()).GetLeftPart(UriPartial.Path));
                        if (Uri.TryCreate(_option.RedirectAuthority, UriKind.Absolute, out var authority))
                        {
                            cleanUri = new Uri(authority, cleanUri.AbsolutePath);
                        }
                        var properties = new AuthenticationProperties()
                        {
                            RedirectUri = cleanUri.ToString()
                        };
                        await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, properties);

                        return;
                    }
                }

                if (null != context.User && context.User.Identity.IsAuthenticated)
                {
                    logger.Technical().System("Create the principal.").Log();

                    // Add Telemetry.
                    using (var activity = _activitySource?.StartActivity("Create Arc4u Principal", ActivityKind.Producer))
                    {
                        // As the extension point can use some ITokenProvider based on the user.
                        // A dummy Principal is created based on the context identity!
                        // Must be registered as Scoped!
                        if (container.TryResolve <IApplicationContext>(out var applicationContext))
                        {
                            applicationContext.SetPrincipal(new AppPrincipal(new Authorization(), context.User.Identity, "S-1-0-0"));
                        }

                        // Load Claims from an external source if necessary!
                        if (_option.ClaimsFillerOptions.LoadClaimsFromClaimsFillerProvider)
                        {
                            await LoadExtraClaimsAsync(context, container, logger);
                        }

                        // Build an AppPrincipal.
                        AppPrincipal principal           = null;
                        var          profileFiller       = container.Resolve <IClaimProfileFiller>();
                        var          authorizationFiller = container.Resolve <IClaimAuthorizationFiller>();

                        var authorization = authorizationFiller.GetAuthorization(context.User.Identity);
                        var profile       = profileFiller.GetProfile(context.User.Identity);
                        principal = new AppPrincipal(authorization, context.User.Identity, profile.Sid)
                        {
                            Profile = profile
                        };

                        // Check if we have an ActivityID.
                        var activityIdHeader = context.Request?.Headers?.FirstOrDefault(h => h.Key.Equals("activityid", StringComparison.InvariantCultureIgnoreCase));

                        if (null == activityIdHeader || !activityIdHeader.HasValue || String.IsNullOrWhiteSpace(activityIdHeader.Value.Key) || StringValues.Empty == activityIdHeader.Value || activityIdHeader.Value.Value.Count == 0)
                        {
                            principal.ActivityID = Guid.NewGuid();
                        }
                        else
                        {
                            Guid activityId = Guid.Empty;
                            if (Guid.TryParse(activityIdHeader.Value.Value[0], out activityId) && activityId != Guid.Empty)
                            {
                                logger.Technical().Information($"Set the activity to the principal based on the caller information: {activityId}.").Log();
                            }
                            else
                            {
                                logger.Technical().Information($"The activity id given by the caller is not a valid Guid. A new one has been assigned.").Log();
                                activityId = Guid.NewGuid();
                            }

                            principal.ActivityID = activityId;
                        }
                        activity?.AddTag(LoggingConstants.ActivityId, principal.ActivityID);
                        // Check for a culture.
                        var cultureHeader = context.Request?.Headers?.FirstOrDefault(h => h.Key.Equals("culture", StringComparison.InvariantCultureIgnoreCase));
                        if (null != cultureHeader && cultureHeader.HasValue && StringValues.Empty != activityIdHeader.Value && cultureHeader.Value.Value.Count > 0)
                        {
                            try
                            {
                                principal.Profile.CurrentCulture = new CultureInfo(cultureHeader.Value.Value[0]);
                            }
                            catch (Exception ex)
                            {
                                logger.Technical().Exception(ex).Log();
                            }
                        }
                        logger.Technical().System("Set AppPrincipal to the HttpContext.User.").Log();
                        context.User = principal;

                        if (null != applicationContext)
                        {
                            logger.Technical().System("Set AppPrincipal to the ApplicationContext.").Log();
                            applicationContext.SetPrincipal(principal);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Technical().Exception(ex).Log();
            }

            await _next(context);
        }
Пример #23
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            ObjectResult result = null;

            using (var activity = MyActivitySource.StartActivity("Foo", ActivityKind.Server, req.Headers["traceparent"]))
            {
                if (activity.ParentId != null)
                {
                    Console.WriteLine("Parentid is" + activity.ParentId);
                }

                activity?.SetTag("enviroment", "Shabuhabs.AzureFunctions-central-sub1");
                activity?.SetTag("Fooz-Functionz", "OTel .NET Rocks!");

                var startTime = DateTimeOffset.Now;

                string name = req.Query["name"];

                string  requestBody = await new StreamReader(req.Body).ReadToEndAsync();
                dynamic data        = JsonConvert.DeserializeObject(requestBody);
                name ??= data?.name;

                log.LogInformation("Foo Function is Running . .. . HttpTrigger");

                activity?.SetTag("query.name", name ?? "no-name");

                string responseMessage = string.IsNullOrEmpty(name)
                    ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
                    : $"Hello, {name}. This HTTP triggered function executed successfully.";

                try
                {
                    // Used to simulate errors only !! ( send in name=crash or name=slow)
                    if (name == "crash")
                    {
                        throw new ArgumentException("CRASHED, 'crash' is invalid arg!");
                    }
                    else if (name == "slow")
                    {
                        System.Threading.Thread.Sleep(4000);
                    }

                    result = new OkObjectResult(responseMessage);
                    activity?.SetTag("http.status_code", 200);
                    activity?.SetTag("error", false);
                }
                catch (Exception e)
                {
                    result = new ExceptionResult(e, includeErrorDetail: true);
                    activity?.SetTag("error.message", e.Message);
                    activity?.SetTag("http.status_code", 500);
                    activity?.SetTag("error", true);
                    log.LogError("Foo Function FAIL!");
                }
                finally
                {
                }
            }
            return(result);
        }
Пример #24
0
        static async Task Main(string[] args)
        {
            var opts = new OptionSet()
            {
                { "u|user="******"(REQUIRED) A github user with admin access.", u => Config.Github.User = u }
                , { "t|token=", "(REQUIRED) A github token that has admin access to the org.", t => Config.Github.Token = t }
                , { "o|org=", "The organization we need to run the actions against (defaults to `sectigo-eng`)", o => Config.Github.Org = o }
                , { "teams=", "A file with the desired team structure in JSON format. If this is set, this will enforce that team structure (including removing members from teams). We expect a Dictionary where the key is the team name, and the value a list of string with the user login names.", ts => Config.TeamStructureFile = ts }
                , { "repos=", "A file with the desired repository teams access in JSON format.", rs => Config.RepoStructureFile = rs }
                , { "report=", "The path were we output the audit report. This defaults to ./", o => Config.ReportingPath = o }
                , { "d|dryrun=", "Should this run authoritative updates (`no`-dryrun), or only display changes (`yes`.Run a dryrun please). Must be either 'yes' or 'no'. Defaults to 'yes'.", (string dry) => Config.DryRunMode = dry.ToLower() }
                , { "no-dryrun", "Run authoritative updates. This can be destructive. This is the greedy option over --dryrun=yes|no.", d => Config.DryRunMode = "no" }
                , { "validate=", "Should this run include validation checks .Runes X exist or not). Excluding these (or `only` running these) saves significant api hits for rate limiting. Must be either `yes` or `no` or `only`. Defaults to `yes`", (string val) => Config.ValidationMode = val.ToLower() }
                , { "tags=", "Only runs the tasks listed here. Note that if this argument is specified, and is inclusive, any tasks that do not have tags will not be run. Expects a CSV. By default everything runs. Current options are `OnlyTeams`", tags => Config.WithTags = new List <string>(tags.Split(',').Where(s => !string.IsNullOrWhiteSpace(s))) }
                , { "e=", "Can only be used with --tags. While exclude any tags listed from the current run. Can be used as a flag, or value of `yes` or `no` Defaults to off.)", (string exclude) => Config.ExludeTagsMode = exclude }
                , { "contextPath=", "If set, will load all the supported manifests (yaml or yml) from that directory, and those will apply to the settings. Path has to exists.", c => Config.ContextPath = c }
                , { "h|help", p => Config.Help = true }
            };

            try
            {
                opts.Parse(args);
                Console.WriteLine($"Current configuration: {Config.ToString()}");
            }
            catch (OptionException e)
            {
                Console.WriteLine(e.Message);
                return;
            }

            if (!Config.ValidateInput() || Config.Help)
            {
                opts.WriteOptionDescriptions(Console.Out);
                return;
            }

            if (Config.Honeycomb.Validate())
            {
                Console.WriteLine("Setting up telemetry");
                source = new ActivitySource("gitman.App", "1.0.0");
                tracer = Sdk
                         .CreateTracerProviderBuilder()
                         .AddHoneycomb(new HoneycombOptions
                {
                    ServiceName = Config.Honeycomb.ServiceName,
                    ApiKey      = Config.Honeycomb.ApiKey,
                    Dataset     = Config.Honeycomb.Dataset
                })
                         .AddSource("gitman.*")
                         .Build();
            }

            using var root = source?.StartActivity("app");

            if (!Config.DryRun)
            {
                Console.WriteLine("\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                Console.WriteLine("!!!                                 Non-DryRun mode                                     !!!");
                if (Config.OnlyValidate)
                {
                    Console.WriteLine("!!!       But w're only running in validation mode, so there shouldn't be changes.      !!!");
                }
                else
                {
                    Console.WriteLine("!!!                              Changes WILL BE authorative                            !!!");
                }
                Console.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
            }

            // For the github sdk to allow us to specify our GithubHttpCache, we have to add all this other
            // nonsense to it.
            client = new GitHubClient(new Connection(
                                          new ProductHeaderValue("SuperMassiveCLI")
                                          , GitHubClient.GitHubApiUrl
                                          , new InMemoryCredentialStore(Credentials.Anonymous)
                                          // this is the only thing that really matters in this instantiation. There is no other way
                                          // to set this up.
                                          , new Octokit.Internal.HttpClientAdapter(() => new Helpers.GithubHttpCache())
                                          , new Octokit.Internal.SimpleJsonSerializer()
                                          ));
            client.Credentials = new Credentials(Config.Github.User, Config.Github.Token);

            var loader = new ResourceLoader();

            if (!string.IsNullOrEmpty(Config.ContextPath))
            {
                loader.LoadResources(Config.ContextPath);
            }

            Console.WriteLine("\n\nChecking merge setting");
            await new Merging {
                Client = client, Loader = loader
            }.Run();

            Console.WriteLine("\n\nChecking branch protections");
            await new Protection()
            {
                Client = client, Loader = loader
            }.Run();

            Console.WriteLine("\n\nPerforming team audit");
            var audit = new Audit(outputPath: Config.ReportingPath)
            {
                Client = client
            };
            await audit.Run();

            if (Config.HasTeamsStructureFile)
            {
                var repository_description = GetRepositoryDescription();
                var proposed_teams         = GetTeams();

                Console.WriteLine($"\n\nChecking for solo collaborators");
                await new OnlyTeams(repository_description, proposed_teams)
                {
                    Client = client
                }.Run();

                Console.WriteLine("\n\nChecking teams");
                await new Teams(proposed_teams)
                {
                    Client = client
                }.Run();

                Console.WriteLine("\n\nChecking teams memberships");
                await new TeamMemberships(proposed_teams)
                {
                    Client = client
                }.Run();


                if (Config.HasRepoStructureFile)
                {
                    Console.WriteLine("\n\nChecking collaborators repository access");
                    await new RepositoryAccess(repository_description, proposed_teams.Keys)
                    {
                        Client = client
                    }.Run();
                }
            }
        }
        public static Activity CreateActivityWithKind(ActivitySource source)
        {
            var activity = source.StartActivity("name", ActivityKind.Client);

            return(activity);
        }
Пример #26
0
        private static async Task RunAsync()
        {
            var endpoint = "/delay/5ms";

            using var listener = new HttpEventListener();

            source = new ActivitySource("http-client-test");

            var serviceCollection = new ServiceCollection();

            serviceCollection.AddHttpOptionsTelemetry(builder => builder.AddConsoleExporter());


            serviceCollection.AddHttpClientOptions(options =>
            {
                options.ServiceName           = "service";
                options.Handler.MaxConnection = 500;
                _server.ConfigureWireMockServer(options);
            });


            var services =
                serviceCollection.BuildServiceProvider();
            await Task.WhenAll(services.GetServices <IHostedService>()
                               .Select(e => e.StartAsync(CancellationToken.None)));

            var factory = services.GetRequiredService <IHttpClientFactory>();
            var client  = factory.CreateClient("service");

            var stopwatch = Stopwatch.StartNew();

            Console.WriteLine("oooo: ");

            do
            {
                try
                {
                    await client.GetAsync(endpoint).ConfigureAwait(false);
                }
                catch (Exception e)
                {
                }
            } while (Console.ReadKey().Key != ConsoleKey.Escape);


            do
            {
                try
                {
                    var activityLinks = new List <ActivityLink>();
                    var initialTags   = new ActivityTagsCollection();

                    initialTags["com.mycompany.product.mytag1"] = "tagValue1";
                    initialTags["com.mycompany.product.mytag2"] = "tagValue2";

                    var linkedContext1 = new ActivityContext(
                        ActivityTraceId.CreateRandom(),
                        ActivitySpanId.CreateRandom(),
                        ActivityTraceFlags.None);

                    var linkedContext2 = new ActivityContext(
                        ActivityTraceId.CreateRandom(),
                        ActivitySpanId.CreateRandom(),
                        ActivityTraceFlags.Recorded);

                    activityLinks.Add(new ActivityLink(linkedContext1));
                    activityLinks.Add(new ActivityLink(linkedContext2));


                    using var activity = source.StartActivity(
                              "ActivityWithLinks",
                              ActivityKind.Server,
                              default(ActivityContext),
                              initialTags,
                              activityLinks);
                    var latencyStats = await TrafficGenerator
                                       .GenerateTraffic(100, () => client.GetAsync(endpoint))
                                       .Latency()
                                       .TakeUntil(DateTimeOffset.Now.AddSeconds(20));

                    Console.WriteLine(latencyStats.Print());
                    await client.GetAsync(endpoint).ConfigureAwait(false);

                    System.Console.WriteLine("Press Enter key to continue.");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            } while (Console.ReadKey().Key != ConsoleKey.Escape);
        }
        internal static object Run(ConsoleOptions options)
        {
            // Enable OpenTelemetry for the source "MyCompany.MyProduct.MyWebServer"
            // and use a single pipeline with a custom MyProcessor, and Console exporter.
            using var openTelemetry = OpenTelemetrySdk.EnableOpenTelemetry(
                      (builder) => builder.AddActivitySource("MyCompany.MyProduct.MyWebServer")
                      .SetResource(Resources.CreateServiceResource("MyServiceName"))
                      .UseConsoleExporter(opt => opt.DisplayAsJson = options.DisplayAsJson,
                                          (p) => p.AddProcessor((next) => new MyProcessor(next))));

            // The above line is required only in applications
            // which decide to use Open Telemetry.

            // Libraries would simply write the following lines of code to
            // emit activities, which are the .NET representation of OT Spans.
            var source = new ActivitySource("MyCompany.MyProduct.MyWebServer");

            // The below commented out line shows more likely code in a real world webserver.
            // using (var parent = source.StartActivity("HttpIn", ActivityKind.Server, HttpContext.Request.Headers["traceparent"] ))
            using (var parent = source.StartActivity("HttpIn", ActivityKind.Server))
            {
                // TagNames can follow the OT guidelines
                // from https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/trace/semantic_conventions
                parent?.AddTag("http.method", "GET");
                parent?.AddTag("http.host", "MyHostName");
                if (parent != null)
                {
                    parent.DisplayName = "HttpIn DisplayName";

                    // IsAllDataRequested is equivalent of Span.IsRecording
                    if (parent.IsAllDataRequested)
                    {
                        parent.AddTag("expensive data", "This data is expensive to obtain. Avoid it if activity is not being recorded");
                    }
                }

                try
                {
                    // Actual code to achieve the purpose of the library.
                    // For websebserver example, this would be calling
                    // user middlware pipeline.

                    // There can be child activities.
                    // In this example HttpOut is a child of HttpIn.
                    using (var child = source.StartActivity("HttpOut", ActivityKind.Client))
                    {
                        child?.AddTag("http.url", "www.mydependencyapi.com");
                        try
                        {
                            // do actual work.

                            child?.AddEvent(new ActivityEvent("sample activity event."));
                            child?.AddTag("http.status_code", "200");
                        }
                        catch (Exception)
                        {
                            child?.AddTag("http.status_code", "500");
                        }
                    }

                    parent?.AddTag("http.status_code", "200");
                }
                catch (Exception)
                {
                    parent?.AddTag("http.status_code", "500");
                }
            }

            System.Console.WriteLine("Press Enter key to exit.");

            return(null);
        }
Пример #28
0
        public override async Task <HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
            // check inputs
            if (context.Deadline == DateTime.MaxValue)
            {   // enforce deadline
                context.Status = new Status(StatusCode.Cancelled, "No Deadline Supplied");
                _logger.LogWarning("Caller '{Peer}' Omitted Deadline", context.Peer);
            }
            else if (string.IsNullOrWhiteSpace(request.Name))
            {   // require name
                context.Status = new Status(StatusCode.InvalidArgument, "'Name' is Required");
            }
            else if (request.Name.Equals("Mud", StringComparison.InvariantCultureIgnoreCase))
            {   // Does anyone else remember this song?
                context.Status = new Status(StatusCode.InvalidArgument, "Your name is not Mud");
            }

            // are we OK?
            if (context.Status.StatusCode != StatusCode.OK)
            {
                // record validation failure as trace event
                Activity.Current?.AddEvent(new ActivityEvent(context.Status.ToString()));

                if (context.Status.StatusCode == StatusCode.InvalidArgument)
                {
                    _logger.LogWarning("Input Validation Failed: {@Request}", request);
                }

                return(null);
            }

            // try to get our reply from the cache
            using (Activity tryCacheActivity = _activitySource.StartActivity("Check-Cache"))
            {
                tryCacheActivity?.AddTag("cache.searchKey", request.Name);
                HelloReply cacheHit = await _cache.GetProto <HelloReply>(request.Name);

                if (cacheHit != null)
                {
                    tryCacheActivity?.AddTag("cache.result", "hit");
                    return(cacheHit);
                }

                tryCacheActivity?.AddTag("cache.result", "miss");
            }

            // build new record add to database
            var record = new GreetingRecord {
                Name = request.Name, Utc = DateTime.UtcNow
            };

            using (Activity insertActivity = _activitySource.StartActivity("Insert-Sql-Record"))
            {
                int recId = await _repository.InsertGreeting(record, context.CancellationToken);

                insertActivity?.AddTag("record.id", recId.ToString());

                _logger.LogInformation("Inserted new greeting record {@record}", record);
            }

            // create reply and add to cache
            var reply = new HelloReply {
                Message = $"{record.Name} said hello at {record.Utc:HH:mm:ss.fff} UTC."
            };

            using (_activitySource.StartActivity("Add-To-Cache"))
                await _cache.SetProto(request.Name, reply, TimeSpan.FromMinutes(1));

            // OK
            return(reply);
        }
Пример #29
0
        internal static Activity CreateTestActivity(
            bool setAttributes = true,
            Dictionary <string, object> additionalAttributes = null,
            bool addEvents    = true,
            bool addLinks     = true,
            Resource resource = null,
            ActivityKind kind = ActivityKind.Client)
        {
            var startTimestamp = DateTime.UtcNow;
            var endTimestamp   = startTimestamp.AddSeconds(60);
            var eventTimestamp = DateTime.UtcNow;
            var traceId        = ActivityTraceId.CreateFromString("e8ea7e9ac72de94e91fabc613f9686b2".AsSpan());

            var parentSpanId = ActivitySpanId.CreateFromBytes(new byte[] { 12, 23, 34, 45, 56, 67, 78, 89 });

            var attributes = new Dictionary <string, object>
            {
                { "stringKey", "value" },
                { "longKey", 1L },
                { "longKey2", 1 },
                { "doubleKey", 1D },
                { "doubleKey2", 1F },
                { "boolKey", true },
                { "int_array", new int[] { 1, 2 } },
                { "bool_array", new bool[] { true, false } },
                { "double_array", new double[] { 1.0, 1.1 } },
                { "string_array", new string[] { "a", "b" } },
            };

            if (additionalAttributes != null)
            {
                foreach (var attribute in additionalAttributes)
                {
                    attributes.Add(attribute.Key, attribute.Value);
                }
            }

            var events = new List <ActivityEvent>
            {
                new ActivityEvent(
                    "Event1",
                    eventTimestamp,
                    new ActivityTagsCollection(new Dictionary <string, object>
                {
                    { "key", "value" },
                })),
                new ActivityEvent(
                    "Event2",
                    eventTimestamp,
                    new ActivityTagsCollection(new Dictionary <string, object>
                {
                    { "key", "value" },
                })),
            };

            var linkedSpanId = ActivitySpanId.CreateFromString("888915b6286b9c41".AsSpan());

            var activitySource = new ActivitySource(nameof(CreateTestActivity));

            var tags = setAttributes ?
                       attributes
                    : null;
            var links = addLinks ?
                        new[]
            {
                new ActivityLink(new ActivityContext(
                                     traceId,
                                     linkedSpanId,
                                     ActivityTraceFlags.Recorded)),
            }
                    : null;

            var activity = activitySource.StartActivity(
                "Name",
                kind,
                parentContext: new ActivityContext(traceId, parentSpanId, ActivityTraceFlags.Recorded),
                tags,
                links,
                startTime: startTimestamp);

            if (addEvents)
            {
                foreach (var evnt in events)
                {
                    activity.AddEvent(evnt);
                }
            }

            activity.SetEndTime(endTimestamp);
            activity.Stop();

            return(activity);
        }
Пример #30
0
        public void ToOtlpSpanTest()
        {
            var activitySource = new ActivitySource(nameof(this.ToOtlpSpanTest));

            using var rootActivity = activitySource.StartActivity("root", ActivityKind.Producer);

            var attributes = new List <KeyValuePair <string, object> >
            {
                new KeyValuePair <string, object>("bool", true),
                new KeyValuePair <string, object>("long", 1L),
                new KeyValuePair <string, object>("string", "text"),
                new KeyValuePair <string, object>("double", 3.14),
                new KeyValuePair <string, object>("int", 1),
                new KeyValuePair <string, object>("datetime", DateTime.UtcNow),
                new KeyValuePair <string, object>("bool_array", new bool[] { true, false }),
                new KeyValuePair <string, object>("int_array", new int[] { 1, 2 }),
                new KeyValuePair <string, object>("double_array", new double[] { 1.0, 2.09 }),
                new KeyValuePair <string, object>("string_array", new string[] { "a", "b" }),
            };

            foreach (var kvp in attributes)
            {
                rootActivity.SetTag(kvp.Key, kvp.Value);
            }

            var startTime = new DateTime(2020, 02, 20, 20, 20, 20, DateTimeKind.Utc);

            DateTimeOffset dateTimeOffset;

#if NET452
            dateTimeOffset = DateTimeOffsetExtensions.FromUnixTimeMilliseconds(0);
#else
            dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(0);
#endif

            var expectedUnixTimeTicks = (ulong)(startTime.Ticks - dateTimeOffset.Ticks);
            var duration = TimeSpan.FromMilliseconds(1555);

            rootActivity.SetStartTime(startTime);
            rootActivity.SetEndTime(startTime + duration);

            Span <byte> traceIdSpan = stackalloc byte[16];
            rootActivity.TraceId.CopyTo(traceIdSpan);
            var traceId = traceIdSpan.ToArray();

            var otlpSpan = rootActivity.ToOtlpSpan();

            Assert.NotNull(otlpSpan);
            Assert.Equal("root", otlpSpan.Name);
            Assert.Equal(OtlpTrace.Span.Types.SpanKind.Producer, otlpSpan.Kind);
            Assert.Equal(traceId, otlpSpan.TraceId);
            Assert.Empty(otlpSpan.ParentSpanId);
            Assert.Null(otlpSpan.Status);
            Assert.Empty(otlpSpan.Events);
            Assert.Empty(otlpSpan.Links);
            AssertOtlpAttributes(attributes, otlpSpan.Attributes);

            var expectedStartTimeUnixNano = 100 * expectedUnixTimeTicks;
            Assert.Equal(expectedStartTimeUnixNano, otlpSpan.StartTimeUnixNano);
            var expectedEndTimeUnixNano = expectedStartTimeUnixNano + (duration.TotalMilliseconds * 1_000_000);
            Assert.Equal(expectedEndTimeUnixNano, otlpSpan.EndTimeUnixNano);

            var childLinks = new List <ActivityLink> {
                new ActivityLink(rootActivity.Context, new ActivityTagsCollection(attributes))
            };
            var childActivity = activitySource.StartActivity(
                "child",
                ActivityKind.Client,
                rootActivity.Context,
                links: childLinks);

            childActivity.SetStatus(Status.Error);

            var childEvents = new List <ActivityEvent> {
                new ActivityEvent("e0"), new ActivityEvent("e1", default, new ActivityTagsCollection(attributes))