public void MeterProvider_SetDefaultTwice_Throws() { var meterProvider = Sdk.CreateMeterProviderBuilder().Build(); MeterProvider.SetDefault(meterProvider); Assert.Throws <InvalidOperationException>(() => MeterProvider.SetDefault(meterProvider)); }
public void GlobalSetup() { this.meter = new Meter("TestMeter", "1.0.0"); this.responseStream = new MemoryStream(1024 * 1024); this.meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("TestMeter") .AddPrometheusExporter() .Build(); var counter = this.meter.CreateCounter <long>("counter_name_1", "long", "counter_name_1_description"); counter.Add(18, new KeyValuePair <string, object>("label1", "value1"), new KeyValuePair <string, object>("label2", "value2")); var gauge = this.meter.CreateObservableGauge("gauge_name_1", () => 18.0D, "long", "gauge_name_1_description"); var histogram = this.meter.CreateHistogram <long>("histogram_name_1", "long", "histogram_name_1_description"); histogram.Record(100, new KeyValuePair <string, object>("label1", "value1"), new KeyValuePair <string, object>("label2", "value2")); this.context = new DefaultHttpContext(); this.context.Response.Body = this.responseStream; if (!this.meterProvider.TryFindExporter(out this.exporter)) { throw new InvalidOperationException("PrometheusExporter could not be found on MeterProvider."); } this.exporter.Collect(Timeout.Infinite); }
public MetricPointTests() { this.meter = new Meter(Utils.GetCurrentMethodName()); this.histogram = this.meter.CreateHistogram <long>("histogram"); // Evenly distribute the bound values over the range [0, MaxValue) this.bounds = new double[10]; for (int i = 0; i < this.bounds.Length; i++) { this.bounds[i] = i * 1000 / this.bounds.Length; } var exportedItems = new List <Metric>(); this.provider = Sdk.CreateMeterProviderBuilder() .AddMeter(this.meter.Name) .AddInMemoryExporter(exportedItems) .AddView(this.histogram.Name, new ExplicitBucketHistogramConfiguration() { Boundaries = this.bounds }) .Build(); this.histogram.Record(500); this.provider.ForceFlush(); this.metric = exportedItems[0]; var metricPointsEnumerator = this.metric.GetMetricPoints().GetEnumerator(); metricPointsEnumerator.MoveNext(); this.metricPoint = metricPointsEnumerator.Current; }
public void Setup() { this.meter = new Meter(Utils.GetCurrentMethodName()); this.counter = this.meter.CreateCounter <long>("counter"); if (this.ViewConfig == ViewConfiguration.NoView) { this.provider = Sdk.CreateMeterProviderBuilder() .AddMeter(this.meter.Name) .Build(); } else if (this.ViewConfig == ViewConfiguration.ViewNoInstrSelect) { this.provider = Sdk.CreateMeterProviderBuilder() .AddMeter(this.meter.Name) .AddView("nomatch", new MetricStreamConfiguration() { TagKeys = new string[] { "DimName1", "DimName2", "DimName3" } }) .Build(); } else if (this.ViewConfig == ViewConfiguration.ViewSelectsInstr) { this.provider = Sdk.CreateMeterProviderBuilder() .AddMeter(this.meter.Name) .AddView(this.counter.Name, new MetricStreamConfiguration() { TagKeys = new string[] { "DimName1", "DimName2", "DimName3" } }) .Build(); } }
public void ServerEndpointSanityCheckPositiveTest(params string[] uris) { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() .AddPrometheusExporter(opt => { opt.HttpListenerPrefixes = uris; }) .Build(); }
public async Task RequestMetricIsCaptured() { var metricItems = new List <MetricItem>(); var metricExporter = new TestExporter <MetricItem>(ProcessExport); void ProcessExport(Batch <MetricItem> batch) { foreach (var metricItem in batch) { metricItems.Add(metricItem); } } var processor = new PullMetricProcessor(metricExporter, true); this.meterProvider = Sdk.CreateMeterProviderBuilder() .AddAspNetCoreInstrumentation() .AddMetricProcessor(processor) .Build(); using (var client = this.factory.CreateClient()) { var response = await client.GetAsync("/api/values"); response.EnsureSuccessStatusCode(); } // We need to let End callback execute as it is executed AFTER response was returned. // In unit tests environment there may be a lot of parallel unit tests executed, so // giving some breezing room for the End callback to complete await Task.Delay(TimeSpan.FromSeconds(1)); // Invokes the TestExporter which will invoke ProcessExport processor.PullRequest(); this.meterProvider.Dispose(); var requestMetrics = metricItems .SelectMany(item => item.Metrics.Where(metric => metric.Name == "http.server.duration")) .ToArray(); Assert.True(requestMetrics.Length == 1); var metric = requestMetrics[0] as IHistogramMetric; Assert.NotNull(metric); Assert.Equal(1L, metric.PopulationCount); Assert.True(metric.PopulationSum > 0); var bucket = metric.Buckets .Where(b => metric.PopulationSum > b.LowBoundary && metric.PopulationSum <= b.HighBoundary) .FirstOrDefault(); Assert.NotEqual(default, bucket);
public void Setup() { var metricExporter = new TestExporter <Metric>(ProcessExport); void ProcessExport(Batch <Metric> batch) { double sum = 0; foreach (var metric in batch) { if (this.UseWithRef) { // The performant way of iterating. foreach (ref var metricPoint in metric.GetMetricPoints()) { sum += metricPoint.GetCounterSumDouble(); } } else { // The non-performant way of iterating. // This is still "correct", but less performant. foreach (var metricPoint in metric.GetMetricPoints()) { sum += metricPoint.GetCounterSumDouble(); } } } } this.reader = new BaseExportingMetricReader(metricExporter) { Temporality = AggregationTemporality.Cumulative, }; this.meter = new Meter(Utils.GetCurrentMethodName()); this.provider = Sdk.CreateMeterProviderBuilder() .AddMeter(this.meter.Name) .AddReader(this.reader) .Build(); this.counter = this.meter.CreateCounter <double>("counter"); this.token = new CancellationTokenSource(); this.writeMetricTask = new Task(() => { while (!this.token.IsCancellationRequested) { var tag1 = new KeyValuePair <string, object>("DimName1", this.dimensionValues[this.random.Next(0, 10)]); var tag2 = new KeyValuePair <string, object>("DimName2", this.dimensionValues[this.random.Next(0, 10)]); var tag3 = new KeyValuePair <string, object>("DimName3", this.dimensionValues[this.random.Next(0, 10)]); this.counter.Add(100.00, tag1, tag2, tag3); } }); this.writeMetricTask.Start(); }
/// <summary>add MagicOnion Telemetry.</summary> public static IMagicOnionServerBuilder AddOpenTelemetry(this IMagicOnionServerBuilder builder, MagicOnionOpenTelemetryOptions options, Action <MagicOnionOpenTelemetryOptions, MagicOnionOpenTelemetryMeterFactoryOption> configureMeterProvider, Action <MagicOnionOpenTelemetryOptions, IServiceProvider, TracerProviderBuilder> configureTracerProvider) { if (options == null) { throw new ArgumentNullException(nameof(options)); } builder.Services.AddSingleton(options); // configure MeterFactory if (configureMeterProvider != null) { var meterFactoryOption = new MagicOnionOpenTelemetryMeterFactoryOption(); configureMeterProvider(options, meterFactoryOption); MeterProvider.SetDefault(Sdk.CreateMeterProviderBuilder() .SetProcessor(meterFactoryOption.MetricProcessor) .SetExporter(meterFactoryOption.MetricExporter) .SetPushInterval(meterFactoryOption.MetricPushInterval) .Build()); builder.Services.AddSingleton(meterFactoryOption.MetricExporter); if (meterFactoryOption.MeterLogger != null) { builder.Services.AddSingleton <IMagicOnionLogger>(meterFactoryOption.MeterLogger.Invoke(MeterProvider.Default)); } } // configure TracerFactory if (configureTracerProvider != null) { if (string.IsNullOrEmpty(options.MagicOnionActivityName)) { throw new NullReferenceException(nameof(options.MagicOnionActivityName)); } builder.Services.AddOpenTelemetryTracing((provider, builder) => { // ActivitySourceName must match to TracerName. builder.AddSource(options.MagicOnionActivityName); configureTracerProvider?.Invoke(options, provider, builder); }); // Avoid directly register ActivitySource to Singleton for easier identification. var activitySource = new ActivitySource(options.MagicOnionActivityName); var magicOnionActivitySources = new MagicOnionActivitySources(activitySource); builder.Services.AddSingleton(magicOnionActivitySources); } return(builder); }
/// <summary> /// Initializes a new instance of the <see cref="PrometheusExporterMiddleware"/> class. /// </summary> /// <param name="meterProvider"><see cref="MeterProvider"/>.</param> /// <param name="next"><see cref="RequestDelegate"/>.</param> public PrometheusExporterMiddleware(MeterProvider meterProvider, RequestDelegate next) { Guard.ThrowIfNull(meterProvider, nameof(meterProvider)); if (!meterProvider.TryFindExporter(out PrometheusExporter exporter)) { throw new ArgumentException("A PrometheusExporter could not be found configured on the provided MeterProvider."); } this.exporter = exporter; }
public void Setup() { if (this.WithSDK) { this.provider = Sdk.CreateMeterProviderBuilder() .AddSource("TestMeter") // All instruments from this meter are enabled. .Build(); } this.meter = new Meter("TestMeter"); this.counter = this.meter.CreateCounter <long>("counter"); }
public OpenTelemetryCollectorLogger(MeterProvider meterProvider, string metricsPrefix = "magiconion", string version = null, IEnumerable <KeyValuePair <string, string> > defaultLabels = null) { if (meterProvider == null) { throw new ArgumentNullException(nameof(meterProvider)); } // configure defaultTags included as default tag this.defaultLabels = defaultLabels ?? Array.Empty <KeyValuePair <string, string> >(); // todo: how to description? var meter = meterProvider.GetMeter("MagicOnion", version); // Service build time. ms buildServiceDefinitionMeasure = meter.CreateDoubleMeasure($"{metricsPrefix}_buildservicedefinition_duration_milliseconds"); // sum // Unary request count. num unaryRequestCounter = meter.CreateInt64Counter($"{metricsPrefix}_unary_requests_count"); // sum // Unary API request size. bytes unaryRequestSizeMeasure = meter.CreateInt64Measure($"{metricsPrefix}_unary_request_size"); // sum // Unary API response size. bytes unaryResponseSizeMeasure = meter.CreateInt64Measure($"{metricsPrefix}_unary_response_size"); // sum // Unary API error Count. num unaryErrorCounter = meter.CreateInt64Counter($"{metricsPrefix}_unary_error_count"); // sum // Unary API elapsed time. ms unaryElapsedMeasure = meter.CreateDoubleMeasure($"{metricsPrefix}_unary_elapsed_milliseconds"); // sum // StreamingHub error Count. num streamingHubErrorCounter = meter.CreateInt64Counter($"{metricsPrefix}_streaminghub_error_count"); // sum // StreamingHub elapsed time. ms streamingHubElapsedMeasure = meter.CreateDoubleMeasure($"{metricsPrefix}_streaminghub_elapsed_milliseconds"); // sum // StreamingHub request count. num streamingHubRequestCounter = meter.CreateInt64Counter($"{metricsPrefix}_streaminghub_requests_count"); // sum // StreamingHub request size. bytes streamingHubRequestSizeMeasure = meter.CreateInt64Measure($"{metricsPrefix}_streaminghub_request_size"); // sum // StreamingHub response size. bytes streamingHubResponseSizeMeasure = meter.CreateInt64Measure($"{metricsPrefix}_streaminghub_response_size"); // sum // ConnectCount - DisconnectCount = current connect count. (successfully disconnected) // StreamingHub connect count. num streamingHubConnectCounter = meter.CreateInt64Counter($"{metricsPrefix}_streaminghub_connect_count"); // sum // StreamingHub disconnect count. num streamingHubDisconnectCounter = meter.CreateInt64Counter($"{metricsPrefix}_streaminghub_disconnect_count"); // sum // HubBroadcast request count. num broadcastRequestCounter = meter.CreateInt64Counter($"{metricsPrefix}_broadcast_requests_count"); // sum // HubBroadcast request size. num broadcastRequestSizeMeasure = meter.CreateInt64Measure($"{metricsPrefix}_broadcast_request_size"); // sum // HubBroadcast group count. num broadcastGroupCounter = meter.CreateInt64Counter($"{metricsPrefix}_broadcast_group_count"); // sum }
public void Setup() { this.meter = new Meter(Utils.GetCurrentMethodName()); var exportedItems = new List <Metric>(); var reader = new PeriodicExportingMetricReader(new InMemoryExporter <Metric>(exportedItems), 1000); reader.PreferredAggregationTemporality = this.AggregationTemporality; this.provider = Sdk.CreateMeterProviderBuilder() .AddMeter(this.meter.Name) // All instruments from this meter are enabled. .AddReader(reader) .Build(); this.counter = this.meter.CreateCounter <long>("counter"); }
/// <summary>add MagicOnion Telemetry.</summary> public static IServiceCollection AddMagicOnionOpenTelemetry(this IServiceCollection services, MagicOnionOpenTelemetryOptions options, Action <MagicOnionOpenTelemetryOptions, MagicOnionOpenTelemetryMeterFactoryOption> configureMeterProvider, Action <MagicOnionOpenTelemetryOptions, IServiceProvider, TracerProviderBuilder> configureTracerProvider) { if (options == null) { throw new ArgumentNullException(nameof(options)); } services.AddSingleton(options); // configure MeterFactory if (configureMeterProvider != null) { var meterFactoryOption = new MagicOnionOpenTelemetryMeterFactoryOption(); configureMeterProvider(options, meterFactoryOption); MeterProvider.SetDefault(Sdk.CreateMeterProviderBuilder() .SetProcessor(meterFactoryOption.MetricProcessor) .SetExporter(meterFactoryOption.MetricExporter) .SetPushInterval(meterFactoryOption.MetricPushInterval) .Build()); services.AddSingleton(meterFactoryOption.MetricExporter); services.AddSingleton(MeterProvider.Default); } // configure TracerFactory if (configureTracerProvider != null) { if (string.IsNullOrEmpty(options.ActivitySourceName)) { throw new NullReferenceException(nameof(options.ActivitySourceName)); } var tracerFactory = services.AddOpenTelemetryTracerProvider((provider, builder) => { // ActivitySourceName must match to TracerName. builder.AddSource(options.ActivitySourceName); configureTracerProvider(options, provider, builder); }); services.AddSingleton(tracerFactory); services.AddSingleton(new ActivitySource(options.ActivitySourceName)); } return(services); }
public async Task PrometheusExporterMiddlewareIntegration_MeterProvider() { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MeterName) .AddPrometheusExporter() .Build(); await RunPrometheusExporterMiddlewareIntegrationTest( "/metrics", app => app.UseOpenTelemetryPrometheusScrapingEndpoint( meterProvider: meterProvider, predicate: null, path: null, configureBranchedPipeline: null), registerMeterProvider : false).ConfigureAwait(false); }
public void MeterProvider_SetDefault() { var meterProvider = Sdk.CreateMeterProviderBuilder().Build(); MeterProvider.SetDefault(meterProvider); var defaultMeter = MeterProvider.Default.GetMeter(string.Empty); Assert.NotNull(defaultMeter); Assert.IsType <MeterSdk>(defaultMeter); Assert.NotSame(defaultMeter, MeterProvider.Default.GetMeter("named meter")); var counter = defaultMeter.CreateDoubleCounter("ctr"); Assert.IsType <DoubleCounterMetricSdk>(counter); }
public void MeterProvider_UpdateDefault_CachedTracer() { var defaultMeter = MeterProvider.Default.GetMeter(string.Empty); var noOpCounter = defaultMeter.CreateDoubleCounter("ctr"); Assert.IsType <NoOpCounterMetric <double> >(noOpCounter); MeterProvider.SetDefault(Sdk.CreateMeterProvider(b => { })); var counter = defaultMeter.CreateDoubleCounter("ctr"); Assert.IsType <DoubleCounterMetricSdk>(counter); var newdefaultMeter = MeterProvider.Default.GetMeter(string.Empty); Assert.NotSame(defaultMeter, newdefaultMeter); Assert.IsType <MeterSdk>(newdefaultMeter); }
/// <summary> /// Adds OpenTelemetry Prometheus scraping endpoint middleware to an /// <see cref="IApplicationBuilder"/> instance. /// </summary> /// <remarks>Note: A branched pipeline is created based on the <paramref /// name="predicate"/> or <paramref name="path"/>. If neither <paramref /// name="predicate"/> nor <paramref name="path"/> are provided then /// <see cref="PrometheusExporterOptions.ScrapeEndpointPath"/> is /// used.</remarks> /// <param name="app">The <see cref="IApplicationBuilder"/> to add /// middleware to.</param> /// <param name="meterProvider">Optional <see cref="MeterProvider"/> /// containing a <see cref="PrometheusExporter"/> otherwise the primary /// SDK provider will be resolved using application services.</param> /// <param name="predicate">Optional predicate for deciding if a given /// <see cref="HttpContext"/> should be branched. If supplied <paramref /// name="path"/> is ignored.</param> /// <param name="path">Optional path to use for the branched pipeline. /// Ignored if <paramref name="predicate"/> is supplied.</param> /// <param name="configureBranchedPipeline">Optional callback to /// configure the branched pipeline. Called before registration of the /// Prometheus middleware.</param> /// <returns>A reference to the original <see /// cref="IApplicationBuilder"/> for chaining calls.</returns> public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint( this IApplicationBuilder app, MeterProvider meterProvider, Func <HttpContext, bool> predicate, string path, Action <IApplicationBuilder> configureBranchedPipeline) { // Note: Order is important here. MeterProvider is accessed before // GetOptions<PrometheusExporterOptions> so that any changes made to // PrometheusExporterOptions in deferred AddPrometheusExporter // configure actions are reflected. meterProvider ??= app.ApplicationServices.GetRequiredService <MeterProvider>(); if (predicate == null) { if (path == null) { var options = app.ApplicationServices.GetOptions <PrometheusExporterOptions>(); path = options.ScrapeEndpointPath ?? PrometheusExporterOptions.DefaultScrapeEndpointPath; } if (!path.StartsWith("/")) { path = $"/{path}"; } return(app.Map( new PathString(path), builder => { configureBranchedPipeline?.Invoke(builder); builder.UseMiddleware <PrometheusExporterMiddleware>(meterProvider); })); } return(app.MapWhen( context => predicate(context), builder => { configureBranchedPipeline?.Invoke(builder); builder.UseMiddleware <PrometheusExporterMiddleware>(meterProvider); })); }
public void GlobalSetup() { this.meter = new Meter(Utils.GetCurrentMethodName()); this.meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(this.meter.Name) .AddInMemoryExporter(this.metrics) .Build(); var counter = this.meter.CreateCounter <long>("counter_name_1", "long", "counter_name_1_description"); counter.Add(18, new("label1", "value1"), new("label2", "value2")); var gauge = this.meter.CreateObservableGauge("gauge_name_1", () => 18.0D, "long", "gauge_name_1_description"); var histogram = this.meter.CreateHistogram <long>("histogram_name_1", "long", "histogram_name_1_description"); histogram.Record(100, new("label1", "value1"), new("label2", "value2")); this.meterProvider.ForceFlush(); }
public void ServerEndpointSanityCheckNegativeTest(params string[] uris) { try { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() .AddPrometheusExporter(opt => { opt.HttpListenerPrefixes = uris; }) .Build(); } catch (Exception ex) { if (ex is not ArgumentNullException) { Assert.Equal("System.ArgumentException", ex.GetType().ToString()); #if NETFRAMEWORK Assert.Equal("Prometheus server path should be a valid URI with http/https scheme.\r\nParameter name: httpListenerPrefixes", ex.Message); #else Assert.Equal("Prometheus server path should be a valid URI with http/https scheme. (Parameter 'httpListenerPrefixes')", ex.Message); #endif } } }
public MeterProviderTests() { MeterProvider.Reset(); }
async void CreateConfigFile(string setting, string dragoConnexCode) { var dialog = new CommonOpenFileDialog(); dialog.IsFolderPicker = true; CommonFileDialogResult result = dialog.ShowDialog(); if (result == CommonFileDialogResult.Ok) { var configFolder = $"{dialog.FileName} \\Config {dragoConnexCode}\\"; Directory.CreateDirectory(configFolder); Dictionary <string, string> dict = JsonConvert.DeserializeObject <Dictionary <string, string> >(setting); string toWrite = ""; foreach (var item in dict.Reverse().Skip(2).Reverse()) { if (string.IsNullOrEmpty(item.Value)) { toWrite += "\"" + Environment.NewLine; } else { if (!item.Key.Equals("AlarmSetting") || !item.Key.Equals("AMRSetting")) { toWrite += item.Value + Environment.NewLine; } } } toWrite = toWrite.Remove(toWrite.Length - 1); toWrite += Environment.NewLine; File.WriteAllText(configFolder + "NETWORK.csv", toWrite); MeterProvider MeterProvider = new MeterProvider(); toWrite = ""; var meters = await MeterProvider.GetMetersAsync("http://dragoservices.azurewebsites.net/api/DragoAdmin/Meters/DragoConnex/" + dragoConnexCode); if (meters != null) { foreach (var meter in meters) { Dictionary <string, string> dictMeter = JsonConvert.DeserializeObject <Dictionary <string, string> >(meter.MeterSetting); foreach (var item in dictMeter) { toWrite += item.Value + ","; } toWrite = toWrite.Remove(toWrite.Length - 1); toWrite += Environment.NewLine; } File.WriteAllText(configFolder + "METER.csv", toWrite); } var dictAlarmSetting = JsonConvert.DeserializeObject <Dictionary <string, string> >(dict["AlarmSetting"]); toWrite = ""; foreach (var item in dictAlarmSetting) { if (String.IsNullOrEmpty(item.Value)) { toWrite += "0" + ","; } else { toWrite += item.Value + ","; } } toWrite = toWrite.Remove(toWrite.Length - 1); toWrite += Environment.NewLine; File.WriteAllText(configFolder + "ALARM.csv", toWrite); var dictAMRSetting = JsonConvert.DeserializeObject <Dictionary <string, string> >(dict["AMRSetting"]); toWrite = ""; foreach (var item in dictAMRSetting) { if (String.IsNullOrEmpty(item.Value)) { toWrite += "0" + Environment.NewLine; } else { toWrite += item.Value + Environment.NewLine; } } File.WriteAllText(configFolder + "AMR.csv", toWrite); } }
static async Task Main(string[] args) { var config = new ConfigurationBuilder().AddEnvironmentVariables().Build(); var exporterEndpoint = config.GetValue <string>("MeterExporterEndpoint", "http://localhost:9184/metrics/"); var exporterHostingEndpoint = config.GetValue <string>("MeterExporterHostingEndpoint", "http://localhost:9184/metrics/"); var tracerEndpoint = config.GetValue <string>("TracerExporterEndpoint", "http://localhost:9411/api/v2/spans"); // MetricsServer for Prometheus pull model var exporter = new PrometheusExporter(new PrometheusExporterOptions() { Url = exporterEndpoint }); var metricsServer = new PrometheusExporterMetricsHttpServerCustom(exporter, exporterHostingEndpoint); metricsServer.Start(); Console.WriteLine($"Started Metrics Server on {exporterEndpoint}"); /* * Following is sample prometheus.yml config. Adjust port,interval as needed. * scrape_configs: # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config. # - job_name: 'OpenTelemetryTest' # metrics_path defaults to '/metrics' # scheme defaults to 'http'. # static_configs: # - targets: ['localhost:9184'] */ // Metrics (factory is cacheable) var processor = new UngroupedBatcher(); var spanContext = default(SpanContext); MeterProvider.SetDefault(Sdk.CreateMeterProviderBuilder() .SetProcessor(processor) .SetExporter(exporter) .SetPushInterval(TimeSpan.FromSeconds(10)) .Build()); var meterProvider = MeterProvider.Default; var meter = meterProvider.GetMeter("Sample"); var counter = meter.CreateInt64Counter("sample/measure/initialize"); var labels1 = new List <KeyValuePair <string, string> >(); labels1.Add(new KeyValuePair <string, string>("dim1", "value1")); var labels2 = new List <KeyValuePair <string, string> >(); labels2.Add(new KeyValuePair <string, string>("dim2", "value2")); // TracerServer for Zipkin push model (in case you won't run on docker) // $ docker run --rm -p 9411:9411 openzipkin/zipkin // Tracer (factory is cacheable) using var tracerFactory = Sdk.CreateTracerProviderBuilder() .AddSource("Samples.SampleClient", "Samples.SampleServer") .AddZipkinExporter(o => { o.ServiceName = "front-zipkin"; o.Endpoint = new Uri(tracerEndpoint); }) .Build(); using var backEndTracerFactory = Sdk.CreateTracerProviderBuilder() .AddSource("Samples.SampleServer.Redis", "Samples.SampleServer.Db") .AddZipkinExporter(o => { o.ServiceName = "backend-zipkin"; o.Endpoint = new Uri(tracerEndpoint); }) .Build(); Console.WriteLine($"Started Tracer Server on {tracerEndpoint}"); // Execute http://0.0.0.0:19999 using var sample = new InstrumentationWithActivitySource(); sample.Start(); var sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalMinutes < 10) { // metrics counter.Add(spanContext, 100, meter.GetLabelSet(labels1)); await Task.Delay(1000); var remaining = (10 * 60) - sw.Elapsed.TotalSeconds; Console.WriteLine("Running and emitting metrics. Remaining time:" + (int)remaining + " seconds"); } metricsServer.Stop(); }
public void Dispose() { MeterProvider.Reset(); }
public async Task RequestMetricIsCaptured() { var metricItems = new List <Metric>(); this.meterProvider = Sdk.CreateMeterProviderBuilder() .AddAspNetCoreInstrumentation() .AddInMemoryExporter(metricItems) .Build(); using (var client = this.factory.CreateClient()) { var response = await client.GetAsync("/api/values"); response.EnsureSuccessStatusCode(); } // We need to let End callback execute as it is executed AFTER response was returned. // In unit tests environment there may be a lot of parallel unit tests executed, so // giving some breezing room for the End callback to complete await Task.Delay(TimeSpan.FromSeconds(1)); this.meterProvider.Dispose(); var requestMetrics = metricItems .Where(item => item.Name == "http.server.duration") .ToArray(); Assert.True(requestMetrics.Length == 1); var metric = requestMetrics[0]; Assert.NotNull(metric); Assert.True(metric.MetricType == MetricType.Histogram); var metricPoints = new List <MetricPoint>(); foreach (var p in metric.GetMetricPoints()) { metricPoints.Add(p); } Assert.Single(metricPoints); var metricPoint = metricPoints[0]; var count = metricPoint.GetHistogramCount(); var sum = metricPoint.GetHistogramSum(); Assert.Equal(1L, count); Assert.True(sum > 0); /* * var bucket = metric.Buckets * .Where(b => * metric.PopulationSum > b.LowBoundary && * metric.PopulationSum <= b.HighBoundary) * .FirstOrDefault(); * Assert.NotEqual(default, bucket); * Assert.Equal(1, bucket.Count); */ var attributes = new KeyValuePair <string, object> [metricPoint.Tags.Count]; int i = 0; foreach (var tag in metricPoint.Tags) { attributes[i++] = tag; } var method = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpMethod, "GET"); var scheme = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpScheme, "http"); var statusCode = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpStatusCode, "200"); var flavor = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpFlavor, "HTTP/1.1"); var host = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpHost, "localhost"); var target = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpTarget, "api/Values"); Assert.Contains(method, attributes); Assert.Contains(scheme, attributes); Assert.Contains(statusCode, attributes); Assert.Contains(flavor, attributes); Assert.Contains(host, attributes); Assert.Contains(target, attributes); Assert.Equal(6, attributes.Length); }
public void MeterProvider_SetDefaultNull() { Assert.Throws <ArgumentNullException>(() => MeterProvider.SetDefault(null)); }
public void MeterProvider_SetDefaultTwice_Throws() { MeterProvider.SetDefault(Sdk.CreateMeterProvider(b => { })); Assert.Throws <InvalidOperationException>(() => MeterProvider.SetDefault(Sdk.CreateMeterProvider(b => { }))); }
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); if (File.Exists(xmlPath)) { c.IncludeXmlComments(xmlPath); } }); // Switch between Zipkin/Jaeger by setting UseExporter in appsettings.json. var exporter = this.Configuration.GetValue <string>("UseExporter").ToLowerInvariant(); switch (exporter) { case "jaeger": services.AddOpenTelemetryTracing((builder) => builder .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(this.Configuration.GetValue <string>("Jaeger:ServiceName"))) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddJaegerExporter()); services.Configure <JaegerExporterOptions>(this.Configuration.GetSection("Jaeger")); break; case "zipkin": services.AddOpenTelemetryTracing((builder) => builder .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(this.Configuration.GetValue <string>("Zipkin:ServiceName"))) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddZipkinExporter()); services.Configure <ZipkinExporterOptions>(this.Configuration.GetSection("Zipkin")); break; case "otlp": // 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); services.AddOpenTelemetryTracing((builder) => builder .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(this.Configuration.GetValue <string>("Otlp:ServiceName"))) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddOtlpExporter(otlpOptions => { otlpOptions.Endpoint = new Uri(this.Configuration.GetValue <string>("Otlp:Endpoint")); })); break; default: services.AddOpenTelemetryTracing((builder) => builder .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddConsoleExporter()); // For options which can be bound from IConfiguration. services.Configure <AspNetCoreInstrumentationOptions>(this.Configuration.GetSection("AspNetCoreInstrumentation")); // For options which can be configured from code only. services.Configure <AspNetCoreInstrumentationOptions>(options => { options.Filter = (req) => { return(req.Request.Host != null); }; }); break; } // TODO: Add IServiceCollection.AddOpenTelemetryMetrics extension method var providerBuilder = Sdk.CreateMeterProviderBuilder() .AddAspNetCoreInstrumentation(); // TODO: Add configuration switch for Prometheus and OTLP export providerBuilder .AddConsoleExporter(); this.meterProvider = providerBuilder.Build(); }
internal static async Task <object> RunAsync(int port, int pushIntervalInSecs, int totalDurationInMins) { System.Console.WriteLine($"OpenTelemetry Prometheus Exporter is making metrics available at http://localhost:{port}/metrics/"); /* * Following is sample prometheus.yml config. Adjust port,interval as needed. * * scrape_configs: # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config. # - job_name: 'OpenTelemetryTest' # # metrics_path defaults to '/metrics' # scheme defaults to 'http'. # # static_configs: # - targets: ['localhost:9184'] */ // Create and Setup Prometheus Exporter var promOptions = new PrometheusExporterOptions() { Url = $"http://localhost:{port}/metrics/" }; var promExporter = new PrometheusExporter(promOptions); var metricsHttpServer = new PrometheusExporterMetricsHttpServer(promExporter); metricsHttpServer.Start(); // Create Processor (called Batcher in Metric spec, this is still not decided) var processor = new UngroupedBatcher(); // Application which decides to enable OpenTelemetry metrics // would setup a MeterProvider and make it default. // All meters from this factory will be configured with the common processing pipeline. MeterProvider.SetDefault(Sdk.CreateMeterProviderBuilder() .SetProcessor(processor) .SetExporter(promExporter) .SetPushInterval(TimeSpan.FromSeconds(pushIntervalInSecs)) .Build()); // The following shows how libraries would obtain a MeterProvider. // MeterProvider is the entry point, which provides Meter. // If user did not set the Default MeterProvider (shown in earlier lines), // all metric operations become no-ops. var meterProvider = MeterProvider.Default; var meter = meterProvider.GetMeter("MyMeter"); // the rest is purely from Metrics API. var testCounter = meter.CreateInt64Counter("MyCounter"); var testMeasure = meter.CreateInt64Measure("MyMeasure"); var testObserver = meter.CreateInt64Observer("MyObservation", CallBackForMyObservation); var labels1 = new List <KeyValuePair <string, string> >(); labels1.Add(new KeyValuePair <string, string>("dim1", "value1")); var labels2 = new List <KeyValuePair <string, string> >(); labels2.Add(new KeyValuePair <string, string>("dim1", "value2")); var defaultContext = default(SpanContext); Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalMinutes < totalDurationInMins) { testCounter.Add(defaultContext, 100, meter.GetLabelSet(labels1)); testMeasure.Record(defaultContext, 100, meter.GetLabelSet(labels1)); testMeasure.Record(defaultContext, 500, meter.GetLabelSet(labels1)); testMeasure.Record(defaultContext, 5, meter.GetLabelSet(labels1)); testMeasure.Record(defaultContext, 750, meter.GetLabelSet(labels1)); // Obviously there is no testObserver.Oberve() here, as Observer instruments // have callbacks that are called by the Meter automatically at each collection interval. await Task.Delay(1000); var remaining = (totalDurationInMins * 60) - sw.Elapsed.TotalSeconds; System.Console.WriteLine("Running and emitting metrics. Remaining time:" + (int)remaining + " seconds"); } // Stopping metricsHttpServer.Stop(); System.Console.WriteLine("Metrics server shutdown."); System.Console.WriteLine("Press Enter key to exit."); return(null); }
public async Task RequestMetricIsCaptured() { var metricItems = new List <Metric>(); var metricExporter = new TestExporter <Metric>(ProcessExport); void ProcessExport(Batch <Metric> batch) { foreach (var metricItem in batch) { metricItems.Add(metricItem); } } var metricReader = new BaseExportingMetricReader(metricExporter) { PreferredAggregationTemporality = AggregationTemporality.Cumulative, }; this.meterProvider = Sdk.CreateMeterProviderBuilder() .AddAspNetCoreInstrumentation() .AddReader(metricReader) .Build(); using (var client = this.factory.CreateClient()) { var response = await client.GetAsync("/api/values"); response.EnsureSuccessStatusCode(); } // We need to let End callback execute as it is executed AFTER response was returned. // In unit tests environment there may be a lot of parallel unit tests executed, so // giving some breezing room for the End callback to complete await Task.Delay(TimeSpan.FromSeconds(1)); this.meterProvider.Dispose(); var requestMetrics = metricItems .Where(item => item.Name == "http.server.duration") .ToArray(); Assert.True(requestMetrics.Length == 1); var metric = requestMetrics[0]; Assert.NotNull(metric); Assert.True(metric.MetricType == MetricType.Histogram); var metricPoints = new List <MetricPoint>(); foreach (var p in metric.GetMetricPoints()) { metricPoints.Add(p); } Assert.Single(metricPoints); var metricPoint = metricPoints[0]; Assert.Equal(1L, metricPoint.LongValue); Assert.True(metricPoint.DoubleValue > 0); /* * var bucket = metric.Buckets * .Where(b => * metric.PopulationSum > b.LowBoundary && * metric.PopulationSum <= b.HighBoundary) * .FirstOrDefault(); * Assert.NotEqual(default, bucket); * Assert.Equal(1, bucket.Count); */ var attributes = new KeyValuePair <string, object> [metricPoint.Keys.Length]; for (int i = 0; i < attributes.Length; i++) { attributes[i] = new KeyValuePair <string, object>(metricPoint.Keys[i], metricPoint.Values[i]); } var method = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpMethod, "GET"); var scheme = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpScheme, "http"); var statusCode = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpStatusCode, 200); var flavor = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpFlavor, "HTTP/1.1"); Assert.Contains(method, attributes); Assert.Contains(scheme, attributes); Assert.Contains(statusCode, attributes); Assert.Contains(flavor, attributes); Assert.Equal(4, attributes.Length); }
/// <summary> /// Adds OpenTelemetry Prometheus scraping endpoint middleware to an /// <see cref="IApplicationBuilder"/> instance. /// </summary> /// <param name="app">The <see cref="IApplicationBuilder"/> to add /// middleware to.</param> /// <param name="meterProvider">Optional <see cref="MeterProvider"/> /// containing a <see cref="PrometheusExporter"/> otherwise the primary /// SDK provider will be resolved using application services.</param> /// <returns>A reference to the <see cref="IApplicationBuilder"/> instance after the operation has completed.</returns> public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint(this IApplicationBuilder app, MeterProvider meterProvider = null) { var options = app.ApplicationServices.GetOptions <PrometheusExporterOptions>(); string path = options.ScrapeEndpointPath ?? PrometheusExporterOptions.DefaultScrapeEndpointPath; if (!path.StartsWith("/")) { path = $"/{path}"; } return(app.Map( new PathString(path), builder => builder.UseMiddleware <PrometheusExporterMiddleware>(meterProvider ?? app.ApplicationServices.GetRequiredService <MeterProvider>()))); }