static Program() { var process = Process.GetCurrentProcess(); MyMeter.CreateObservableCounter("Thread.CpuTime", () => GetThreadCpuTime(process), "ms"); MyMeter.CreateObservableGauge("Thread.State", () => GetThreadState(process)); }
internal static object Run(int port) { /* prometheus.yml * * global: * scrape_interval: 1s * evaluation_interval: 1s * * scrape_configs: * - job_name: "opentelemetry" * static_configs: * - targets: ["localhost:9184"] */ using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MyMeter.Name) .AddMeter(MyMeter2.Name) .AddPrometheusExporter(options => { options.StartHttpListener = true; options.HttpListenerPrefixes = new string[] { $"http://localhost:{port}/" }; options.ScrapeResponseCacheDurationMilliseconds = 0; }) .Build(); var process = Process.GetCurrentProcess(); MyMeter.CreateObservableCounter("thread.cpu_time", () => GetThreadCpuTime(process), "ms"); // If the same Instrument name+unit combination happened under different Meters, PrometheusExporter // exporter will output duplicated metric names. Related issues and PRs: // * https://github.com/open-telemetry/opentelemetry-specification/pull/2017 // * https://github.com/open-telemetry/opentelemetry-specification/pull/2035 // * https://github.com/open-telemetry/opentelemetry-dotnet/pull/2593 // // MyMeter2.CreateObservableCounter("thread.cpu_time", () => GetThreadCpuTime(process), "ms"); using var token = new CancellationTokenSource(); Task.Run(() => { while (!token.IsCancellationRequested) { Counter.Add(9.9, new("name", "apple"), new("color", "red")); Counter.Add(99.9, new("name", "lemon"), new("color", "yellow")); MyHistogram.Record(ThreadLocalRandom.Value.Next(1, 1500), new("tag1", "value1"), new("tag2", "value2")); Task.Delay(10).Wait(); } }); System.Console.WriteLine($"PrometheusExporter is listening on http://localhost:{port}/metrics/"); System.Console.WriteLine($"Press any key to exit..."); System.Console.ReadKey(); token.Cancel(); return(null); }
public void ObservableCounterAggregationTest(bool exportDelta) { var metricItems = new List <Metric>(); var metricExporter = new InMemoryExporter <Metric>(metricItems); var metricReader = new BaseExportingMetricReader(metricExporter) { PreferredAggregationTemporality = exportDelta ? AggregationTemporality.Delta : AggregationTemporality.Cumulative, }; using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{exportDelta}"); int i = 1; var counterLong = meter.CreateObservableCounter( "observable-counter", () => { return(new List <Measurement <long> >() { new Measurement <long>(i++ *10), }); }); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader(metricReader) .Build(); metricReader.Collect(); long sumReceived = GetLongSum(metricItems); Assert.Equal(10, sumReceived); metricItems.Clear(); metricReader.Collect(); sumReceived = GetLongSum(metricItems); if (exportDelta) { Assert.Equal(10, sumReceived); } else { Assert.Equal(20, sumReceived); } metricItems.Clear(); metricReader.Collect(); sumReceived = GetLongSum(metricItems); if (exportDelta) { Assert.Equal(10, sumReceived); } else { Assert.Equal(30, sumReceived); } }
public void ViewToDropSingleInstrumentObservableCounter() { using var meter = new Meter(Utils.GetCurrentMethodName()); var exportedItems = new List <Metric>(); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddView("observableCounterNotInteresting", MetricStreamConfiguration.Drop) .AddInMemoryExporter(exportedItems) .Build(); // Expecting one metric stream. meter.CreateObservableCounter("observableCounterNotInteresting", () => { return(10); }, "ms"); meter.CreateObservableCounter("observableCounterInteresting", () => { return(10); }, "ms"); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Single(exportedItems); var metric = exportedItems[0]; Assert.Equal("observableCounterInteresting", metric.Name); }
internal static object Run(int port) { /* prometheus.yml * * global: * scrape_interval: 1s * evaluation_interval: 1s * * scrape_configs: * - job_name: "opentelemetry" * static_configs: * - targets: ["localhost:9184"] */ using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MyMeter.Name) .AddPrometheusExporter(opt => { opt.StartHttpListener = true; opt.HttpListenerPrefixes = new string[] { $"http://localhost:{port}/" }; }) .Build(); var process = Process.GetCurrentProcess(); MyMeter.CreateObservableCounter("thread.cpu_time", () => GetThreadCpuTime(process), "ms"); using var token = new CancellationTokenSource(); Task.Run(() => { while (!token.IsCancellationRequested) { Counter.Add(9.9, new("name", "apple"), new("color", "red")); Counter.Add(99.9, new("name", "lemon"), new("color", "yellow")); MyHistogram.Record(ThreadLocalRandom.Value.Next(1, 1500), new("tag1", "value1"), new("tag2", "value2")); Task.Delay(10).Wait(); } }); System.Console.WriteLine($"PrometheusExporter is listening on http://localhost:{port}/metrics/"); System.Console.WriteLine($"Press any key to exit..."); System.Console.ReadKey(); token.Cancel(); return(null); }
public static async Task Main(string[] args) { using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddSource("TestMeter") .AddConsoleExporter() .Build(); int i = 1; var observableCounter = MyMeter.CreateObservableCounter <long>( "observable-counter", () => { var tag1 = new KeyValuePair <string, object>("tag1", "value1"); var tag2 = new KeyValuePair <string, object>("tag2", "value2"); return(new List <Measurement <long> >() { // Report an absolute value (not an increment/delta value). new Measurement <long>(i++ *10, tag1, tag2), }); }); await Task.Delay(10000); }
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); } }
public void ObservableCounterAggregationTest(bool exportDelta) { var meterName = "TestMeter" + exportDelta; 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 = exportDelta ? AggregationTemporality.Delta : AggregationTemporality.Cumulative, }; using var meter = new Meter(meterName); int i = 1; var counterLong = meter.CreateObservableCounter <long>( "observable-counter", () => { return(new List <Measurement <long> >() { new Measurement <long>(i++ *10), }); }); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meterName) .AddReader(metricReader) .Build(); metricReader.Collect(); long sumReceived = GetLongSum(metricItems); Assert.Equal(10, sumReceived); metricItems.Clear(); metricReader.Collect(); sumReceived = GetLongSum(metricItems); if (exportDelta) { Assert.Equal(10, sumReceived); } else { Assert.Equal(20, sumReceived); } metricItems.Clear(); metricReader.Collect(); sumReceived = GetLongSum(metricItems); if (exportDelta) { Assert.Equal(10, sumReceived); } else { Assert.Equal(30, sumReceived); } }
public void ObservableCounterAggregationTest(bool exportDelta) { var meterName = "TestMeter" + exportDelta; 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 pullProcessor = new PullMetricProcessor(metricExporter, exportDelta); var meter = new Meter(meterName); int i = 1; var counterLong = meter.CreateObservableCounter <long>( "observable-counter", () => { return(new List <Measurement <long> >() { new Measurement <long>(i++ *10), }); }); var meterProvider = Sdk.CreateMeterProviderBuilder() .AddSource(meterName) .AddMetricProcessor(pullProcessor) .Build(); pullProcessor.PullRequest(); long sumReceived = GetSum(metricItems); Assert.Equal(10, sumReceived); metricItems.Clear(); pullProcessor.PullRequest(); sumReceived = GetSum(metricItems); if (exportDelta) { Assert.Equal(10, sumReceived); } else { Assert.Equal(20, sumReceived); } metricItems.Clear(); pullProcessor.PullRequest(); sumReceived = GetSum(metricItems); if (exportDelta) { Assert.Equal(10, sumReceived); } else { Assert.Equal(30, sumReceived); } }
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}"); }