public void ObserverCallbackExceptionTest() { using var meter = new Meter(Utils.GetCurrentMethodName()); var exportedItems = new List <Metric>(); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(exportedItems) .Build(); var measurement = new Measurement <int>(100, new("name", "apple"), new("color", "red")); meter.CreateObservableGauge("myGauge", () => measurement); meter.CreateObservableGauge <long>("myBadGauge", observeValues: () => throw new Exception("gauge read error")); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Single(exportedItems); var metric = exportedItems[0]; Assert.Equal("myGauge", metric.Name); List <MetricPoint> metricPoints = new List <MetricPoint>(); foreach (ref readonly var mp in metric.GetMetricPoints()) { metricPoints.Add(mp); } Assert.Single(metricPoints); var metricPoint = metricPoints[0]; Assert.Equal(100, metricPoint.GetGaugeLastValueLong()); Assert.True(metricPoint.Tags.Count > 0); }
static Program() { var process = Process.GetCurrentProcess(); StressMeter.CreateObservableGauge("Process.NonpagedSystemMemorySize64", () => process.NonpagedSystemMemorySize64); StressMeter.CreateObservableGauge("Process.PagedSystemMemorySize64", () => process.PagedSystemMemorySize64); StressMeter.CreateObservableGauge("Process.PagedMemorySize64", () => process.PagedMemorySize64); StressMeter.CreateObservableGauge("Process.WorkingSet64", () => process.WorkingSet64); StressMeter.CreateObservableGauge("Process.VirtualMemorySize64", () => process.VirtualMemorySize64); }
public void ValidateOneDimension(MetricType metricType) { var metrics = new List <Metric>(); using var meter = new Meter(nameof(ValidateOneDimension)); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(metrics) .Build(); var dataPointType = DataPointType.Aggregation; string name = null; if (metricType == MetricType.DoubleSum) { name = "TestDoubleCounter"; var doubleCounter = meter.CreateCounter <double>(name); doubleCounter.Add(123.45, new KeyValuePair <string, object>("tag", "value")); } else if (metricType == MetricType.DoubleGauge) { name = "TestGauge"; meter.CreateObservableGauge( name, () => new List <Measurement <double> >() { new(123.45, new KeyValuePair <string, object>("tag", "value")), });
public void MeterSourcesWildcardSupportMatchTest(bool hasView) { var meterNames = new[] { "AbcCompany.XyzProduct.ComponentA", "abcCompany.xYzProduct.componentC", // Wildcard match is case insensitive. "DefCompany.AbcProduct.ComponentC", "DefCompany.XyzProduct.ComponentC", // Wildcard match supports matching multiple patterns. "GhiCompany.qweProduct.ComponentN", "SomeCompany.SomeProduct.SomeComponent", }; using var meter1 = new Meter(meterNames[0]); using var meter2 = new Meter(meterNames[1]); using var meter3 = new Meter(meterNames[2]); using var meter4 = new Meter(meterNames[3]); using var meter5 = new Meter(meterNames[4]); using var meter6 = new Meter(meterNames[5]); var exportedItems = new List <Metric>(); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddMeter("AbcCompany.XyzProduct.*") .AddMeter("DefCompany.*.ComponentC") .AddMeter("GhiCompany.qweProduct.ComponentN") // Mixing of non-wildcard meter name and wildcard meter name. .AddInMemoryExporter(exportedItems); if (hasView) { meterProviderBuilder.AddView("myGauge1", "newName"); } using var meterProvider = meterProviderBuilder.Build(); var measurement = new Measurement <int>(100, new("name", "apple"), new("color", "red")); meter1.CreateObservableGauge("myGauge1", () => measurement); meter2.CreateObservableGauge("myGauge2", () => measurement); meter3.CreateObservableGauge("myGauge3", () => measurement); meter4.CreateObservableGauge("myGauge4", () => measurement); meter5.CreateObservableGauge("myGauge5", () => measurement); meter6.CreateObservableGauge("myGauge6", () => measurement); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.True(exportedItems.Count == 5); // "SomeCompany.SomeProduct.SomeComponent" will not be subscribed. if (hasView) { Assert.Equal("newName", exportedItems[0].Name); } else { Assert.Equal("myGauge1", exportedItems[0].Name); } Assert.Equal("myGauge2", exportedItems[1].Name); Assert.Equal("myGauge3", exportedItems[2].Name); Assert.Equal("myGauge4", exportedItems[3].Name); Assert.Equal("myGauge5", exportedItems[4].Name); }
public void ObserverCallbackTest() { using var meter = new Meter("ObserverCallbackErrorTest"); var exportedItems = new List <Metric>(); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(exportedItems) .Build(); var measurement = new Measurement <int>(100, new("name", "apple"), new("color", "red")); meter.CreateObservableGauge("myGauge", () => measurement); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Single(exportedItems); var metric = exportedItems[0]; Assert.Equal("myGauge", metric.Name); List <MetricPoint> metricPoints = new List <MetricPoint>(); foreach (ref var mp in metric.GetMetricPoints()) { metricPoints.Add(mp); } Assert.Single(metricPoints); var metricPoint = metricPoints[0]; Assert.Equal(100, metricPoint.LongValue); Assert.NotNull(metricPoint.Keys); Assert.NotNull(metricPoint.Values); }
public void GaugeZeroDimensionWithDescription() { var buffer = new byte[85000]; var metrics = new List <Metric>(); using var meter = new Meter(Utils.GetCurrentMethodName()); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(metrics) .Build(); meter.CreateObservableGauge("test_gauge", () => 123, description: "Hello, world!"); provider.ForceFlush(); var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); Assert.Matches( ("^" + "# HELP test_gauge Hello, world!\n" + "# TYPE test_gauge gauge\n" + "test_gauge 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); }
public void GaugeOneDimension() { var buffer = new byte[85000]; var metrics = new List <Metric>(); using var meter = new Meter(Utils.GetCurrentMethodName()); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(metrics) .Build(); meter.CreateObservableGauge( "test_gauge", () => new Measurement <long>(123, new KeyValuePair <string, object>("tagKey", "tagValue"))); provider.ForceFlush(); var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]); Assert.Matches( ("^" + "# TYPE test_gauge gauge\n" + "test_gauge{tagKey='tagValue'} 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); }
public void MeterSourcesWildcardSupportNegativeTestNoMeterAdded(bool hasView) { var meterNames = new[] { "AbcCompany.XyzProduct.ComponentA", "abcCompany.xYzProduct.componentC", }; using var meter1 = new Meter(meterNames[0]); using var meter2 = new Meter(meterNames[1]); var exportedItems = new List <Metric>(); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddInMemoryExporter(exportedItems); if (hasView) { meterProviderBuilder.AddView("gauge1", "renamed"); } using var meterProvider = meterProviderBuilder.Build(); var measurement = new Measurement <int>(100, new("name", "apple"), new("color", "red")); meter1.CreateObservableGauge("myGauge1", () => measurement); meter2.CreateObservableGauge("myGauge2", () => measurement); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.True(exportedItems.Count == 0); }
static Program() { var process = Process.GetCurrentProcess(); MyMeter.CreateObservableCounter("Thread.CpuTime", () => GetThreadCpuTime(process), "ms"); MyMeter.CreateObservableGauge("Thread.State", () => GetThreadState(process)); }
private static string WriteLongGauge(Meter meter, KeyValuePair <string, object>[] tags, string tagsExpected) { var gauge = meter.CreateObservableGauge( "gauge_long", () => new Measurement <long>[] { new Measurement <long>(18, tags) }); return($"# TYPE gauge_long gauge\ngauge_long{tagsExpected} 18 1633041000000\n"); }
private void InitializeNewMetric(string metricName, string metricDescription) { var gauge = azureMonitorMeter.CreateObservableGauge <double>(metricName, description: metricDescription, observeValues: () => ReportMeasurementsForMetric(metricName)); _gauges.TryAdd(metricName, gauge); _measurements.TryAdd(metricName, new HashSet <Measurement <double> >()); }
static Program() { var process = Process.GetCurrentProcess(); MyMeter.CreateObservableGauge <long>( "MyProcessWorkingSetGauge", () => new List <Measurement <long> >() { new(process.WorkingSet64, new("process.id", process.Id), new("process.bitness", IntPtr.Size << 3)), });
private static string WriteDoubleGauge(Meter meter, KeyValuePair <string, object>[] tags, string tagsExpected) { var value = 0.18F; var gauge = meter.CreateObservableGauge( "gauge_double", () => new Measurement <float>[] { new Measurement <float>(99F, tags), new Measurement <float>(value, tags) }); return($"# TYPE gauge_double gauge\ngauge_double{tagsExpected} {(double)value} 1633041000000\n"); }
public void ViewToDropSingleInstrumentObservableGauge() { using var meter = new Meter(Utils.GetCurrentMethodName()); var exportedItems = new List <Metric>(); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddView("observableGaugeNotInteresting", MetricStreamConfiguration.Drop) .AddInMemoryExporter(exportedItems) .Build(); // Expecting one metric stream. meter.CreateObservableGauge("observableGaugeNotInteresting", () => { return(10); }, "ms"); meter.CreateObservableGauge("observableGaugeInteresting", () => { return(10); }, "ms"); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Single(exportedItems); var metric = exportedItems[0]; Assert.Equal("observableGaugeInteresting", metric.Name); }
public void GaugeDoubleSubnormal() { var buffer = new byte[85000]; var metrics = new List <Metric>(); using var meter = new Meter(Utils.GetCurrentMethodName()); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(metrics) .Build(); meter.CreateObservableGauge("test_gauge", () => new List <Measurement <double> > { new(double.NegativeInfinity, new("x", "1"), new("y", "2")), new(double.PositiveInfinity, new("x", "3"), new("y", "4")), new(double.NaN, new("x", "5"), new("y", "6")), });
public async void When_ObservableGauge_Is_Recorded_Should_Export_To_Prometheus() { // arrange var observeValue = new Mock <Func <Measurement <int> > >(); var stationEui = new StationEui(1); var measurement = new Measurement <int>(1, KeyValuePair.Create(MetricRegistry.ConcentratorIdTagName, (object?)stationEui)); observeValue.Setup(ov => ov.Invoke()).Returns(measurement); using var meter = new Meter("LoRaWan", "1.0"); _ = meter.CreateObservableGauge(ObservableGauge.Name, observeValue.Object); // act this.prometheusMetricExporter.Start(); // assert await observeValue.RetryVerifyAsync(ov => ov.Invoke(), Times.Once); this.recordObservableGaugeMock.Verify(r => r.Invoke(ObservableGauge.Name, new[] { stationEui.ToString() }, measurement.Value), Times.Once); }
public void ObservableInstrumentCallbacksInvokedForEachReaders(bool hasViews) { var exportedItems1 = new List <Metric>(); var exportedItems2 = new List <Metric>(); using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{hasViews}"); int callbackInvocationCount = 0; var gauge = meter.CreateObservableGauge("gauge", () => { callbackInvocationCount++; return(10 * callbackInvocationCount); }); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(exportedItems1) .AddInMemoryExporter(exportedItems2); if (hasViews) { meterProviderBuilder.AddView("gauge", "renamedGauge"); } using var meterProvider = meterProviderBuilder.Build(); meterProvider.ForceFlush(); // VALIDATE Assert.Equal(2, callbackInvocationCount); Assert.Single(exportedItems1); Assert.Single(exportedItems2); if (hasViews) { Assert.Equal("renamedGauge", exportedItems1[0].Name); Assert.Equal("renamedGauge", exportedItems2[0].Name); } else { Assert.Equal("gauge", exportedItems1[0].Name); Assert.Equal("gauge", exportedItems2[0].Name); } }
public void ValidateZeroDimension(MetricType metricType) { var metrics = new List <Metric>(); using var meter = new Meter(nameof(ValidateZeroDimension)); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(metrics) .Build(); var dataPointType = DataPointType.Aggregation; string name = null; if (metricType == MetricType.DoubleSum) { name = "TestDoubleCounter"; var doubleCounter = meter.CreateCounter <double>(name); doubleCounter.Add(123.45); } else if (metricType == MetricType.DoubleGauge) { name = "TestGauge"; meter.CreateObservableGauge(name, () => 123.45); dataPointType = DataPointType.Measurement; } provider.ForceFlush(); var enumerator = metrics[0].GetMetricPoints().GetEnumerator(); enumerator.MoveNext(); var metricPoint = enumerator.Current; var metricData = new MetricsData(Version, metrics[0], ref metricPoint); Assert.Equal(2, metricData.Version); Assert.Equal(name, metricData.Metrics.First().Name); Assert.Equal(123.45, metricData.Metrics.First().Value); Assert.Equal(dataPointType, metricData.Metrics.First().DataPointType); // Properties will contain _MS.AggregationIntervalMs Assert.Equal(1, metricData.Properties.Count); }
public async void When_ObservableGauge_Is_Recorded_Should_Export_To_ApplicationInsights() { // arrange var observeValue = new Mock <Func <Measurement <int> > >(); var stationEui = new StationEui(1); var measurement = new Measurement <int>(1, KeyValuePair.Create(MetricRegistry.ConcentratorIdTagName, (object)stationEui)); _ = observeValue.Setup(ov => ov.Invoke()).Returns(measurement); using var meter = new Meter("LoRaWan", "1.0"); _ = meter.CreateObservableGauge(ObservableGaugeMetric.Name, observeValue.Object); // act this.applicationInsightsMetricExporter.Start(); // assert await this.trackValueMock.RetryVerifyAsync(me => me.Invoke(It.Is <Metric>(m => m.Identifier.MetricNamespace == MetricRegistry.Namespace && m.Identifier.MetricId == ObservableGaugeMetric.Name), measurement.Value, new[] { stationEui.ToString() }), Times.Once); }
public static async Task Main(string[] args) { using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddSource("TestMeter") .AddConsoleExporter() .Build(); ObservableGauge <long> gauge = MyMeter.CreateObservableGauge <long>( "Gauge", () => { var tag1 = new KeyValuePair <string, object>("tag1", "value1"); var tag2 = new KeyValuePair <string, object>("tag2", "value2"); return(new List <Measurement <long> >() { new Measurement <long>(RandomGenerator.Next(1, 1000), tag1, tag2), }); }); await Task.Delay(10000); }
public static void Stress(int concurrency = 0, int prometheusPort = 0) { #if DEBUG Console.WriteLine("***WARNING*** The current build is DEBUG which may affect timing!"); Console.WriteLine(); #endif if (concurrency < 0) { throw new ArgumentOutOfRangeException(nameof(concurrency), "concurrency level should be a non-negative number."); } if (concurrency == 0) { concurrency = Environment.ProcessorCount; } using var meter = new Meter("OpenTelemetry.Tests.Stress." + Guid.NewGuid().ToString("D")); var cntLoopsTotal = 0UL; meter.CreateObservableCounter( "OpenTelemetry.Tests.Stress.Loops", () => unchecked ((long)cntLoopsTotal), description: "The total number of `Run()` invocations that are completed."); var dLoopsPerSecond = 0D; meter.CreateObservableGauge( "OpenTelemetry.Tests.Stress.LoopsPerSecond", () => dLoopsPerSecond, description: "The rate of `Run()` invocations based on a small sliding window of few hundreds of milliseconds."); var dCpuCyclesPerLoop = 0D; #if NET462 if (Environment.OSVersion.Platform == PlatformID.Win32NT) #else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) #endif { meter.CreateObservableGauge( "OpenTelemetry.Tests.Stress.CpuCyclesPerLoop", () => dCpuCyclesPerLoop, description: "The average CPU cycles for each `Run()` invocation, based on a small sliding window of few hundreds of milliseconds."); } using var meterProvider = prometheusPort != 0 ? Sdk.CreateMeterProviderBuilder() .AddMeter(StressMeter.Name) .AddMeter(meter.Name) .AddPrometheusExporter(options => { options.StartHttpListener = true; options.HttpListenerPrefixes = new string[] { $"http://localhost:{prometheusPort}/" }; options.ScrapeResponseCacheDurationMilliseconds = 0; }) .Build() : null; var statistics = new long[concurrency]; var watchForTotal = new Stopwatch(); watchForTotal.Start(); Parallel.Invoke( () => { Console.Write($"Running (concurrency = {concurrency}"); if (prometheusPort != 0) { Console.Write($", prometheusEndpoint = http://localhost:{prometheusPort}/metrics/"); } Console.WriteLine("), press <Esc> to stop..."); var bOutput = false; var watch = new Stopwatch(); while (true) { if (Console.KeyAvailable) { var key = Console.ReadKey(true).Key; switch (key) { case ConsoleKey.Enter: Console.WriteLine(string.Format("{0} {1}", DateTime.UtcNow.ToString("O"), output)); break; case ConsoleKey.Escape: bContinue = false; return; case ConsoleKey.Spacebar: bOutput = !bOutput; break; } continue; } if (bOutput) { Console.WriteLine(string.Format("{0} {1}", DateTime.UtcNow.ToString("O"), output)); } var cntLoopsOld = (ulong)statistics.Sum(); var cntCpuCyclesOld = GetCpuCycles(); watch.Restart(); Thread.Sleep(200); watch.Stop(); cntLoopsTotal = (ulong)statistics.Sum(); var cntCpuCyclesNew = GetCpuCycles(); var nLoops = cntLoopsTotal - cntLoopsOld; var nCpuCycles = cntCpuCyclesNew - cntCpuCyclesOld; dLoopsPerSecond = (double)nLoops / ((double)watch.ElapsedMilliseconds / 1000.0); dCpuCyclesPerLoop = nLoops == 0 ? 0 : nCpuCycles / nLoops; output = $"Loops: {cntLoopsTotal:n0}, Loops/Second: {dLoopsPerSecond:n0}, CPU Cycles/Loop: {dCpuCyclesPerLoop:n0}"; Console.Title = output; } }, () => { Parallel.For(0, concurrency, (i) => { statistics[i] = 0; while (bContinue) { Run(); statistics[i]++; } }); }); watchForTotal.Stop(); cntLoopsTotal = (ulong)statistics.Sum(); var totalLoopsPerSecond = (double)cntLoopsTotal / ((double)watchForTotal.ElapsedMilliseconds / 1000.0); var cntCpuCyclesTotal = GetCpuCycles(); var cpuCyclesPerLoopTotal = cntLoopsTotal == 0 ? 0 : cntCpuCyclesTotal / cntLoopsTotal; Console.WriteLine("Stopping the stress test..."); Console.WriteLine($"* Total Loops: {cntLoopsTotal:n0}"); Console.WriteLine($"* Average Loops/Second: {totalLoopsPerSecond:n0}"); Console.WriteLine($"* Average CPU Cycles/Loop: {cpuCyclesPerLoopTotal:n0}"); }
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 }
private static void Main() { // Uncomment this to suppress the default sample metrics. //Metrics.SuppressDefaultMetrics(); // Replace the first line with an appropriate type of tester to run different manual tests. //var tester = new MetricPusherTester(); //var tester = new KestrelMetricServerTester(); var tester = new AspNetCoreMiddlewareTester(); //var tester = new GrpcMiddlewareTester(); //var tester = new MetricServerTester(); // For testing Kestrel metric server with HTTPS, you need at least a self-signed certificate (one included here) // and the matching domain pointed to 127.0.0.1 (e.g. hardcoded in the PCs hosts file) and you also need to // import this certificate into your Trusted Root Certification Authorities certificate store to trust it. //var certificate = new X509Certificate2("prometheus-net.test.pfx", "prometheus-net.test"); //var tester = new KestrelMetricServerTester("prometheus-net.test", certificate); tester.OnStart(); var metricServer = tester.InitializeMetricServer(); metricServer?.Start(); var counter = Metrics.CreateCounter("myCounter", "help text", new CounterConfiguration { LabelNames = new[] { "method", "endpoint" } }); counter.WithLabels("GET", "/").Inc(); counter.WithLabels("POST", "/cancel").Inc(); Metrics.CreateCounter("always_zero", "This counter is always zero but still needs to be present in the output!"); var gauge = Metrics.CreateGauge("gauge", "help text"); gauge.Inc(3.4); gauge.Dec(2.1); gauge.Set(5.3); // As the initial value is suppressed and a new one never assigned, this one never shows up in the export. Metrics.CreateGauge("should_not_show_up", "", new GaugeConfiguration { SuppressInitialValue = true }); var hist = Metrics.CreateHistogram("myHistogram", "help text", new HistogramConfiguration { Buckets = new[] { 0, 0.2, 0.4, 0.6, 0.8, 0.9 } }); hist.Observe(0.4); var timedHistogram = Metrics.CreateHistogram("myTimedHistogram", "help text", new HistogramConfiguration { Buckets = new[] { 0, 0.2, 0.4, 0.6, 0.8, 0.9 } }); var latestGauge = Metrics.CreateGauge("latestGauge", "Reports the latest cycle time"); var summary = Metrics.CreateSummary("mySummary", "help text"); summary.Observe(5.3); // Example implementation of updating values before every collection. var collectionCount = Metrics.CreateCounter("beforecollect_example", "This counter is incremented before every data collection."); // Synchronous callbacks should be instantaneous, to avoid causing delays in the pipeline. Metrics.DefaultRegistry.AddBeforeCollectCallback(() => collectionCount.Inc()); var googlePageBytes = Metrics.CreateCounter("beforecollect_async_example", "This counter is incremented before every data collection, but asynchronously."); // Callbacks can also be asynchronous. It is fine for these to take a bit more time. // For example, you can make an asynchronous HTTP request to a remote system in such a callback. var httpClient = new HttpClient(); Metrics.DefaultRegistry.AddBeforeCollectCallback(async(cancel) => { // Probe a remote system. var response = await httpClient.GetAsync("https://google.com", cancel); // Increase a counter by however many bytes we loaded. googlePageBytes.Inc(response.Content.Headers.ContentLength ?? 0); }); // Uncomment this to test deliberately causing collections to fail. This should result in 503 responses. // With MetricPusherTester you might get a 1st push already before it fails but after that it should stop pushing. //Metrics.DefaultRegistry.AddBeforeCollectCallback(() => throw new ScrapeFailedException()); #if NETCOREAPP var diagnosticSourceRegistration = DiagnosticSourceAdapter.StartListening(); var eventCounterRegistration = EventCounterAdapter.StartListening(); #endif #if NET6_0_OR_GREATER var meter = new Meter("sample.dotnet.meter", "1.2.3"); var meterCounter = meter.CreateCounter <double>("sample_counter"); var meterGauge = meter.CreateObservableGauge <byte>("sample_gauge", () => 92, "Buckets", "How much cheese is loaded"); var meterRegistration = MeterAdapter.StartListening(); #endif var cts = new CancellationTokenSource(); var random = new Random(); // Update metrics on a regular interval until told to stop. var updateInterval = TimeSpan.FromSeconds(0.5); var updateTask = Task.Factory.StartNew(async delegate { while (!cts.IsCancellationRequested) { using (latestGauge.NewTimer()) using (timedHistogram.NewTimer()) { var duration = Stopwatch.StartNew(); counter.Inc(); counter.Labels("GET", "/").Inc(2); gauge.Set(random.NextDouble() + 2); hist.Observe(random.NextDouble()); summary.Observe(random.NextDouble()); #if NET6_0_OR_GREATER meterCounter.Add(1); #endif try { tester.OnTimeToObserveMetrics(); } catch (Exception ex) { Console.Error.WriteLine(ex); } var sleepTime = updateInterval - duration.Elapsed; if (sleepTime > TimeSpan.Zero) { await Task.Delay(sleepTime, cts.Token); } } } }).Result; Console.WriteLine("Press enter to stop metricServer"); Console.ReadLine(); cts.Cancel(); try { updateTask.GetAwaiter().GetResult(); } catch (OperationCanceledException) { } metricServer?.StopAsync().GetAwaiter().GetResult(); tester.OnEnd(); Console.WriteLine("Press enter to stop tester"); Console.ReadLine(); }
public void SdkSupportsMultipleReaders(AggregationTemporality aggregationTemporality, bool hasViews) { var exportedItems1 = new List <Metric>(); using var deltaExporter1 = new InMemoryExporter <Metric>(exportedItems1); using var deltaReader1 = new BaseExportingMetricReader(deltaExporter1) { Temporality = AggregationTemporality.Delta, }; var exportedItems2 = new List <Metric>(); using var deltaExporter2 = new InMemoryExporter <Metric>(exportedItems2); using var deltaReader2 = new BaseExportingMetricReader(deltaExporter2) { Temporality = aggregationTemporality, }; using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{aggregationTemporality}.{hasViews}"); var counter = meter.CreateCounter <long>("counter"); int index = 0; var values = new long[] { 100, 200, 300, 400 }; long GetValue() => values[index++]; var gauge = meter.CreateObservableGauge("gauge", () => GetValue()); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader(deltaReader1) .AddReader(deltaReader2); if (hasViews) { meterProviderBuilder.AddView("counter", "renamedCounter"); } using var meterProvider = meterProviderBuilder.Build(); counter.Add(10, new KeyValuePair <string, object>("key", "value")); meterProvider.ForceFlush(); Assert.Equal(2, exportedItems1.Count); Assert.Equal(2, exportedItems2.Count); // Check value exported for Counter this.AssertLongSumValueForMetric(exportedItems1[0], 10); this.AssertLongSumValueForMetric(exportedItems2[0], 10); // Check value exported for Gauge this.AssertLongSumValueForMetric(exportedItems1[1], 100); this.AssertLongSumValueForMetric(exportedItems2[1], 200); exportedItems1.Clear(); exportedItems2.Clear(); counter.Add(15, new KeyValuePair <string, object>("key", "value")); meterProvider.ForceFlush(); Assert.Equal(2, exportedItems1.Count); Assert.Equal(2, exportedItems2.Count); // Check value exported for Counter this.AssertLongSumValueForMetric(exportedItems1[0], 15); if (aggregationTemporality == AggregationTemporality.Delta) { this.AssertLongSumValueForMetric(exportedItems2[0], 15); } else { this.AssertLongSumValueForMetric(exportedItems2[0], 25); } // Check value exported for Gauge this.AssertLongSumValueForMetric(exportedItems1[1], 300); this.AssertLongSumValueForMetric(exportedItems2[1], 400); }
internal static object Run(MetricsOptions options) { using var meter = new Meter("TestMeter"); var providerBuilder = Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("myservice")) .AddMeter(meter.Name); // All instruments from this meter are enabled. if (options.UseExporter.Equals("otlp", StringComparison.OrdinalIgnoreCase)) { /* * Prerequisite to run this example: * Set up an OpenTelemetry Collector to run on local docker. * * Open a terminal window at the examples/Console/ directory and * launch the OpenTelemetry Collector with an OTLP receiver, by running: * * - On Unix based systems use: * docker run --rm -it -p 4317:4317 -v $(pwd):/cfg otel/opentelemetry-collector:0.33.0 --config=/cfg/otlp-collector-example/config.yaml * * - On Windows use: * docker run --rm -it -p 4317:4317 -v "%cd%":/cfg otel/opentelemetry-collector:0.33.0 --config=/cfg/otlp-collector-example/config.yaml * * Open another terminal window at the examples/Console/ directory and * launch the OTLP example by running: * * dotnet run metrics --useExporter otlp * * The OpenTelemetry Collector will output all received metrics to the stdout of its terminal. * */ // Adding the OtlpExporter creates a GrpcChannel. // This switch must be set before creating a GrpcChannel 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); providerBuilder .AddOtlpExporter((exporterOptions, metricReaderOptions) => { exporterOptions.Protocol = options.UseGrpc ? OtlpExportProtocol.Grpc : OtlpExportProtocol.HttpProtobuf; metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = options.DefaultCollectionPeriodMilliseconds; metricReaderOptions.TemporalityPreference = options.IsDelta ? MetricReaderTemporalityPreference.Delta : MetricReaderTemporalityPreference.Cumulative; }); } else { providerBuilder .AddConsoleExporter((exporterOptions, metricReaderOptions) => { exporterOptions.Targets = ConsoleExporterOutputTargets.Console; metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = options.DefaultCollectionPeriodMilliseconds; metricReaderOptions.TemporalityPreference = options.IsDelta ? MetricReaderTemporalityPreference.Delta : MetricReaderTemporalityPreference.Cumulative; }); } using var provider = providerBuilder.Build(); Counter <int> counter = null; if (options.FlagCounter ?? true) { counter = meter.CreateCounter <int>("counter", "things", "A count of things"); } Histogram <int> histogram = null; if (options.FlagHistogram ?? false) { histogram = meter.CreateHistogram <int>("histogram"); } if (options.FlagGauge ?? false) { var observableCounter = meter.CreateObservableGauge("gauge", () => { return(new List <Measurement <int> >() { new Measurement <int>( (int)Process.GetCurrentProcess().PrivateMemorySize64, new KeyValuePair <string, object>("tag1", "value1")), }); }); } System.Console.WriteLine("Press any key to exit."); while (!System.Console.KeyAvailable) { histogram?.Record(10); histogram?.Record( 100, new KeyValuePair <string, object>("tag1", "value1")); histogram?.Record( 200, new KeyValuePair <string, object>("tag1", "value2"), new KeyValuePair <string, object>("tag2", "value2")); histogram?.Record( 100, new KeyValuePair <string, object>("tag1", "value1")); histogram?.Record( 200, new KeyValuePair <string, object>("tag2", "value2"), new KeyValuePair <string, object>("tag1", "value2")); counter?.Add(10); counter?.Add( 100, new KeyValuePair <string, object>("tag1", "value1")); counter?.Add( 200, new KeyValuePair <string, object>("tag1", "value2"), new KeyValuePair <string, object>("tag2", "value2")); counter?.Add( 100, new KeyValuePair <string, object>("tag1", "value1")); counter?.Add( 200, new KeyValuePair <string, object>("tag2", "value2"), new KeyValuePair <string, object>("tag1", "value2")); Task.Delay(500).Wait(); } return(null); }
internal static object Run(int port, int totalDurationInMins) { /* * 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'] */ using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddSource("TestMeter") .AddPrometheusExporter(opt => opt.Url = $"http://localhost:{port}/metrics/") .Build(); ObservableGauge <long> gauge = MyMeter.CreateObservableGauge <long>( "Gauge", () => { var tag1 = new KeyValuePair <string, object>("tag1", "value1"); var tag2 = new KeyValuePair <string, object>("tag2", "value2"); return(new List <Measurement <long> >() { new Measurement <long>(RandomGenerator.Next(1, 1000), tag1, tag2), }); }); using var token = new CancellationTokenSource(); Task writeMetricTask = new Task(() => { while (!token.IsCancellationRequested) { Counter.Add( 10, new KeyValuePair <string, object>("tag1", "value1"), new KeyValuePair <string, object>("tag2", "value2")); Counter.Add( 100, new KeyValuePair <string, object>("tag1", "anothervalue"), new KeyValuePair <string, object>("tag2", "somethingelse")); MyHistogram.Record( RandomGenerator.Next(1, 1500), new KeyValuePair <string, object>("tag1", "value1"), new KeyValuePair <string, object>("tag2", "value2")); Task.Delay(10).Wait(); } }); writeMetricTask.Start(); token.CancelAfter(totalDurationInMins * 60 * 1000); System.Console.WriteLine($"OpenTelemetry Prometheus Exporter is making metrics available at http://localhost:{port}/metrics/"); System.Console.WriteLine($"Press Enter key to exit now or will exit automatically after {totalDurationInMins} minutes."); System.Console.ReadLine(); token.Cancel(); System.Console.WriteLine("Exiting..."); return(null); }
internal static object Run(MetricsOptions options) { using var meter = new Meter("TestMeter"); var providerBuilder = Sdk.CreateMeterProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("myservice")) .AddMeter(meter.Name); // All instruments from this meter are enabled. if (options.UseExporter.ToLower() == "otlp") { /* * Prerequisite to run this example: * Set up an OpenTelemetry Collector to run on local docker. * * Open a terminal window at the examples/Console/ directory and * launch the OpenTelemetry Collector with an OTLP receiver, by running: * * - On Unix based systems use: * docker run --rm -it -p 4317:4317 -v $(pwd):/cfg otel/opentelemetry-collector:0.33.0 --config=/cfg/otlp-collector-example/config.yaml * * - On Windows use: * docker run --rm -it -p 4317:4317 -v "%cd%":/cfg otel/opentelemetry-collector:0.33.0 --config=/cfg/otlp-collector-example/config.yaml * * Open another terminal window at the examples/Console/ directory and * launch the OTLP example by running: * * dotnet run metrics --useExporter otlp * * The OpenTelemetry Collector will output all received metrics to the stdout of its terminal. * */ // 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); providerBuilder .AddOtlpExporter(o => { o.MetricExportIntervalMilliseconds = options.DefaultCollectionPeriodMilliseconds; o.AggregationTemporality = options.IsDelta ? AggregationTemporality.Delta : AggregationTemporality.Cumulative; }); } else { providerBuilder .AddConsoleExporter(o => { o.MetricExportIntervalMilliseconds = options.DefaultCollectionPeriodMilliseconds; o.AggregationTemporality = options.IsDelta ? AggregationTemporality.Delta : AggregationTemporality.Cumulative; }); } using var provider = providerBuilder.Build(); Counter <int> counter = null; if (options.FlagCounter ?? true) { counter = meter.CreateCounter <int>("counter", "things", "A count of things"); } Histogram <int> histogram = null; if (options.FlagHistogram ?? false) { histogram = meter.CreateHistogram <int>("histogram"); } if (options.FlagGauge ?? false) { var observableCounter = meter.CreateObservableGauge("gauge", () => { return(new List <Measurement <int> >() { new Measurement <int>( (int)Process.GetCurrentProcess().PrivateMemorySize64, new KeyValuePair <string, object>("tag1", "value1")), }); }); } var cts = new CancellationTokenSource(); var tasks = new List <Task>(); for (int i = 0; i < options.NumTasks; i++) { var taskno = i; tasks.Add(Task.Run(() => { System.Console.WriteLine($"Task started {taskno + 1}/{options.NumTasks}."); var loops = 0; while (!cts.IsCancellationRequested) { if (options.MaxLoops > 0 && loops >= options.MaxLoops) { break; } histogram?.Record(10); histogram?.Record( 100, new KeyValuePair <string, object>("tag1", "value1")); histogram?.Record( 200, new KeyValuePair <string, object>("tag1", "value2"), new KeyValuePair <string, object>("tag2", "value2")); histogram?.Record( 100, new KeyValuePair <string, object>("tag1", "value1")); histogram?.Record( 200, new KeyValuePair <string, object>("tag2", "value2"), new KeyValuePair <string, object>("tag1", "value2")); counter?.Add(10); counter?.Add( 100, new KeyValuePair <string, object>("tag1", "value1")); counter?.Add( 200, new KeyValuePair <string, object>("tag1", "value2"), new KeyValuePair <string, object>("tag2", "value2")); counter?.Add( 100, new KeyValuePair <string, object>("tag1", "value1")); counter?.Add( 200, new KeyValuePair <string, object>("tag2", "value2"), new KeyValuePair <string, object>("tag1", "value2")); loops++; } })); } cts.CancelAfter(options.RunTime); System.Console.WriteLine($"Wait for {options.RunTime} milliseconds."); while (!cts.IsCancellationRequested) { Task.Delay(1000).Wait(); } Task.WaitAll(tasks.ToArray()); return(null); }
public void SdkSupportsMultipleReaders(MetricReaderTemporalityPreference aggregationTemporality, bool hasViews) { var exportedItems1 = new List <Metric>(); var exportedItems2 = new List <Metric>(); using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{aggregationTemporality}.{hasViews}"); var counter = meter.CreateCounter <long>("counter"); int index = 0; var values = new long[] { 100, 200, 300, 400 }; long GetValue() => values[index++]; var gauge = meter.CreateObservableGauge("gauge", () => GetValue()); int indexSum = 0; var valuesSum = new long[] { 1000, 1200, 1300, 1400 }; long GetSum() => valuesSum[indexSum++]; var observableCounter = meter.CreateObservableCounter("obs-counter", () => GetSum()); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddInMemoryExporter(exportedItems1, metricReaderOptions => { metricReaderOptions.TemporalityPreference = MetricReaderTemporalityPreference.Delta; }) .AddInMemoryExporter(exportedItems2, metricReaderOptions => { metricReaderOptions.TemporalityPreference = aggregationTemporality; }); if (hasViews) { meterProviderBuilder.AddView("counter", "renamedCounter"); meterProviderBuilder.AddView("gauge", "renamedGauge"); meterProviderBuilder.AddView("obs-counter", "renamedObservableCounter"); } using var meterProvider = meterProviderBuilder.Build(); counter.Add(10, new KeyValuePair <string, object>("key", "value")); meterProvider.ForceFlush(); Assert.Equal(3, exportedItems1.Count); Assert.Equal(3, exportedItems2.Count); if (hasViews) { Assert.Equal("renamedCounter", exportedItems1[0].Name); Assert.Equal("renamedCounter", exportedItems2[0].Name); Assert.Equal("renamedGauge", exportedItems1[1].Name); Assert.Equal("renamedGauge", exportedItems2[1].Name); Assert.Equal("renamedObservableCounter", exportedItems1[2].Name); Assert.Equal("renamedObservableCounter", exportedItems2[2].Name); } else { Assert.Equal("counter", exportedItems1[0].Name); Assert.Equal("counter", exportedItems2[0].Name); Assert.Equal("gauge", exportedItems1[1].Name); Assert.Equal("gauge", exportedItems2[1].Name); Assert.Equal("obs-counter", exportedItems1[2].Name); Assert.Equal("obs-counter", exportedItems2[2].Name); } // Check value exported for Counter AssertLongSumValueForMetric(exportedItems1[0], 10); AssertLongSumValueForMetric(exportedItems2[0], 10); // Check value exported for Gauge AssertLongSumValueForMetric(exportedItems1[1], 100); AssertLongSumValueForMetric(exportedItems2[1], 200); // Check value exported for ObservableCounter AssertLongSumValueForMetric(exportedItems1[2], 1000); if (aggregationTemporality == MetricReaderTemporalityPreference.Delta) { AssertLongSumValueForMetric(exportedItems2[2], 1200); } else { AssertLongSumValueForMetric(exportedItems2[2], 1200); } exportedItems1.Clear(); exportedItems2.Clear(); counter.Add(15, new KeyValuePair <string, object>("key", "value")); meterProvider.ForceFlush(); Assert.Equal(3, exportedItems1.Count); Assert.Equal(3, exportedItems2.Count); // Check value exported for Counter AssertLongSumValueForMetric(exportedItems1[0], 15); if (aggregationTemporality == MetricReaderTemporalityPreference.Delta) { AssertLongSumValueForMetric(exportedItems2[0], 15); } else { AssertLongSumValueForMetric(exportedItems2[0], 25); } // Check value exported for Gauge AssertLongSumValueForMetric(exportedItems1[1], 300); AssertLongSumValueForMetric(exportedItems2[1], 400); // Check value exported for ObservableCounter AssertLongSumValueForMetric(exportedItems1[2], 300); if (aggregationTemporality == MetricReaderTemporalityPreference.Delta) { AssertLongSumValueForMetric(exportedItems2[2], 200); } else { AssertLongSumValueForMetric(exportedItems2[2], 1400); } }