// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton <IWeatherDataSource, WeatherDataSource>(); services.AddOpenTelemetryTracing( builder => builder .SetResourceBuilder(ResourceBuilder.CreateEmpty().AddService("task-processor")) .AddBlueprintInstrumentation() .AddConsoleExporter() .AddJaegerExporter() ); services.AddHangfire(h => { h .UseStorage(new SqlServerStorage("Server=(localdb)\\MSSQLLocalDB;Integrated Security=true;Initial Catalog=blueprint-examples")) .UseRecommendedSerializerSettings() .UseDashboardMetric(SqlServerStorage.ActiveConnections) .UseDashboardMetric(SqlServerStorage.TotalConnections) .UseDashboardMetric(DashboardMetrics.FailedCount) .UseDashboardMetric(DashboardMetrics.ProcessingCount) .UseDashboardMetric(DashboardMetrics.ScheduledCount) .UseDashboardMetric(DashboardMetrics.EnqueuedCountOrNull); }); services.AddHangfireServer(); services.AddBlueprintApi(a => a .AddBackgroundTasks(b => b.UseHangfire()) .SetApplicationName("SampleTaskProcessor") .Operations(o => o .Scan(typeof(Blueprint.Sample.WebApi.Startup).Assembly)) .AddLogging()); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSingleton <IWeatherDataSource, WeatherDataSource>(); services.AddHangfire(h => { h .UseStorage(new SqlServerStorage("Server=(localdb)\\MSSQLLocalDB;Integrated Security=true;Initial Catalog=blueprint-examples")) .UseRecommendedSerializerSettings(); }); services.AddOpenTelemetryTracing( builder => builder .SetResourceBuilder(ResourceBuilder.CreateEmpty().AddService("web-api")) .AddAspNetCoreInstrumentation() .AddBlueprintInstrumentation() .AddJaegerExporter() .AddConsoleExporter() ); services.AddBlueprintApi(b => b .Http() .SetApplicationName("SampleWebApi") .Operations(o => o.Scan(typeof(WebApi.Startup).Assembly)) .AddTasksClient(t => t.UseHangfire()) .AddOpenApi() .AddLogging() .AddHateoasLinks() .AddAuthentication(a => a.UseContextLoader <AnonymousUserAuthorisationContextFactory>()) .AddAuthorisation() .AddResourceEvents <NullResourceEventRepository>()); }
public void ServiceResource_AutoGenerateServiceInstanceIdOff() { var resource = ResourceBuilder.CreateEmpty().AddService("my-service", autoGenerateServiceInstanceId: false).Build(); Assert.Single(resource.Attributes); Assert.Contains(new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceName, "my-service"), resource.Attributes); }
public void ServiceResource_ServiceNameAndInstance() { var resource = ResourceBuilder.CreateEmpty().AddService("my-service", serviceInstanceId: "123").Build(); Assert.Equal(2, resource.Attributes.Count()); Assert.Contains(new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceName, "my-service"), resource.Attributes); Assert.Contains(new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceInstance, "123"), resource.Attributes); }
public void ServiceResource_ServiceName() { var resource = ResourceBuilder.CreateEmpty().AddService("my-service").Build(); Assert.Equal(2, resource.Attributes.Count()); Assert.Contains(new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceName, "my-service"), resource.Attributes); Assert.Single(resource.Attributes.Where(kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName)); Assert.True(Guid.TryParse((string)resource.Attributes.Single(kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceInstance).Value, out _)); }
public void ToOtlpResourceMetricsTest(bool includeServiceNameInResource) { var resourceBuilder = ResourceBuilder.CreateEmpty(); if (includeServiceNameInResource) { resourceBuilder.AddAttributes( new List <KeyValuePair <string, object> > { new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceName, "service-name"), new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceNamespace, "ns1"), }); } var metrics = new List <Metric>(); using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{includeServiceNameInResource}", "0.0.1"); using var provider = Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(resourceBuilder) .AddMeter(meter.Name) .AddInMemoryExporter(metrics) .Build(); var counter = meter.CreateCounter <int>("counter"); counter.Add(100); provider.ForceFlush(); var batch = new Batch <Metric>(metrics.ToArray(), metrics.Count); var request = new OtlpCollector.ExportMetricsServiceRequest(); request.AddMetrics(resourceBuilder.Build().ToOtlpResource(), batch); Assert.Single(request.ResourceMetrics); var resourceMetric = request.ResourceMetrics.First(); var oltpResource = resourceMetric.Resource; if (includeServiceNameInResource) { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.StringValue == "service-name"); Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceNamespace && kvp.Value.StringValue == "ns1"); } else { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); } Assert.Single(resourceMetric.InstrumentationLibraryMetrics); var instrumentationLibraryMetrics = resourceMetric.InstrumentationLibraryMetrics.First(); Assert.Equal(string.Empty, instrumentationLibraryMetrics.SchemaUrl); Assert.Equal(meter.Name, instrumentationLibraryMetrics.InstrumentationLibrary.Name); Assert.Equal("0.0.1", instrumentationLibraryMetrics.InstrumentationLibrary.Version); }
public void JaegerTraceExporter_SetResource_IgnoreServiceResources() { using var jaegerTraceExporter = new JaegerExporter(new JaegerExporterOptions()); var process = jaegerTraceExporter.Process; jaegerTraceExporter.SetResourceAndInitializeBatch(ResourceBuilder.CreateEmpty().AddAttributes(new Dictionary <string, object> { [ResourceSemanticConventions.AttributeServiceName] = "servicename", [ResourceSemanticConventions.AttributeServiceNamespace] = "servicenamespace", }).Build()); Assert.Null(process.Tags); }
public void JaegerTraceExporter_SetResource_CreatesTags() { using var jaegerTraceExporter = new JaegerExporter(new JaegerExporterOptions()); var process = jaegerTraceExporter.Process; jaegerTraceExporter.SetResourceAndInitializeBatch(ResourceBuilder.CreateEmpty().AddAttributes(new Dictionary <string, object> { ["Tag"] = "value", }).Build()); Assert.NotNull(process.Tags); Assert.Single(process.Tags); Assert.Equal("value", process.Tags["Tag"].VStr); }
/// <summary> /// Add AWS Lambda configurations. /// </summary> /// <param name="builder"><see cref="TracerProviderBuilder"/> being configured.</param> /// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns> public static TracerProviderBuilder AddAWSLambdaConfigurations(this TracerProviderBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.AddSource(AWSLambdaUtils.ActivitySourceName); builder.SetResourceBuilder(ResourceBuilder .CreateEmpty() .AddService(AWSLambdaUtils.GetFunctionName(), null, null, false) .AddTelemetrySdk() .AddAttributes(AWSLambdaResourceDetector.Detect())); return(builder); }
public void JaegerTraceExporter_SetResource_CombinesTags() { using var jaegerTraceExporter = new JaegerExporter(new JaegerExporterOptions()); var process = jaegerTraceExporter.Process; process.Tags = new Dictionary<string, JaegerTag> { ["Tag1"] = new KeyValuePair<string, object>("Tag1", "value1").ToJaegerTag() }; jaegerTraceExporter.SetResourceAndInitializeBatch(ResourceBuilder.CreateEmpty().AddAttributes(new Dictionary<string, object> { ["Tag2"] = "value2", }).Build()); Assert.NotNull(process.Tags); Assert.Equal(2, process.Tags.Count); Assert.Equal("value1", process.Tags["Tag1"].VStr); Assert.Equal("value2", process.Tags["Tag2"].VStr); }
public void JaegerTraceExporter_SetResource_UpdatesServiceName() { using var jaegerTraceExporter = new JaegerExporter(new JaegerExporterOptions()); var process = jaegerTraceExporter.Process; process.ServiceName = "TestService"; jaegerTraceExporter.SetResourceAndInitializeBatch(Resource.Empty); Assert.Equal("TestService", process.ServiceName); jaegerTraceExporter.SetResourceAndInitializeBatch(ResourceBuilder.CreateEmpty().AddService("MyService").Build()); Assert.Equal("MyService", process.ServiceName); jaegerTraceExporter.SetResourceAndInitializeBatch(ResourceBuilder.CreateEmpty().AddService("MyService", "MyNamespace").Build()); Assert.Equal("MyNamespace.MyService", process.ServiceName); }
private static ResourceBuilder SetupResourceBuilder(InstrumentationConfig conf) { var resourceBuilder = ResourceBuilder.CreateEmpty() .AddService(conf.ServiceName, serviceVersion: conf.ServiceVersion) .AddAttributes(new Dictionary <string, object> { { OpenTelemetryResourceAttributes.AttributeDeploymentEnvironment, conf.DeploymentEnvironment }, { OpenTelemetryResourceAttributes.AttributeTelemetrySdkLanguage, "dotnet" }, { "lightstep.access_token", conf.LightStepProjectToken } }); if (conf.ResourceEnhancers == null) { return(resourceBuilder); } foreach (var resourceEnhancer in conf.ResourceEnhancers) { resourceEnhancer?.RegisterResourceAttributes(resourceBuilder); } return(resourceBuilder); }
public void ToOtlpResourceTest(bool includeServiceNameInResource) { // Targeted test to cover OTel Resource to OTLP Resource // conversion, independent of signals. var resourceBuilder = ResourceBuilder.CreateEmpty(); if (includeServiceNameInResource) { resourceBuilder.AddService("service-name", "ns1"); } var resource = resourceBuilder.Build(); var otlpResource = resource.ToOtlpResource(); if (includeServiceNameInResource) { Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.StringValue == "service-name"); Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceNamespace && kvp.Value.StringValue == "ns1"); } else { Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); } }
public void ToOtlpResourceSpansTest(bool addResource) { var evenTags = new[] { new KeyValuePair <string, object>("k0", "v0") }; var oddTags = new[] { new KeyValuePair <string, object>("k1", "v1") }; var sources = new[] { new ActivitySource("even", "2.4.6"), new ActivitySource("odd", "1.3.5"), }; using var exporter = new OtlpTraceExporter( new OtlpExporterOptions(), new NoopTraceServiceClient()); if (addResource) { exporter.SetResource( ResourceBuilder.CreateEmpty().AddAttributes( new List <KeyValuePair <string, object> > { new KeyValuePair <string, object>(Resources.ResourceSemanticConventions.AttributeServiceName, "service-name"), new KeyValuePair <string, object>(Resources.ResourceSemanticConventions.AttributeServiceNamespace, "ns1"), }).Build()); } else { exporter.SetResource(Resources.Resource.Empty); } var builder = Sdk.CreateTracerProviderBuilder() .AddSource(sources[0].Name) .AddSource(sources[1].Name); using var openTelemetrySdk = builder.Build(); var processor = new BatchActivityExportProcessor(new TestExporter <Activity>(RunTest)); const int numOfSpans = 10; bool isEven; for (var i = 0; i < numOfSpans; i++) { isEven = i % 2 == 0; var source = sources[i % 2]; var activityKind = isEven ? ActivityKind.Client : ActivityKind.Server; var activityTags = isEven ? evenTags : oddTags; using Activity activity = source.StartActivity($"span-{i}", activityKind, parentContext: default, activityTags); processor.OnEnd(activity); } processor.Shutdown(); void RunTest(Batch <Activity> batch) { var request = new OtlpCollector.ExportTraceServiceRequest(); request.AddBatch(exporter.ProcessResource, batch); Assert.Single(request.ResourceSpans); var oltpResource = request.ResourceSpans.First().Resource; if (addResource) { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == Resources.ResourceSemanticConventions.AttributeServiceName && kvp.Value.StringValue == "service-name"); Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == Resources.ResourceSemanticConventions.AttributeServiceNamespace && kvp.Value.StringValue == "ns1"); } else { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == Resources.ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); } foreach (var instrumentationLibrarySpans in request.ResourceSpans.First().InstrumentationLibrarySpans) { Assert.Equal(numOfSpans / 2, instrumentationLibrarySpans.Spans.Count); Assert.NotNull(instrumentationLibrarySpans.InstrumentationLibrary); var expectedSpanNames = new List <string>(); var start = instrumentationLibrarySpans.InstrumentationLibrary.Name == "even" ? 0 : 1; for (var i = start; i < numOfSpans; i += 2) { expectedSpanNames.Add($"span-{i}"); } var otlpSpans = instrumentationLibrarySpans.Spans; Assert.Equal(expectedSpanNames.Count, otlpSpans.Count); var kv0 = new OtlpCommon.KeyValue { Key = "k0", Value = new OtlpCommon.AnyValue { StringValue = "v0" } }; var kv1 = new OtlpCommon.KeyValue { Key = "k1", Value = new OtlpCommon.AnyValue { StringValue = "v1" } }; var expectedTag = instrumentationLibrarySpans.InstrumentationLibrary.Name == "even" ? kv0 : kv1; foreach (var otlpSpan in otlpSpans) { Assert.Contains(otlpSpan.Name, expectedSpanNames); Assert.Contains(expectedTag, otlpSpan.Attributes); } } } }
public void ToOtlpResourceMetricsTest(bool includeServiceNameInResource) { using var exporter = new OtlpMetricsExporter( new OtlpExporterOptions(), new NoopMetricsServiceClient()); var resourceBuilder = ResourceBuilder.CreateEmpty(); if (includeServiceNameInResource) { resourceBuilder.AddAttributes( new List <KeyValuePair <string, object> > { new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceName, "service-name"), new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceNamespace, "ns1"), }); } var tags = new KeyValuePair <string, object>[] { new KeyValuePair <string, object>("key1", "value1"), new KeyValuePair <string, object>("key2", "value2"), }; var processor = new PullMetricProcessor(new TestExporter <MetricItem>(RunTest), true); using var provider = Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(resourceBuilder) .AddSource("TestMeter") .AddMetricProcessor(processor) .Build(); exporter.ParentProvider = provider; using var meter = new Meter("TestMeter", "0.0.1"); var counter = meter.CreateCounter <int>("counter"); counter.Add(100, tags); var testCompleted = false; // Invokes the TestExporter which will invoke RunTest processor.PullRequest(); Assert.True(testCompleted); void RunTest(Batch <MetricItem> metricItem) { var request = new OtlpCollector.ExportMetricsServiceRequest(); request.AddBatch(exporter.ProcessResource, metricItem); Assert.Single(request.ResourceMetrics); var resourceMetric = request.ResourceMetrics.First(); var oltpResource = resourceMetric.Resource; if (includeServiceNameInResource) { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.StringValue == "service-name"); Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceNamespace && kvp.Value.StringValue == "ns1"); } else { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); } Assert.Single(resourceMetric.InstrumentationLibraryMetrics); var instrumentationLibraryMetrics = resourceMetric.InstrumentationLibraryMetrics.First(); Assert.Equal(string.Empty, instrumentationLibraryMetrics.SchemaUrl); Assert.Equal("TestMeter", instrumentationLibraryMetrics.InstrumentationLibrary.Name); Assert.Equal("0.0.1", instrumentationLibraryMetrics.InstrumentationLibrary.Version); Assert.Single(instrumentationLibraryMetrics.Metrics); foreach (var metric in instrumentationLibraryMetrics.Metrics) { Assert.Equal(string.Empty, metric.Description); Assert.Equal(string.Empty, metric.Unit); Assert.Equal("counter", metric.Name); Assert.Equal(OtlpMetrics.Metric.DataOneofCase.Sum, metric.DataCase); Assert.True(metric.Sum.IsMonotonic); Assert.Equal(OtlpMetrics.AggregationTemporality.Delta, metric.Sum.AggregationTemporality); Assert.Single(metric.Sum.DataPoints); var dataPoint = metric.Sum.DataPoints.First(); Assert.True(dataPoint.StartTimeUnixNano > 0); Assert.True(dataPoint.TimeUnixNano > 0); Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsInt, dataPoint.ValueCase); Assert.Equal(100, dataPoint.AsInt); #pragma warning disable CS0612 // Type or member is obsolete Assert.Empty(dataPoint.Labels); #pragma warning restore CS0612 // Type or member is obsolete OtlpTestHelpers.AssertOtlpAttributes(tags.ToList(), dataPoint.Attributes); Assert.Empty(dataPoint.Exemplars); } testCompleted = true; } }
public void TestCounterToOtlpMetric(string name, string description, string unit, long?longValue, double?doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool isMonotonic, params object[] keysValues) { var metrics = new List <Metric>(); using var meter = new Meter(Utils.GetCurrentMethodName()); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(metrics, metricReaderOptions => { metricReaderOptions.TemporalityPreference = aggregationTemporality; }) .Build(); var attributes = ToAttributes(keysValues).ToArray(); if (longValue.HasValue) { var counter = meter.CreateCounter <long>(name, unit, description); counter.Add(longValue.Value, attributes); } else { var counter = meter.CreateCounter <double>(name, unit, description); counter.Add(doubleValue.Value, attributes); } provider.ForceFlush(); var batch = new Batch <Metric>(metrics.ToArray(), metrics.Count); var request = new OtlpCollector.ExportMetricsServiceRequest(); request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); var resourceMetric = request.ResourceMetrics.Single(); var scopeMetrics = resourceMetric.ScopeMetrics.Single(); var actual = scopeMetrics.Metrics.Single(); Assert.Equal(name, actual.Name); Assert.Equal(description ?? string.Empty, actual.Description); Assert.Equal(unit ?? string.Empty, actual.Unit); Assert.Equal(OtlpMetrics.Metric.DataOneofCase.Sum, actual.DataCase); Assert.Null(actual.Gauge); Assert.NotNull(actual.Sum); Assert.Null(actual.Histogram); Assert.Null(actual.ExponentialHistogram); Assert.Null(actual.Summary); Assert.Equal(isMonotonic, actual.Sum.IsMonotonic); var otlpAggregationTemporality = aggregationTemporality == MetricReaderTemporalityPreference.Cumulative ? OtlpMetrics.AggregationTemporality.Cumulative : OtlpMetrics.AggregationTemporality.Delta; Assert.Equal(otlpAggregationTemporality, actual.Sum.AggregationTemporality); Assert.Single(actual.Sum.DataPoints); var dataPoint = actual.Sum.DataPoints.First(); Assert.True(dataPoint.StartTimeUnixNano > 0); Assert.True(dataPoint.TimeUnixNano > 0); if (longValue.HasValue) { Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsInt, dataPoint.ValueCase); Assert.Equal(longValue, dataPoint.AsInt); } else { Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsDouble, dataPoint.ValueCase); Assert.Equal(doubleValue, dataPoint.AsDouble); } if (attributes.Length > 0) { OtlpTestHelpers.AssertOtlpAttributes(attributes, dataPoint.Attributes); } else { Assert.Empty(dataPoint.Attributes); } Assert.Empty(dataPoint.Exemplars); }
public void SendExportRequest_ExportTraceServiceRequest_SendsCorrectHttpRequest(bool includeServiceNameInResource) { // Arrange var evenTags = new[] { new KeyValuePair <string, object>("k0", "v0") }; var oddTags = new[] { new KeyValuePair <string, object>("k1", "v1") }; var sources = new[] { new ActivitySource("even", "2.4.6"), new ActivitySource("odd", "1.3.5"), }; var header1 = new { Name = "hdr1", Value = "val1" }; var header2 = new { Name = "hdr2", Value = "val2" }; var options = new OtlpExporterOptions { Endpoint = new Uri("http://localhost:4317"), Headers = $"{header1.Name}={header1.Value}, {header2.Name} = {header2.Value}", }; var httpHandlerMock = new Mock <HttpMessageHandler>(); HttpRequestMessage httpRequest = null; var httpRequestContent = Array.Empty <byte>(); httpHandlerMock.Protected() #if NET5_0_OR_GREATER .Setup <HttpResponseMessage>("Send", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .Returns((HttpRequestMessage request, CancellationToken token) => { return(new HttpResponseMessage()); }) .Callback <HttpRequestMessage, CancellationToken>((r, ct) => { httpRequest = r; // We have to capture content as it can't be accessed after request is disposed inside of SendExportRequest method httpRequestContent = r.Content.ReadAsByteArrayAsync()?.Result; }) #else .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .ReturnsAsync((HttpRequestMessage request, CancellationToken token) => { return(new HttpResponseMessage()); }) .Callback <HttpRequestMessage, CancellationToken>(async(r, ct) => { httpRequest = r; // We have to capture content as it can't be accessed after request is disposed inside of SendExportRequest method httpRequestContent = await r.Content.ReadAsByteArrayAsync(); }) #endif .Verifiable(); var exportClient = new OtlpHttpTraceExportClient(options, new HttpClient(httpHandlerMock.Object)); var resourceBuilder = ResourceBuilder.CreateEmpty(); if (includeServiceNameInResource) { resourceBuilder.AddAttributes( new List <KeyValuePair <string, object> > { new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceName, "service_name"), new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceNamespace, "ns_1"), }); } var builder = Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(resourceBuilder) .AddSource(sources[0].Name) .AddSource(sources[1].Name); using var openTelemetrySdk = builder.Build(); var processor = new BatchActivityExportProcessor(new TestExporter <Activity>(RunTest)); const int numOfSpans = 10; bool isEven; for (var i = 0; i < numOfSpans; i++) { isEven = i % 2 == 0; var source = sources[i % 2]; var activityKind = isEven ? ActivityKind.Client : ActivityKind.Server; var activityTags = isEven ? evenTags : oddTags; using Activity activity = source.StartActivity($"span-{i}", activityKind, parentContext: default, activityTags); processor.OnEnd(activity); } processor.Shutdown(); void RunTest(Batch <Activity> batch) { var request = new OtlpCollector.ExportTraceServiceRequest(); request.AddBatch(resourceBuilder.Build().ToOtlpResource(), batch); // Act var result = exportClient.SendExportRequest(request); // Assert Assert.True(result); Assert.NotNull(httpRequest); Assert.Equal(HttpMethod.Post, httpRequest.Method); Assert.Equal("http://localhost:4317/v1/traces", httpRequest.RequestUri.AbsoluteUri); Assert.Equal(2, httpRequest.Headers.Count()); Assert.Contains(httpRequest.Headers, h => h.Key == header1.Name && h.Value.First() == header1.Value); Assert.Contains(httpRequest.Headers, h => h.Key == header2.Name && h.Value.First() == header2.Value); Assert.NotNull(httpRequest.Content); Assert.IsType <OtlpHttpTraceExportClient.ExportRequestContent>(httpRequest.Content); Assert.Contains(httpRequest.Content.Headers, h => h.Key == "Content-Type" && h.Value.First() == OtlpHttpTraceExportClient.MediaContentType); var exportTraceRequest = OtlpCollector.ExportTraceServiceRequest.Parser.ParseFrom(httpRequestContent); Assert.NotNull(exportTraceRequest); Assert.Single(exportTraceRequest.ResourceSpans); var resourceSpan = exportTraceRequest.ResourceSpans.First(); if (includeServiceNameInResource) { Assert.Contains(resourceSpan.Resource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.StringValue == "service_name"); Assert.Contains(resourceSpan.Resource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceNamespace && kvp.Value.StringValue == "ns_1"); } else { Assert.Contains(resourceSpan.Resource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); } } }
public void TestGaugeToOtlpMetric(string name, string description, string unit, long?longValue, double?doubleValue) { var metrics = new List <Metric>(); using var meter = new Meter(Utils.GetCurrentMethodName()); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(metrics) .Build(); if (longValue.HasValue) { meter.CreateObservableGauge(name, () => longValue.Value, unit, description); } else { meter.CreateObservableGauge(name, () => doubleValue.Value, unit, description); } provider.ForceFlush(); var batch = new Batch <Metric>(metrics.ToArray(), metrics.Count); var request = new OtlpCollector.ExportMetricsServiceRequest(); request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); var resourceMetric = request.ResourceMetrics.Single(); var instrumentationLibraryMetrics = resourceMetric.InstrumentationLibraryMetrics.Single(); var actual = instrumentationLibraryMetrics.Metrics.Single(); Assert.Equal(name, actual.Name); Assert.Equal(description ?? string.Empty, actual.Description); Assert.Equal(unit ?? string.Empty, actual.Unit); Assert.Equal(OtlpMetrics.Metric.DataOneofCase.Gauge, actual.DataCase); Assert.NotNull(actual.Gauge); Assert.Null(actual.Sum); Assert.Null(actual.Histogram); Assert.Null(actual.ExponentialHistogram); Assert.Null(actual.Summary); Assert.Single(actual.Gauge.DataPoints); var dataPoint = actual.Gauge.DataPoints.First(); Assert.True(dataPoint.StartTimeUnixNano > 0); Assert.True(dataPoint.TimeUnixNano > 0); if (longValue.HasValue) { Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsInt, dataPoint.ValueCase); Assert.Equal(longValue, dataPoint.AsInt); } else { Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsDouble, dataPoint.ValueCase); Assert.Equal(doubleValue, dataPoint.AsDouble); } Assert.Empty(dataPoint.Attributes); Assert.Empty(dataPoint.Exemplars); #pragma warning disable CS0612 // Type or member is obsolete Assert.Null(actual.IntGauge); Assert.Null(actual.IntSum); Assert.Null(actual.IntHistogram); Assert.Empty(dataPoint.Labels); #pragma warning restore CS0612 // Type or member is obsolete }
public void TestHistogramToOltpMetric(string name, string description, string unit, long?longValue, double?doubleValue, AggregationTemporality aggregationTemporality, params object[] keysValues) { var metrics = new List <Metric>(); var metricReader = new BaseExportingMetricReader(new InMemoryExporter <Metric>(metrics)); metricReader.Temporality = aggregationTemporality; using var meter = new Meter(Utils.GetCurrentMethodName()); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader(metricReader) .Build(); var attributes = ToAttributes(keysValues).ToArray(); if (longValue.HasValue) { var histogram = meter.CreateHistogram <long>(name, unit, description); histogram.Record(longValue.Value, attributes); } else { var histogram = meter.CreateHistogram <double>(name, unit, description); histogram.Record(doubleValue.Value, attributes); } provider.ForceFlush(); var batch = new Batch <Metric>(metrics.ToArray(), metrics.Count); var request = new OtlpCollector.ExportMetricsServiceRequest(); request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); var resourceMetric = request.ResourceMetrics.Single(); var instrumentationLibraryMetrics = resourceMetric.InstrumentationLibraryMetrics.Single(); var actual = instrumentationLibraryMetrics.Metrics.Single(); Assert.Equal(name, actual.Name); Assert.Equal(description ?? string.Empty, actual.Description); Assert.Equal(unit ?? string.Empty, actual.Unit); Assert.Equal(OtlpMetrics.Metric.DataOneofCase.Histogram, actual.DataCase); Assert.Null(actual.Gauge); Assert.Null(actual.Sum); Assert.NotNull(actual.Histogram); Assert.Null(actual.ExponentialHistogram); Assert.Null(actual.Summary); var otlpAggregationTemporality = aggregationTemporality == AggregationTemporality.Cumulative ? OtlpMetrics.AggregationTemporality.Cumulative : OtlpMetrics.AggregationTemporality.Delta; Assert.Equal(otlpAggregationTemporality, actual.Histogram.AggregationTemporality); Assert.Single(actual.Histogram.DataPoints); var dataPoint = actual.Histogram.DataPoints.First(); Assert.True(dataPoint.StartTimeUnixNano > 0); Assert.True(dataPoint.TimeUnixNano > 0); Assert.Equal(1UL, dataPoint.Count); if (longValue.HasValue) { Assert.Equal((double)longValue, dataPoint.Sum); } else { Assert.Equal(doubleValue, dataPoint.Sum); } int bucketIndex; for (bucketIndex = 0; bucketIndex < dataPoint.ExplicitBounds.Count; ++bucketIndex) { if (dataPoint.Sum <= dataPoint.ExplicitBounds[bucketIndex]) { break; } Assert.Equal(0UL, dataPoint.BucketCounts[bucketIndex]); } Assert.Equal(1UL, dataPoint.BucketCounts[bucketIndex]); if (attributes.Length > 0) { OtlpTestHelpers.AssertOtlpAttributes(attributes, dataPoint.Attributes); } else { Assert.Empty(dataPoint.Attributes); } Assert.Empty(dataPoint.Exemplars); #pragma warning disable CS0612 // Type or member is obsolete Assert.Null(actual.IntGauge); Assert.Null(actual.IntSum); Assert.Null(actual.IntHistogram); Assert.Empty(dataPoint.Labels); #pragma warning restore CS0612 // Type or member is obsolete }
public void ToOtlpResourceMetricsTest(bool includeServiceNameInResource) { var resourceBuilder = ResourceBuilder.CreateEmpty(); if (includeServiceNameInResource) { resourceBuilder.AddAttributes( new List <KeyValuePair <string, object> > { new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceName, "service-name"), new KeyValuePair <string, object>(ResourceSemanticConventions.AttributeServiceNamespace, "ns1"), }); } var tags = new KeyValuePair <string, object>[] { new KeyValuePair <string, object>("key1", "value1"), new KeyValuePair <string, object>("key2", "value2"), }; using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{includeServiceNameInResource}", "0.0.1"); var exportedItems = new List <Metric>(); using var provider = Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(resourceBuilder) .AddMeter(meter.Name) .AddReader(new BaseExportingMetricReader(new InMemoryExporter <Metric>(exportedItems)) { PreferredAggregationTemporality = AggregationTemporality.Delta }) .Build(); var counter = meter.CreateCounter <int>("counter"); counter.Add(100, tags); var testCompleted = false; provider.ForceFlush(); var batch = new Batch <Metric>(exportedItems.ToArray(), exportedItems.Count); RunTest(batch); Assert.True(testCompleted); void RunTest(Batch <Metric> metrics) { var request = new OtlpCollector.ExportMetricsServiceRequest(); request.AddMetrics(resourceBuilder.Build().ToOtlpResource(), metrics); Assert.Single(request.ResourceMetrics); var resourceMetric = request.ResourceMetrics.First(); var oltpResource = resourceMetric.Resource; if (includeServiceNameInResource) { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.StringValue == "service-name"); Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceNamespace && kvp.Value.StringValue == "ns1"); } else { Assert.Contains(oltpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); } Assert.Single(resourceMetric.InstrumentationLibraryMetrics); var instrumentationLibraryMetrics = resourceMetric.InstrumentationLibraryMetrics.First(); Assert.Equal(string.Empty, instrumentationLibraryMetrics.SchemaUrl); Assert.Equal(meter.Name, instrumentationLibraryMetrics.InstrumentationLibrary.Name); Assert.Equal("0.0.1", instrumentationLibraryMetrics.InstrumentationLibrary.Version); Assert.Single(instrumentationLibraryMetrics.Metrics); foreach (var metric in instrumentationLibraryMetrics.Metrics) { Assert.Equal(string.Empty, metric.Description); Assert.Equal(string.Empty, metric.Unit); Assert.Equal("counter", metric.Name); Assert.Equal(OtlpMetrics.Metric.DataOneofCase.Sum, metric.DataCase); Assert.True(metric.Sum.IsMonotonic); Assert.Equal(OtlpMetrics.AggregationTemporality.Delta, metric.Sum.AggregationTemporality); Assert.Single(metric.Sum.DataPoints); var dataPoint = metric.Sum.DataPoints.First(); Assert.True(dataPoint.StartTimeUnixNano > 0); Assert.True(dataPoint.TimeUnixNano > 0); Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsInt, dataPoint.ValueCase); Assert.Equal(100, dataPoint.AsInt); #pragma warning disable CS0612 // Type or member is obsolete Assert.Empty(dataPoint.Labels); #pragma warning restore CS0612 // Type or member is obsolete OtlpTestHelpers.AssertOtlpAttributes(tags.ToList(), dataPoint.Attributes); Assert.Empty(dataPoint.Exemplars); } testCompleted = true; } }