From 4712c3a9b0f9cedcb27297d46b08001fc13282cf Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 20 Jun 2023 15:08:58 -0700 Subject: [PATCH 1/7] [perf] Minor Httpclient improvement (#4572) --- .../HttpClientInstrumentation.cs | 24 +++++++++++++++---- .../HttpClientMetrics.cs | 11 ++++++++- .../HttpClientInstrumentationBenchmarks.cs | 14 +++++------ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentation.cs index c7ffe545f21..842cd0b9af3 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentation.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentation.cs @@ -22,10 +22,26 @@ namespace OpenTelemetry.Instrumentation.Http /// internal sealed class HttpClientInstrumentation : IDisposable { + private static readonly HashSet ExcludedDiagnosticSourceEventsNet7OrGreater = new() + { + "System.Net.Http.Request", + "System.Net.Http.Response", + "System.Net.Http.HttpRequestOut", + }; + + private static readonly HashSet ExcludedDiagnosticSourceEvents = new() + { + "System.Net.Http.Request", + "System.Net.Http.Response", + }; + private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber; - private readonly Func isEnabled = (activityName, obj1, obj2) - => !activityName.Equals("System.Net.Http.HttpRequestOut"); + private readonly Func isEnabled = (eventName, _, _) + => !ExcludedDiagnosticSourceEvents.Contains(eventName); + + private readonly Func isEnabledNet7OrGreater = (eventName, _, _) + => !ExcludedDiagnosticSourceEventsNet7OrGreater.Contains(eventName); /// /// Initializes a new instance of the class. @@ -41,11 +57,11 @@ public HttpClientInstrumentation(HttpClientInstrumentationOptions options) // so that the sampler's decision is respected. if (HttpHandlerDiagnosticListener.IsNet7OrGreater) { - this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), this.isEnabled); + this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), this.isEnabledNet7OrGreater); } else { - this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), null); + this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), this.isEnabled); } this.diagnosticSourceSubscriber.Subscribe(); diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpClientMetrics.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientMetrics.cs index e48af04a625..3bf57029d8a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpClientMetrics.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientMetrics.cs @@ -29,16 +29,25 @@ internal sealed class HttpClientMetrics : IDisposable internal static readonly string InstrumentationName = AssemblyName.Name; internal static readonly string InstrumentationVersion = AssemblyName.Version.ToString(); + private static readonly HashSet ExcludedDiagnosticSourceEvents = new() + { + "System.Net.Http.Request", + "System.Net.Http.Response", + }; + private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber; private readonly Meter meter; + private readonly Func isEnabled = (activityName, obj1, obj2) + => !ExcludedDiagnosticSourceEvents.Contains(activityName); + /// /// Initializes a new instance of the class. /// public HttpClientMetrics() { this.meter = new Meter(InstrumentationName, InstrumentationVersion); - this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerMetricsDiagnosticListener("HttpHandlerDiagnosticListener", this.meter), null); + this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerMetricsDiagnosticListener("HttpHandlerDiagnosticListener", this.meter), this.isEnabled); this.diagnosticSourceSubscriber.Subscribe(); } diff --git a/test/Benchmarks/Instrumentation/HttpClientInstrumentationBenchmarks.cs b/test/Benchmarks/Instrumentation/HttpClientInstrumentationBenchmarks.cs index a25570b9064..2583b32cf0d 100644 --- a/test/Benchmarks/Instrumentation/HttpClientInstrumentationBenchmarks.cs +++ b/test/Benchmarks/Instrumentation/HttpClientInstrumentationBenchmarks.cs @@ -24,19 +24,19 @@ using OpenTelemetry.Trace; /* -BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.23424.1000) -Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores -.NET SDK=7.0.203 +BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.22621.1702/22H2/2022Update/SunValley2) +Intel Core i7-8850H CPU 2.60GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores +.NET SDK=7.0.302 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 DefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 | Method | EnableInstrumentation | Mean | Error | StdDev | Gen0 | Allocated | |------------------ |---------------------- |---------:|--------:|--------:|-------:|----------:| -| HttpClientRequest | None | 222.7 us | 4.28 us | 4.75 us | - | 2.45 KB | -| HttpClientRequest | Traces | 233.0 us | 4.08 us | 3.81 us | 0.4883 | 4.36 KB | -| HttpClientRequest | Metrics | 208.9 us | 4.17 us | 7.53 us | 0.4883 | 3.76 KB | -| HttpClientRequest | Traces, Metrics | 230.9 us | 3.29 us | 2.92 us | 0.4883 | 4.38 KB | +| HttpClientRequest | None | 161.0 us | 1.27 us | 0.99 us | 0.4883 | 2.45 KB | +| HttpClientRequest | Traces | 173.1 us | 1.65 us | 1.54 us | 0.4883 | 4.26 KB | +| HttpClientRequest | Metrics | 165.8 us | 1.33 us | 1.18 us | 0.7324 | 3.66 KB | +| HttpClientRequest | Traces, Metrics | 175.6 us | 1.96 us | 1.83 us | 0.4883 | 4.28 KB | */ namespace Benchmarks.Instrumentation From b7821f7ee0c1023e15f6cba9d2b76e184e6e6ba0 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 20 Jun 2023 18:18:12 -0700 Subject: [PATCH 2/7] Remove zpages (#4604) --- .vscode/settings.json | 3 +- OpenTelemetry.sln | 4 - examples/Console/Examples.Console.csproj | 3 +- examples/Console/Program.cs | 9 +- examples/Console/TestZPagesExporter.cs | 66 ----- .../.publicApi/net462/PublicAPI.Shipped.txt | 0 .../.publicApi/net462/PublicAPI.Unshipped.txt | 21 -- .../netstandard2.0/PublicAPI.Shipped.txt | 0 .../netstandard2.0/PublicAPI.Unshipped.txt | 21 -- .../AssemblyInfo.cs | 22 -- .../CHANGELOG.md | 148 ---------- .../Implementation/ZPagesActivityAggregate.cs | 94 ------- .../Implementation/ZPagesActivityTracker.cs | 133 --------- .../ZPagesExporterEventSource.cs | 75 ----- .../Implementation/ZPagesStatsBuilder.cs | 23 -- .../OpenTelemetry.Exporter.ZPages.csproj | 20 -- src/OpenTelemetry.Exporter.ZPages/README.md | 26 -- .../ZPagesExporter.cs | 64 ----- .../ZPagesExporterHelperExtensions.cs | 47 ---- .../ZPagesExporterOptions.cs | 34 --- .../ZPagesExporterStatsHttpServer.cs | 194 ------------- .../ZPagesProcessor.cs | 117 -------- ...nTelemetry.AotCompatibility.TestApp.csproj | 1 - .../EventSourceTest.cs | 31 -- ...OpenTelemetry.Exporter.ZPages.Tests.csproj | 32 --- .../ZPagesActivityTrackerTests.cs | 43 --- .../ZPagesExporterTests.cs | 265 ------------------ 27 files changed, 3 insertions(+), 1493 deletions(-) delete mode 100644 examples/Console/TestZPagesExporter.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/.publicApi/net462/PublicAPI.Shipped.txt delete mode 100644 src/OpenTelemetry.Exporter.ZPages/.publicApi/net462/PublicAPI.Unshipped.txt delete mode 100644 src/OpenTelemetry.Exporter.ZPages/.publicApi/netstandard2.0/PublicAPI.Shipped.txt delete mode 100644 src/OpenTelemetry.Exporter.ZPages/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt delete mode 100644 src/OpenTelemetry.Exporter.ZPages/AssemblyInfo.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md delete mode 100644 src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityAggregate.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityTracker.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesExporterEventSource.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesStatsBuilder.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/OpenTelemetry.Exporter.ZPages.csproj delete mode 100644 src/OpenTelemetry.Exporter.ZPages/README.md delete mode 100644 src/OpenTelemetry.Exporter.ZPages/ZPagesExporter.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/ZPagesExporterHelperExtensions.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/ZPagesExporterOptions.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/ZPagesExporterStatsHttpServer.cs delete mode 100644 src/OpenTelemetry.Exporter.ZPages/ZPagesProcessor.cs delete mode 100644 test/OpenTelemetry.Exporter.ZPages.Tests/EventSourceTest.cs delete mode 100644 test/OpenTelemetry.Exporter.ZPages.Tests/OpenTelemetry.Exporter.ZPages.Tests.csproj delete mode 100644 test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesActivityTrackerTests.cs delete mode 100644 test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesExporterTests.cs diff --git a/.vscode/settings.json b/.vscode/settings.json index f82a1be7f59..09d712afee5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -54,7 +54,6 @@ "utkarsh", "vishwesh", "xunit", - "zipkin", - "zpages" + "zipkin" ] } diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 6685c16e446..7c75ed0c9b2 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -72,8 +72,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Ho EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.OpenTelemetryProtocol", "src\OpenTelemetry.Exporter.OpenTelemetryProtocol\OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj", "{A38AC295-2745-4B85-8B6B-DCA864CEDD5B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.ZPages", "src\OpenTelemetry.Exporter.ZPages\OpenTelemetry.Exporter.ZPages.csproj", "{56A34828-621A-478B-A0B8-C065FE938383}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Console", "src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj", "{1AFFF251-3B0C-47CA-BE94-937083732C0A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Zipkin.Tests", "test\OpenTelemetry.Exporter.Zipkin.Tests\OpenTelemetry.Exporter.Zipkin.Tests.csproj", "{1D778D2E-9523-450E-A6E0-A36897C7E78E}" @@ -125,8 +123,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D2E73927-5 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.Grpc.Tests", "test\OpenTelemetry.Instrumentation.Grpc.Tests\OpenTelemetry.Instrumentation.Grpc.Tests.csproj", "{305E9DFD-E73B-4A28-8769-795C25551020}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.ZPages.Tests", "test\OpenTelemetry.Exporter.ZPages.Tests\OpenTelemetry.Exporter.ZPages.Tests.csproj", "{98F9556B-116F-49B5-9211-BB1D418446FF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Console", "examples\Console\Examples.Console.csproj", "{FF3E6E08-E8E4-4523-B526-847CD989279F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.AspNetCore", "examples\AspNetCore\Examples.AspNetCore.csproj", "{0935622B-9377-4056-8343-AE6ECDC274CF}" diff --git a/examples/Console/Examples.Console.csproj b/examples/Console/Examples.Console.csproj index 02438fb885e..ee9d4d0c6ce 100644 --- a/examples/Console/Examples.Console.csproj +++ b/examples/Console/Examples.Console.csproj @@ -30,12 +30,11 @@ - - + diff --git a/examples/Console/Program.cs b/examples/Console/Program.cs index 8dffaef6a03..db0fb90389f 100644 --- a/examples/Console/Program.cs +++ b/examples/Console/Program.cs @@ -33,7 +33,6 @@ public class Program /// dotnet run --project Examples.Console.csproj -- jaeger -h localhost -p 6831 /// dotnet run --project Examples.Console.csproj -- prometheus -p 9464 /// dotnet run --project Examples.Console.csproj -- otlp -e "http://localhost:4317" -p "grpc" - /// dotnet run --project Examples.Console.csproj -- zpages /// dotnet run --project Examples.Console.csproj -- metrics --help /// /// To see all available examples in the project run: @@ -46,7 +45,7 @@ public class Program /// Arguments from command line. public static void Main(string[] args) { - Parser.Default.ParseArguments(args) + Parser.Default.ParseArguments(args) .MapResult( (JaegerOptions options) => TestJaegerExporter.Run(options.Host, options.Port), (ZipkinOptions options) => TestZipkinExporter.Run(options.Uri), @@ -55,7 +54,6 @@ public static void Main(string[] args) (LogsOptions options) => TestLogs.Run(options), (GrpcNetClientOptions options) => TestGrpcNetClient.Run(), (HttpClientOptions options) => TestHttpClient.Run(), - (ZPagesOptions options) => TestZPagesExporter.Run(), (ConsoleOptions options) => TestConsoleExporter.Run(options), (OpenTelemetryShimOptions options) => TestOTelShimWithConsoleExporter.Run(options), (OpenTracingShimOptions options) => TestOpenTracingShim.Run(options), @@ -129,11 +127,6 @@ internal class HttpClientOptions { } - [Verb("zpages", HelpText = "Specify the options required to test ZPages")] - internal class ZPagesOptions - { - } - [Verb("console", HelpText = "Specify the options required to test console exporter")] internal class ConsoleOptions { diff --git a/examples/Console/TestZPagesExporter.cs b/examples/Console/TestZPagesExporter.cs deleted file mode 100644 index 2162725520d..00000000000 --- a/examples/Console/TestZPagesExporter.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Diagnostics; -using OpenTelemetry; -using OpenTelemetry.Exporter.ZPages; -using OpenTelemetry.Trace; - -namespace Examples.Console -{ - internal class TestZPagesExporter - { - internal static object Run() - { - var zpagesOptions = new ZPagesExporterOptions() { Url = "http://localhost:7284/rpcz/", RetentionTime = 3600000 }; - using var zpagesExporter = new ZPagesExporter(zpagesOptions); - using var httpServer = new ZPagesExporterStatsHttpServer(zpagesExporter); - - // Start the server - httpServer.Start(); - - using var tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddSource("zpages-test") - .AddZPagesExporter(o => - { - o.Url = zpagesOptions.Url; - o.RetentionTime = zpagesOptions.RetentionTime; - }) - .Build(); - - using var activitySource = new ActivitySource("zpages-test"); - - while (true) - { - // Create a scoped activity. It will end automatically when using statement ends - using (activitySource.StartActivity("Main")) - { - System.Console.WriteLine("About to do a busy work in Main"); - } - - Thread.Sleep(3000); - - // Create a scoped activity. It will end automatically when using statement ends - using (activitySource.StartActivity("Test")) - { - System.Console.WriteLine("About to do a busy work in Test"); - } - - Thread.Sleep(5000); - } - } - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/.publicApi/net462/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.ZPages/.publicApi/net462/PublicAPI.Shipped.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/OpenTelemetry.Exporter.ZPages/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.ZPages/.publicApi/net462/PublicAPI.Unshipped.txt deleted file mode 100644 index faf071f21a0..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/.publicApi/net462/PublicAPI.Unshipped.txt +++ /dev/null @@ -1,21 +0,0 @@ -OpenTelemetry.Exporter.ZPages.ZPagesExporter -OpenTelemetry.Exporter.ZPages.ZPagesExporter.ZPagesExporter(OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions options) -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.RetentionTime.get -> long -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.RetentionTime.set -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.Url.get -> string -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.Url.set -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.ZPagesExporterOptions() -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Dispose() -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Start(System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Stop() -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.ZPagesExporterStatsHttpServer(OpenTelemetry.Exporter.ZPages.ZPagesExporter exporter) -> void -OpenTelemetry.Exporter.ZPages.ZPagesProcessor -OpenTelemetry.Exporter.ZPages.ZPagesProcessor.ZPagesProcessor(OpenTelemetry.Exporter.ZPages.ZPagesExporter exporter) -> void -OpenTelemetry.Trace.ZPagesExporterHelperExtensions -override OpenTelemetry.Exporter.ZPages.ZPagesExporter.Export(in OpenTelemetry.Batch batch) -> OpenTelemetry.ExportResult -override OpenTelemetry.Exporter.ZPages.ZPagesProcessor.OnEnd(System.Diagnostics.Activity activity) -> void -override OpenTelemetry.Exporter.ZPages.ZPagesProcessor.OnStart(System.Diagnostics.Activity activity) -> void -static OpenTelemetry.Trace.ZPagesExporterHelperExtensions.AddZPagesExporter(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder -virtual OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Dispose(bool disposing) -> void \ No newline at end of file diff --git a/src/OpenTelemetry.Exporter.ZPages/.publicApi/netstandard2.0/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.ZPages/.publicApi/netstandard2.0/PublicAPI.Shipped.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/OpenTelemetry.Exporter.ZPages/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.ZPages/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt deleted file mode 100644 index faf071f21a0..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ /dev/null @@ -1,21 +0,0 @@ -OpenTelemetry.Exporter.ZPages.ZPagesExporter -OpenTelemetry.Exporter.ZPages.ZPagesExporter.ZPagesExporter(OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions options) -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.RetentionTime.get -> long -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.RetentionTime.set -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.Url.get -> string -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.Url.set -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterOptions.ZPagesExporterOptions() -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Dispose() -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Start(System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Stop() -> void -OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.ZPagesExporterStatsHttpServer(OpenTelemetry.Exporter.ZPages.ZPagesExporter exporter) -> void -OpenTelemetry.Exporter.ZPages.ZPagesProcessor -OpenTelemetry.Exporter.ZPages.ZPagesProcessor.ZPagesProcessor(OpenTelemetry.Exporter.ZPages.ZPagesExporter exporter) -> void -OpenTelemetry.Trace.ZPagesExporterHelperExtensions -override OpenTelemetry.Exporter.ZPages.ZPagesExporter.Export(in OpenTelemetry.Batch batch) -> OpenTelemetry.ExportResult -override OpenTelemetry.Exporter.ZPages.ZPagesProcessor.OnEnd(System.Diagnostics.Activity activity) -> void -override OpenTelemetry.Exporter.ZPages.ZPagesProcessor.OnStart(System.Diagnostics.Activity activity) -> void -static OpenTelemetry.Trace.ZPagesExporterHelperExtensions.AddZPagesExporter(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder -virtual OpenTelemetry.Exporter.ZPages.ZPagesExporterStatsHttpServer.Dispose(bool disposing) -> void \ No newline at end of file diff --git a/src/OpenTelemetry.Exporter.ZPages/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.ZPages/AssemblyInfo.cs deleted file mode 100644 index 0332b70b3e2..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System.Runtime.CompilerServices; - -#if SIGNED -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.ZPages.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] -#else -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.ZPages.Tests")] -#endif diff --git a/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md b/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md deleted file mode 100644 index f036fbdbd8b..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/CHANGELOG.md +++ /dev/null @@ -1,148 +0,0 @@ -# Changelog - -## Unreleased - -## 1.5.0-beta.1 - -Released 2023-Jun-05 - -* Bumped the package version to `1.5.0-beta.1` to keep its major and minor - version in sync with that of the core packages. This would make it more - intuitive for users to figure out what version of core packages would work - with a given version of this package. The pre-release identifier has also been - changed from `rc` to `beta` as we believe this more accurately reflects the - status of this package. We believe the `rc` identifier will be more - appropriate as semantic conventions reach stability. - -## 1.0.0-rc9.14 - -Released 2023-Feb-24 - -* Updated OTel SDK dependency to 1.4.0 - -## 1.4.0-rc9.13 - -Released 2023-Feb-10 - -## 1.0.0-rc9.12 - -Released 2023-Feb-01 - -## 1.0.0-rc9.11 - -Released 2023-Jan-09 - -## 1.0.0-rc9.10 - -Released 2022-Dec-12 - -## 1.0.0-rc9.9 - -Released 2022-Nov-07 - -## 1.0.0-rc9.8 - -Released 2022-Oct-17 - -## 1.0.0-rc9.7 - -Released 2022-Sep-29 - -## 1.0.0-rc9.6 - -Released 2022-Aug-18 - -## 1.0.0-rc9.5 - -Released 2022-Aug-02 - -## 1.0.0-rc9.4 - -Released 2022-Jun-03 - -* Removes .NET Framework 4.6.1. The minimum .NET Framework - version supported is .NET 4.6.2. ([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190)) - -## 1.0.0-rc9.3 - -Released 2022-Apr-15 - -## 1.0.0-rc9.2 - -Released 2022-Apr-12 - -## 1.0.0-rc9.1 - -Released 2022-Mar-30 - -## 1.0.0-rc10 (broken. use 1.0.0-rc9.1 and newer) - -Released 2022-Mar-04 - -## 1.0.0-rc9 - -Released 2022-Feb-02 - -## 1.0.0-rc8 - -Released 2021-Oct-08 - -* Removes .NET Framework 4.5.2, 4.6 support. The minimum .NET Framework version - supported is .NET 4.6.1. ([2138](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2138)) - -## 1.0.0-rc7 - -Released 2021-Jul-12 - -## 1.0.0-rc6 - -Released 2021-Jun-25 - -## 1.0.0-rc5 - -Released 2021-Jun-09 - -## 1.0.0-rc3 - -Released 2021-Mar-19 - -## 1.0.0-rc2 - -Released 2021-Jan-29 - -## 1.0.0-rc1.1 - -Released 2020-Nov-17 - -## 0.8.0-beta.1 - -Released 2020-Nov-5 - -## 0.7.0-beta.1 - -Released 2020-Oct-16 - -## 0.6.0-beta.1 - -Released 2020-Sep-15 - -## 0.5.0-beta.2 - -Released 2020-08-28 - -* Renamed extension method from `UseZPagesExporter` to `AddZPagesExporter`. - ([#1066](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1066)) -* Changed `ZPagesExporter` to use `ZPagesProcessor` by default. - ([#1108](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1108)) - -## 0.4.0-beta.2 - -Released 2020-07-24 - -* First beta release - -## 0.3.0-beta - -Released 2020-07-23 - -* Initial release diff --git a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityAggregate.cs b/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityAggregate.cs deleted file mode 100644 index b15fa015a2c..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityAggregate.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Diagnostics; - -namespace OpenTelemetry.Exporter.ZPages.Implementation -{ - /// - /// Stores the activity information aggregated according to activity name. - /// - internal sealed class ZPagesActivityAggregate - { - /// - /// Initializes a new instance of the class. - /// - public ZPagesActivityAggregate() - { - } - - /// - /// Initializes a new instance of the class when activity data is provided. - /// - /// Input activity data to be used for initialization. - public ZPagesActivityAggregate(Activity activity) - { - this.Name = activity.DisplayName; - this.Count = 1; - this.EndedCount = 0; - this.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds(); - this.TotalLatency = 0; - this.AvgLatencyTotal = 0; - this.ErrorCount = 0; - } - - /// - /// Gets or sets the name of the activity. - /// - public string Name { get; set; } - - /// - /// Gets or sets the total count of the activity. - /// - public long Count { get; set; } - - /// - /// Gets or sets the total count of the ended activity. - /// - public long EndedCount { get; set; } - - /// - /// Gets or sets the total error count of the activity. - /// - public long ErrorCount { get; set; } - - /// - /// Gets or sets the total average latency of the activity. - /// - public long AvgLatencyTotal { get; set; } - - /// - /// Gets or sets the total latency of all activities. - /// - public long TotalLatency { get; set; } - - /// - /// Gets or sets the last updated timestamp. - /// - public long LastUpdated { get; set; } - - /// - /// Calculates and returns the total average latency. - /// - /// Total average latency. - public long GetTotalAverageLatency() - { - this.AvgLatencyTotal = this.TotalLatency / this.EndedCount; - this.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds(); - return this.AvgLatencyTotal; - } - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityTracker.cs b/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityTracker.cs deleted file mode 100644 index e9200db6939..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesActivityTracker.cs +++ /dev/null @@ -1,133 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Collections.Concurrent; -using System.Timers; - -namespace OpenTelemetry.Exporter.ZPages.Implementation -{ - /// - /// Implements the ZPages Activity Queue which stores all the required activity information. - /// - internal static class ZPagesActivityTracker - { - private static readonly long StartTime; - - /// - /// Initializes static members of the class. - /// - static ZPagesActivityTracker() - { - ZQueue = new LinkedList>(); - ProcessingList = new Dictionary(); - CurrentMinuteList = new ConcurrentDictionary(); - CurrentHourList = new ConcurrentDictionary(); - TotalCount = new Dictionary(); - TotalEndedCount = new Dictionary(); - TotalErrorCount = new Dictionary(); - TotalLatency = new Dictionary(); - StartTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - - /// - /// Gets or sets ZQueue which stores the minute-wise activity information. - /// - public static LinkedList> ZQueue { get; set; } - - /// - /// Gets or sets the current minute activity information list. - /// - public static ConcurrentDictionary CurrentMinuteList { get; set; } - - /// - /// Gets or sets the current hour activity information list. - /// - public static ConcurrentDictionary CurrentHourList { get; set; } - - /// - /// Gets or sets the processing activity information list. This holds the names of the activities which have not ended yet, along with the active count. - /// - public static Dictionary ProcessingList { get; set; } - - /// - /// Gets or sets the count of activities name-wise. - /// - public static Dictionary TotalCount { get; set; } - - /// - /// Gets or sets the count of ended activities name-wise. - /// - public static Dictionary TotalEndedCount { get; set; } - - /// - /// Gets or sets the count of activity errors according to activity name. - /// - public static Dictionary TotalErrorCount { get; set; } - - /// - /// Gets or sets the total latency of activities name-wise. - /// - public static Dictionary TotalLatency { get; set; } - - /// - /// Gets or sets the retention time (in milliseconds) for the metrics. - /// - public static long RetentionTime { get; set; } - - /// - /// Triggers Calculations every minute. - /// - /// Source. - /// Event Arguments. - public static void PurgeCurrentMinuteData(object source, ElapsedEventArgs e) - { - // Enqueue the last minute's activity information list to ZQueue - ZQueue.AddFirst(new Dictionary(CurrentMinuteList)); - - // Clear the current minute activity list to start recording new activities only - CurrentMinuteList.Clear(); - - // Remove the stale activity information which is at the end of the list - if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - StartTime >= RetentionTime) - { - ZQueue.RemoveLast(); - } - } - - /// - /// Triggers Calculations every hour. - /// - /// Source. - /// Event Arguments. - public static void PurgeCurrentHourData(object source, ElapsedEventArgs e) - { - // Clear the last hour's activity information list - CurrentHourList.Clear(); - } - - internal static void Reset() - { - TotalCount.Clear(); - TotalLatency.Clear(); - CurrentHourList.Clear(); - CurrentMinuteList.Clear(); - ZQueue.Clear(); - ProcessingList.Clear(); - TotalEndedCount.Clear(); - TotalErrorCount.Clear(); - } - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesExporterEventSource.cs b/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesExporterEventSource.cs deleted file mode 100644 index acdddea8b18..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesExporterEventSource.cs +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Diagnostics.Tracing; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Exporter.ZPages.Implementation -{ - /// - /// EventSource events emitted from the project. - /// - [EventSource(Name = "OpenTelemetry-Exporter-ZPages")] - internal sealed class ZPagesExporterEventSource : EventSource - { - public static ZPagesExporterEventSource Log = new(); - - [NonEvent] - public void FailedExport(Exception ex) - { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.FailedExport(ex.ToInvariantString()); - } - } - - [NonEvent] - public void CanceledExport(Exception ex) - { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.CanceledExport(ex.ToInvariantString()); - } - } - - [NonEvent] - public void FailedProcess(Exception ex) - { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.FailedProcess(ex.ToInvariantString()); - } - } - - [Event(1, Message = "Failed to export activities: '{0}'", Level = EventLevel.Error)] - public void FailedExport(string exception) - { - this.WriteEvent(1, exception); - } - - [Event(2, Message = "Canceled to export activities: '{0}'", Level = EventLevel.Error)] - public void CanceledExport(string exception) - { - this.WriteEvent(2, exception); - } - - [Event(3, Message = "Failed to process activity: '{0}'", Level = EventLevel.Error)] - public void FailedProcess(string exception) - { - this.WriteEvent(3, exception); - } - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesStatsBuilder.cs b/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesStatsBuilder.cs deleted file mode 100644 index 2409d02ef86..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesStatsBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -namespace OpenTelemetry.Exporter.ZPages.Implementation -{ - internal sealed class ZPagesStatsBuilder - { - public const string ContentType = "text/html"; - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/OpenTelemetry.Exporter.ZPages.csproj b/src/OpenTelemetry.Exporter.ZPages/OpenTelemetry.Exporter.ZPages.csproj deleted file mode 100644 index e5b6fbc3f20..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/OpenTelemetry.Exporter.ZPages.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - - netstandard2.0;net462 - ZPages exporter for OpenTelemetry .NET - $(PackageTags);ZPages;distributed-tracing - - - disable - - - - - - - - - - diff --git a/src/OpenTelemetry.Exporter.ZPages/README.md b/src/OpenTelemetry.Exporter.ZPages/README.md deleted file mode 100644 index 28d49716560..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# ZPages Exporter for OpenTelemetry .NET - -[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Exporter.ZPages.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.ZPages) -[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Exporter.ZPages.svg)](https://www.nuget.org/packages/OpenTelemetry.Exporter.ZPages) - -## Installation - -```shell -dotnet add package --prerelease OpenTelemetry.Exporter.ZPages -``` - -## Configuration - -You can configure the `ZPagesExporter` by following the directions below: - -* `Url`: The url to listen to. Typically it ends with `/rpcz` like `http://localhost:7284/rpcz/`. -* `RetentionTime`: The retention time (in milliseconds) for the metrics. - -See the -[`TestZPagesExporter.cs`](../../examples/Console/TestZPagesExporter.cs) -for an example of how to use the exporter. - -## References - -* [OpenTelemetry Project](https://opentelemetry.io/) -* [zPages](https://opencensus.io/zpages/) diff --git a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporter.cs b/src/OpenTelemetry.Exporter.ZPages/ZPagesExporter.cs deleted file mode 100644 index 973e966b8ec..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporter.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Diagnostics; -using System.Timers; -using OpenTelemetry.Exporter.ZPages.Implementation; -using OpenTelemetry.Internal; -using Timer = System.Timers.Timer; - -namespace OpenTelemetry.Exporter.ZPages -{ - /// - /// Implements ZPages exporter. - /// - public class ZPagesExporter : BaseExporter - { - internal readonly ZPagesExporterOptions Options; - private readonly Timer minuteTimer; - private readonly Timer hourTimer; - - /// - /// Initializes a new instance of the class. - /// - /// Options for the exporter. - public ZPagesExporter(ZPagesExporterOptions options) - { - Guard.ThrowIfNull(options?.RetentionTime); - - ZPagesActivityTracker.RetentionTime = options.RetentionTime; - - this.Options = options; - - // Create a timer with one minute interval - this.minuteTimer = new Timer(60000); - this.minuteTimer.Elapsed += new ElapsedEventHandler(ZPagesActivityTracker.PurgeCurrentMinuteData); - this.minuteTimer.Enabled = true; - - // Create a timer with one hour interval - this.hourTimer = new Timer(3600000); - this.hourTimer.Elapsed += new ElapsedEventHandler(ZPagesActivityTracker.PurgeCurrentHourData); - this.hourTimer.Enabled = true; - } - - /// - public override ExportResult Export(in Batch batch) - { - // var spanDatas = batch as SpanData[] ?? batch.ToArray(); - return ExportResult.Success; - } - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterHelperExtensions.cs deleted file mode 100644 index a7bcea87246..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterHelperExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using OpenTelemetry.Exporter.ZPages; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Trace -{ - /// - /// Extension methods to simplify registering of Zipkin exporter. - /// - public static class ZPagesExporterHelperExtensions - { - /// - /// Registers a Zipkin exporter that will receive instances. - /// - /// builder to use. - /// Exporter configuration options. - /// The instance of to chain the calls. - public static TracerProviderBuilder AddZPagesExporter( - this TracerProviderBuilder builder, - Action configure = null) - { - Guard.ThrowIfNull(builder); - - var exporterOptions = new ZPagesExporterOptions(); - configure?.Invoke(exporterOptions); - var zpagesExporter = new ZPagesExporter(exporterOptions); - - // TODO: Pick Simple vs Batching based on ZipkinExporterOptions - return builder.AddProcessor(new ZPagesProcessor(zpagesExporter)); - } - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterOptions.cs b/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterOptions.cs deleted file mode 100644 index 221382b455d..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterOptions.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -namespace OpenTelemetry.Exporter.ZPages -{ - /// - /// Options to run zpages exporter. - /// - public class ZPagesExporterOptions - { - /// - /// Gets or sets the URL to listen to. Typically it ends with /rpcz like http://localhost:7284/rpcz/. - /// - public string Url { get; set; } - - /// - /// Gets or sets the retention time (in milliseconds) for the metrics. Default: 100000. - /// - public long RetentionTime { get; set; } = 100000; - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterStatsHttpServer.cs b/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterStatsHttpServer.cs deleted file mode 100644 index 360a12e2a6b..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/ZPagesExporterStatsHttpServer.cs +++ /dev/null @@ -1,194 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Collections.Concurrent; -using System.Net; -using OpenTelemetry.Exporter.ZPages.Implementation; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Exporter.ZPages -{ - /// - /// A HTTP listener used to expose ZPages stats. - /// - public class ZPagesExporterStatsHttpServer : IDisposable - { - private readonly HttpListener httpListener = new(); - private readonly object syncObject = new(); - - private CancellationTokenSource tokenSource; - private Task workerThread; - - /// - /// Initializes a new instance of the class. - /// - /// The instance. - public ZPagesExporterStatsHttpServer(ZPagesExporter exporter) - { - Guard.ThrowIfNull(exporter?.Options?.Url); - - this.httpListener.Prefixes.Add(exporter.Options.Url); - } - - /// - /// Start exporter. - /// - /// An optional that can be used to stop the http server. - public void Start(CancellationToken token = default) - { - lock (this.syncObject) - { - if (this.tokenSource != null) - { - return; - } - - // link the passed in token if not null - this.tokenSource = token == default ? - new CancellationTokenSource() : - CancellationTokenSource.CreateLinkedTokenSource(token); - - this.httpListener.Start(); - - this.workerThread = Task.Factory.StartNew(this.WorkerThread, default, TaskCreationOptions.LongRunning, TaskScheduler.Default); - } - } - - /// - /// Stop exporter. - /// - public void Stop() - { - lock (this.syncObject) - { - if (this.tokenSource == null) - { - return; - } - - this.tokenSource.Cancel(); - this.workerThread.Wait(); - this.tokenSource = null; - } - } - - /// - /// Disposes of managed resources. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases the unmanaged resources used by this class and optionally releases the managed resources. - /// - /// to release both managed and unmanaged resources; to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (this.httpListener != null && this.httpListener.IsListening) - { - this.Stop(); - } - } - - private void WorkerThread() - { - try - { - while (!this.tokenSource.IsCancellationRequested) - { - Task ctxTask = this.httpListener.GetContextAsync(); - ctxTask.Wait(this.tokenSource.Token); - - var ctx = ctxTask.Result; - - ctx.Response.StatusCode = 200; - ctx.Response.ContentType = ZPagesStatsBuilder.ContentType; - - using Stream output = ctx.Response.OutputStream; - using var writer = new StreamWriter(output); - writer.WriteLine(""); - writer.WriteLine("RPC Stats" + - "" + - "" + - "" + - "" + - ""); - writer.WriteLine("

RPC Stats

" + - "" + - "" + - "" + - ""); - - ConcurrentDictionary currentHourSpanList = ZPagesActivityTracker.CurrentHourList; - ConcurrentDictionary currentMinuteSpanList = ZPagesActivityTracker.CurrentMinuteList; - - // Put span information in each row of the table - foreach (var spanName in currentHourSpanList.Keys) - { - long countInLastMinute = 0; - long countInLastHour = 0; - long averageLatencyInLastMinute = 0; - long averageLatencyInLastHour = 0; - long errorCountInLastMinute = 0; - long errorCountInLastHour = 0; - - if (currentMinuteSpanList.ContainsKey(spanName)) - { - currentMinuteSpanList.TryGetValue(spanName, out ZPagesActivityAggregate minuteSpanInformation); - countInLastMinute = minuteSpanInformation.EndedCount + ZPagesActivityTracker.ProcessingList[spanName]; - averageLatencyInLastMinute = minuteSpanInformation.AvgLatencyTotal; - errorCountInLastMinute = minuteSpanInformation.ErrorCount; - } - - currentHourSpanList.TryGetValue(spanName, out ZPagesActivityAggregate hourSpanInformation); - countInLastHour = hourSpanInformation.EndedCount + ZPagesActivityTracker.ProcessingList[spanName]; - averageLatencyInLastHour = hourSpanInformation.AvgLatencyTotal; - errorCountInLastHour = hourSpanInformation.ErrorCount; - - long totalAverageLatency = ZPagesActivityTracker.TotalLatency[spanName] / ZPagesActivityTracker.TotalEndedCount[spanName]; - - DateTimeOffset dateTimeOffset; - - dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(hourSpanInformation.LastUpdated); - - writer.WriteLine("" + - "" + - ""); - } - - writer.WriteLine("
Span NameTotal CountCount in last minuteCount in last hourAverage Latency (ms)Average Latency in last minute (ms)Average Latency in last hour (ms)Total ErrorsErrors in last minuteErrors in last minuteLast Updated
" + hourSpanInformation.Name + "" + ZPagesActivityTracker.TotalCount[spanName] + "" + countInLastMinute + "" + countInLastHour + "" + totalAverageLatency + "" + averageLatencyInLastMinute + "" + averageLatencyInLastHour + "" + ZPagesActivityTracker.TotalErrorCount[spanName] + "" + errorCountInLastMinute + "" + errorCountInLastHour + "" + dateTimeOffset + " GMT" + "
"); - writer.WriteLine("
"); - } - } - catch (OperationCanceledException ex) - { - ZPagesExporterEventSource.Log.CanceledExport(ex); - } - catch (Exception ex) - { - ZPagesExporterEventSource.Log.FailedExport(ex); - } - finally - { - this.httpListener.Stop(); - this.httpListener.Close(); - } - } - } -} diff --git a/src/OpenTelemetry.Exporter.ZPages/ZPagesProcessor.cs b/src/OpenTelemetry.Exporter.ZPages/ZPagesProcessor.cs deleted file mode 100644 index c7a0c7951c6..00000000000 --- a/src/OpenTelemetry.Exporter.ZPages/ZPagesProcessor.cs +++ /dev/null @@ -1,117 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Diagnostics; -using OpenTelemetry.Exporter.ZPages.Implementation; -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Exporter.ZPages -{ - /// - /// Implements the zpages span processor that exports spans in OnEnd call without batching. - /// - public class ZPagesProcessor : BaseProcessor - { - private readonly ZPagesExporter exporter; - - /// - /// Initializes a new instance of the class. - /// - /// Zpages Span processor instance. - public ZPagesProcessor(ZPagesExporter exporter) - { - this.exporter = exporter; - } - - /// - public override void OnStart(Activity activity) - { - Debug.Assert(activity != null, "Activity should not be null"); - - if (!ZPagesActivityTracker.ProcessingList.ContainsKey(activity.DisplayName)) - { - // If the span name is not in the processing span list, add it to the span list, the total count list, the ended count list and the error count list. - ZPagesActivityTracker.ProcessingList.Add(activity.DisplayName, 1); - ZPagesActivityTracker.TotalCount.Add(activity.DisplayName, 1); - ZPagesActivityTracker.TotalEndedCount.Add(activity.DisplayName, 0); - ZPagesActivityTracker.TotalErrorCount.Add(activity.DisplayName, 0); - ZPagesActivityTracker.TotalLatency.Add(activity.DisplayName, 0); - } - else - { - // If the span name already exists, then increment the numbers in processing list as well as the total count list. - ZPagesActivityTracker.ProcessingList.TryGetValue(activity.DisplayName, out var activeCount); - ZPagesActivityTracker.ProcessingList[activity.DisplayName] = activeCount + 1; - ZPagesActivityTracker.TotalCount.TryGetValue(activity.DisplayName, out var totalCount); - ZPagesActivityTracker.TotalCount[activity.DisplayName] = totalCount + 1; - } - } - - /// - public override void OnEnd(Activity activity) - { - Debug.Assert(activity != null, "Activity should not be null"); - try - { - // If the span name is not in the current minute list, add it to the span list. - if (!ZPagesActivityTracker.CurrentMinuteList.ContainsKey(activity.DisplayName)) - { - ZPagesActivityTracker.CurrentMinuteList.TryAdd(activity.DisplayName, new ZPagesActivityAggregate(activity)); - } - - // If the span name is not in the current hour list, add it to the span list. - if (!ZPagesActivityTracker.CurrentHourList.ContainsKey(activity.DisplayName)) - { - ZPagesActivityTracker.CurrentHourList.TryAdd(activity.DisplayName, new ZPagesActivityAggregate(activity)); - } - - ZPagesActivityTracker.CurrentMinuteList.TryGetValue(activity.DisplayName, out var minuteSpanInformation); - ZPagesActivityTracker.CurrentHourList.TryGetValue(activity.DisplayName, out var hourSpanInformation); - ZPagesActivityTracker.ProcessingList.TryGetValue(activity.DisplayName, out var activeCount); - - // Decrement the active span count in processing list, Increment the count of ended spans and calculate the average latency values for one minute and one hour. - ZPagesActivityTracker.ProcessingList[activity.DisplayName] = activeCount - 1; - minuteSpanInformation.EndedCount++; - hourSpanInformation.EndedCount++; - minuteSpanInformation.TotalLatency += (long)activity.Duration.TotalMilliseconds; - hourSpanInformation.TotalLatency += (long)activity.Duration.TotalMilliseconds; - ZPagesActivityTracker.TotalLatency[activity.DisplayName] += (long)activity.Duration.TotalMilliseconds; - minuteSpanInformation.GetTotalAverageLatency(); - hourSpanInformation.GetTotalAverageLatency(); - ZPagesActivityTracker.TotalEndedCount.TryGetValue(activity.DisplayName, out var endedCount); - ZPagesActivityTracker.TotalEndedCount[activity.DisplayName] = endedCount + 1; - - // Increment the error count, if it applies in all applicable lists. - if (activity.GetStatus() != Status.Ok) - { - minuteSpanInformation.ErrorCount++; - hourSpanInformation.ErrorCount++; - ZPagesActivityTracker.TotalErrorCount.TryGetValue(activity.DisplayName, out var errorCount); - ZPagesActivityTracker.TotalErrorCount[activity.DisplayName] = errorCount + 1; - } - - // Set the last updated timestamp. - minuteSpanInformation.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds(); - hourSpanInformation.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds(); - } - catch (Exception ex) - { - ZPagesExporterEventSource.Log.FailedProcess(ex); - Console.Write("OnEnd {0}", ex); - } - } - } -} diff --git a/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj b/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj index ef9a89b3874..8bdc01e944f 100644 --- a/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj +++ b/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj @@ -16,7 +16,6 @@ - diff --git a/test/OpenTelemetry.Exporter.ZPages.Tests/EventSourceTest.cs b/test/OpenTelemetry.Exporter.ZPages.Tests/EventSourceTest.cs deleted file mode 100644 index 03db587cc39..00000000000 --- a/test/OpenTelemetry.Exporter.ZPages.Tests/EventSourceTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using OpenTelemetry.Exporter.ZPages.Implementation; -using OpenTelemetry.Tests; -using Xunit; - -namespace OpenTelemetry.Exporter.ZPages.Tests -{ - public class EventSourceTest - { - [Fact] - public void EventSourceTest_ZPagesExporterEventSource() - { - EventSourceTestHelper.MethodsAreImplementedConsistentlyWithTheirAttributes(ZPagesExporterEventSource.Log); - } - } -} diff --git a/test/OpenTelemetry.Exporter.ZPages.Tests/OpenTelemetry.Exporter.ZPages.Tests.csproj b/test/OpenTelemetry.Exporter.ZPages.Tests/OpenTelemetry.Exporter.ZPages.Tests.csproj deleted file mode 100644 index b8a159610c9..00000000000 --- a/test/OpenTelemetry.Exporter.ZPages.Tests/OpenTelemetry.Exporter.ZPages.Tests.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - - net6.0 - $(TargetFrameworks);net462 - - - disable - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - - - - - - diff --git a/test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesActivityTrackerTests.cs b/test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesActivityTrackerTests.cs deleted file mode 100644 index 80aa1ae66a4..00000000000 --- a/test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesActivityTrackerTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Diagnostics; -using OpenTelemetry.Exporter.ZPages.Implementation; -using Xunit; - -namespace OpenTelemetry.Exporter.ZPages.Tests -{ - public class ZPagesActivityTrackerTests - { - [Fact] - public void CheckingPurge() - { - using var activity1 = new Activity("new"); - ZPagesActivityTracker.CurrentHourList.TryAdd("new", new ZPagesActivityAggregate(activity1)); - Assert.NotEmpty(ZPagesActivityTracker.CurrentHourList); - ZPagesActivityTracker.PurgeCurrentHourData(null, null); - Assert.Empty(ZPagesActivityTracker.CurrentHourList); - - using var activity2 = new Activity("new"); - ZPagesActivityTracker.CurrentMinuteList.TryAdd("new", new ZPagesActivityAggregate(activity2)); - Assert.NotEmpty(ZPagesActivityTracker.CurrentMinuteList); - ZPagesActivityTracker.PurgeCurrentMinuteData(null, null); - Assert.Empty(ZPagesActivityTracker.CurrentMinuteList); - - ZPagesActivityTracker.ProcessingList.Clear(); - } - } -} diff --git a/test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesExporterTests.cs b/test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesExporterTests.cs deleted file mode 100644 index 82fe22bc65c..00000000000 --- a/test/OpenTelemetry.Exporter.ZPages.Tests/ZPagesExporterTests.cs +++ /dev/null @@ -1,265 +0,0 @@ -// -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -using System.Diagnostics; -using System.Net.Http; -using OpenTelemetry.Exporter.ZPages.Implementation; -using OpenTelemetry.Resources; -using OpenTelemetry.Tests; -using OpenTelemetry.Trace; -using Xunit; - -namespace OpenTelemetry.Exporter.ZPages.Tests -{ - public class ZPagesExporterTests - { - private static readonly HttpClient HttpClient = new(); - - static ZPagesExporterTests() - { - Activity.DefaultIdFormat = ActivityIdFormat.W3C; - Activity.ForceDefaultIdFormat = true; - - var listener = new ActivityListener - { - ShouldListenTo = _ => true, - Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, - }; - - ActivitySource.AddActivityListener(listener); - } - - [Fact] - public void CheckingBadArgs() - { - TracerProviderBuilder builder = null; - Assert.Throws(() => builder.AddZPagesExporter()); - } - - [Fact] - public void CheckingCustomActivityProcessor() - { - const string ActivitySourceName = "zpages.test"; - Guid requestId = Guid.NewGuid(); - TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); - - bool startCalled = false; - bool endCalled = false; - - testActivityProcessor.StartAction = - (a) => - { - startCalled = true; - }; - - testActivityProcessor.EndAction = - (a) => - { - endCalled = true; - }; - - using var tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddSource(ActivitySourceName) - .AddProcessor(testActivityProcessor) - .AddZPagesExporter() - .Build(); - - using var source = new ActivitySource(ActivitySourceName); - using var activity = source.StartActivity("Test Zipkin Activity"); - activity?.Stop(); - - Assert.True(startCalled); - Assert.True(endCalled); - - ZPagesActivityTracker.Reset(); - } - - [Fact] - public void CheckingCustomOptions() - { - ZPagesExporterOptions options = new ZPagesExporterOptions - { - RetentionTime = 100_000, - Url = "http://localhost:7284/rpcz/", - }; - - ZPagesExporter exporter = new ZPagesExporter(options); - - Assert.Equal(options.Url, exporter.Options.Url); - Assert.Equal(options.RetentionTime, exporter.Options.RetentionTime); - } - - [Fact] - public async Task CheckingZPagesProcessor() - { - const string ActivitySourceName = "zpages.test"; - ZPagesExporterOptions options = new ZPagesExporterOptions - { - RetentionTime = 100_000, - Url = "http://localhost:7284/rpcz/", - }; - ZPagesExporter exporter = new ZPagesExporter(options); - var zpagesProcessor = new ZPagesProcessor(exporter); - - using var source = new ActivitySource(ActivitySourceName); - using var activity0 = source.StartActivity("Test Zipkin Activity 1"); - zpagesProcessor.OnStart(activity0); - - // checking size of dictionaries from ZPagesActivityTracker - Assert.Equal(1, ZPagesActivityTracker.ProcessingList.First().Value); - Assert.Equal(1, ZPagesActivityTracker.TotalCount.First().Value); - Assert.Single(ZPagesActivityTracker.TotalEndedCount); - Assert.Single(ZPagesActivityTracker.TotalErrorCount); - Assert.Single(ZPagesActivityTracker.TotalLatency); - - using var activity1 = source.StartActivity("Test Zipkin Activity 1"); - zpagesProcessor.OnStart(activity1); - - // checking size of dictionaries from ZPagesActivityTracker - Assert.Equal(2, ZPagesActivityTracker.ProcessingList.First().Value); - Assert.Equal(2, ZPagesActivityTracker.TotalCount.First().Value); - Assert.Single(ZPagesActivityTracker.TotalEndedCount); - Assert.Single(ZPagesActivityTracker.TotalErrorCount); - Assert.Single(ZPagesActivityTracker.TotalLatency); - - using var activity2 = source.StartActivity("Test Zipkin Activity 2"); - zpagesProcessor.OnStart(activity2); - - // checking size of dictionaries from ZPagesActivityTracker - Assert.Equal(2, ZPagesActivityTracker.ProcessingList.Count); - Assert.Equal(2, ZPagesActivityTracker.TotalCount.Count); - Assert.Equal(2, ZPagesActivityTracker.TotalEndedCount.Count); - Assert.Equal(2, ZPagesActivityTracker.TotalErrorCount.Count); - Assert.Equal(2, ZPagesActivityTracker.TotalLatency.Count); - - activity0?.Stop(); - activity1?.Stop(); - activity2?.Stop(); - zpagesProcessor.OnEnd(activity0); - zpagesProcessor.OnEnd(activity1); - zpagesProcessor.OnEnd(activity2); - - // checking if activities were processed - Assert.Equal(0, ZPagesActivityTracker.ProcessingList.First().Value); - Assert.Equal(0, ZPagesActivityTracker.ProcessingList.Last().Value); - Assert.Empty(ZPagesActivityTracker.ZQueue); - - var zpagesServer = new ZPagesExporterStatsHttpServer(exporter); - zpagesServer.Start(); - - using var httpResponseMessage = await HttpClient.GetAsync("http://localhost:7284/rpcz/").ConfigureAwait(false); - Assert.True(httpResponseMessage.IsSuccessStatusCode); - - var content = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); - Assert.Contains($"Test Zipkin Activity 1", content); - Assert.Contains($"Test Zipkin Activity 2", content); - - zpagesProcessor.Dispose(); - zpagesServer.Stop(); - zpagesServer.Dispose(); - exporter.Dispose(); - } - - internal static Activity CreateTestActivity( - bool setAttributes = true, - Dictionary additionalAttributes = null, - bool addEvents = true, - bool addLinks = true, - Resource resource = null, - ActivityKind kind = ActivityKind.Client) - { - var startTimestamp = DateTime.UtcNow; - var endTimestamp = startTimestamp.AddSeconds(60); - var eventTimestamp = DateTime.UtcNow; - var traceId = ActivityTraceId.CreateFromString("e8ea7e9ac72de94e91fabc613f9686b2".AsSpan()); - - var parentSpanId = ActivitySpanId.CreateFromBytes(new byte[] { 12, 23, 34, 45, 56, 67, 78, 89 }); - - var attributes = new Dictionary - { - { "stringKey", "value" }, - { "longKey", 1L }, - { "longKey2", 1 }, - { "doubleKey", 1D }, - { "doubleKey2", 1F }, - { "boolKey", true }, - }; - if (additionalAttributes != null) - { - foreach (var attribute in additionalAttributes) - { - attributes.Add(attribute.Key, attribute.Value); - } - } - - var events = new List - { - new ActivityEvent( - "Event1", - eventTimestamp, - new ActivityTagsCollection(new Dictionary - { - { "key", "value" }, - })), - new ActivityEvent( - "Event2", - eventTimestamp, - new ActivityTagsCollection(new Dictionary - { - { "key", "value" }, - })), - }; - - var linkedSpanId = ActivitySpanId.CreateFromString("888915b6286b9c41".AsSpan()); - - var activitySource = new ActivitySource(nameof(CreateTestActivity)); - - var tags = setAttributes ? - attributes.Select(kvp => new KeyValuePair(kvp.Key, kvp.Value.ToString())) - : null; - var links = addLinks ? - new[] - { - new ActivityLink(new ActivityContext( - traceId, - linkedSpanId, - ActivityTraceFlags.Recorded)), - } - : null; - - var activity = activitySource.StartActivity( - "Name", - kind, - parentContext: new ActivityContext(traceId, parentSpanId, ActivityTraceFlags.Recorded), - tags, - links, - startTime: startTimestamp); - - if (addEvents) - { - foreach (var evnt in events) - { - activity.AddEvent(evnt); - } - } - - activity.SetEndTime(endTimestamp); - activity.Stop(); - - return activity; - } - } -} From 7333a557b4147d7ac01a73b95a2f57836dc909f4 Mon Sep 17 00:00:00 2001 From: mfogliatto <2962955+mfogliatto@users.noreply.github.com> Date: Wed, 21 Jun 2023 03:53:15 +0200 Subject: [PATCH 3/7] Add ToOtlpLog unit tests for scopes (cont.) (#4578) --- .../OtlpLogExporterTests.cs | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs index d71303c845a..cdd73675b9c 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs @@ -14,6 +14,7 @@ // limitations under the License. // +using System.Collections.ObjectModel; using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -676,6 +677,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeStrin // Assert. var logRecord = logRecords.Single(); var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.Equal(2, otlpLogRecord.Attributes.Count); var actualScope = TryGetAttribute(otlpLogRecord, scopeKey); Assert.NotNull(actualScope); Assert.Equal(scopeKey, actualScope.Key); @@ -714,6 +716,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeBoolV // Assert. var logRecord = logRecords.Single(); var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.Equal(2, otlpLogRecord.Attributes.Count); var actualScope = TryGetAttribute(otlpLogRecord, scopeKey); Assert.NotNull(actualScope); Assert.Equal(scopeKey, actualScope.Key); @@ -764,6 +767,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeIntVa // Assert. var logRecord = logRecords.Single(); var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.Equal(2, otlpLogRecord.Attributes.Count); var actualScope = TryGetAttribute(otlpLogRecord, scopeKey); Assert.NotNull(actualScope); Assert.Equal(scopeKey, actualScope.Key); @@ -802,6 +806,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubl // Assert. var logRecord = logRecords.Single(); var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.Equal(2, otlpLogRecord.Attributes.Count); var actualScope = TryGetAttribute(otlpLogRecord, scopeKey); Assert.NotNull(actualScope); Assert.Equal(scopeKey, actualScope.Key); @@ -840,12 +845,274 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubl // Assert. var logRecord = logRecords.Single(); var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.Equal(2, otlpLogRecord.Attributes.Count); var actualScope = TryGetAttribute(otlpLogRecord, scopeKey); Assert.NotNull(actualScope); Assert.Equal(scopeKey, actualScope.Key); Assert.Equal(scopeValue.ToString(), actualScope.Value.DoubleValue.ToString()); } + [Fact] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfTypeString_ScopeIsIgnored() + { + // Arrange. + var logRecords = new List(1); + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.IncludeScopes = true; + options.AddInMemoryExporter(logRecords); + }); + }); + var logger = loggerFactory.CreateLogger(nameof(OtlpLogExporterTests)); + + const string scopeState = "Some scope state"; + + // Act. + using (logger.BeginScope(scopeState)) + { + logger.LogInformation("Some log information message."); + } + + // Assert. + var logRecord = logRecords.Single(); + var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.NotNull(otlpLogRecord); + Assert.Single(otlpLogRecord.Attributes); + } + + [Theory] + [InlineData(typeof(int))] + [InlineData(typeof(float))] + [InlineData(typeof(decimal))] + [InlineData(typeof(char))] + [InlineData(typeof(bool))] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfPrimitiveTypes_ScopeIsIgnored(Type typeOfScopeState) + { + // Arrange. + var logRecords = new List(1); + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.IncludeScopes = true; + options.AddInMemoryExporter(logRecords); + }); + }); + var logger = loggerFactory.CreateLogger(nameof(OtlpLogExporterTests)); + + var scopeState = Activator.CreateInstance(typeOfScopeState); + + // Act. + using (logger.BeginScope(scopeState)) + { + logger.LogInformation("Some log information message."); + } + + // Assert. + var logRecord = logRecords.Single(); + var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.NotNull(otlpLogRecord); + Assert.Single(otlpLogRecord.Attributes); + } + + [Fact] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfDictionaryType_ScopeIsProcessed() + { + // Arrange. + var logRecords = new List(1); + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.IncludeScopes = true; + options.AddInMemoryExporter(logRecords); + }); + }); + var logger = loggerFactory.CreateLogger(nameof(OtlpLogExporterTests)); + + const string scopeKey = "Some scope key"; + var scopeState = new Dictionary() { { scopeKey, "Some scope value" } }; + + // Act. + using (logger.BeginScope(scopeState)) + { + logger.LogInformation("Some log information message."); + } + + // Assert. + var logRecord = logRecords.Single(); + var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.Equal(2, otlpLogRecord.Attributes.Count); + var actualScope = TryGetAttribute(otlpLogRecord, scopeKey); + Assert.NotNull(actualScope); + Assert.Equal(scopeKey, actualScope.Key); + } + + [Theory] + [InlineData(typeof(List>))] + [InlineData(typeof(ReadOnlyCollection>))] + [InlineData(typeof(HashSet>))] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfEnumerableType_ScopeIsProcessed(Type typeOfScopeState) + { + // Arrange. + var logRecords = new List(1); + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.IncludeScopes = true; + options.AddInMemoryExporter(logRecords); + }); + }); + var logger = loggerFactory.CreateLogger(nameof(OtlpLogExporterTests)); + + const string scopeKey = "Some scope key"; + var scopeValues = new List> { new KeyValuePair(scopeKey, "Some scope value") }; + var scopeState = Activator.CreateInstance(typeOfScopeState, scopeValues) as ICollection>; + + // Act. + using (logger.BeginScope(scopeState)) + { + logger.LogInformation("Some log information message."); + } + + // Assert. + var logRecord = logRecords.Single(); + var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + Assert.Equal(2, otlpLogRecord.Attributes.Count); + var actualScope = TryGetAttribute(otlpLogRecord, scopeKey); + Assert.NotNull(actualScope); + Assert.Equal(scopeKey, actualScope.Key); + } + + [Theory] + [InlineData("Same scope key", "Same scope key")] + [InlineData("Scope key 1", "Scope key 2")] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopesAreAdded_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2) + { + // Arrange. + var logRecords = new List(1); + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.IncludeScopes = true; + options.AddInMemoryExporter(logRecords); + }); + }); + var logger = loggerFactory.CreateLogger(nameof(OtlpLogExporterTests)); + + const string scopeValue1 = "Some scope value"; + const string scopeValue2 = "Some other scope value"; + + // Act. + using (logger.BeginScope(new List> + { + new KeyValuePair(scopeKey1, scopeValue1), + new KeyValuePair(scopeKey2, scopeValue2), + })) + { + logger.LogInformation("Some log information message."); + } + + // Assert. + var logRecord = logRecords.Single(); + var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + var allScopeValues = otlpLogRecord.Attributes + .Where(_ => _.Key == scopeKey1 || _.Key == scopeKey2) + .Select(_ => _.Value.StringValue); + Assert.Equal(2, allScopeValues.Count()); + Assert.Contains(scopeValue1, allScopeValues); + Assert.Contains(scopeValue2, allScopeValues); + } + + [Theory] + [InlineData("Same scope key", "Same scope key")] + [InlineData("Scope key 1", "Scope key 2")] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopeLevelsAreAdded_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2) + { + // Arrange. + var logRecords = new List(1); + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.IncludeScopes = true; + options.AddInMemoryExporter(logRecords); + }); + }); + var logger = loggerFactory.CreateLogger(nameof(OtlpLogExporterTests)); + + const string scopeValue1 = "Some scope value"; + const string scopeValue2 = "Some other scope value"; + + // Act. + using (logger.BeginScope(new List> { new KeyValuePair(scopeKey1, scopeValue1) })) + { + using (logger.BeginScope(new List> { new KeyValuePair(scopeKey2, scopeValue2) })) + { + logger.LogInformation("Some log information message."); + } + } + + // Assert. + var logRecord = logRecords.Single(); + var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + var allScopeValues = otlpLogRecord.Attributes + .Where(_ => _.Key == scopeKey1 || _.Key == scopeKey2) + .Select(_ => _.Value.StringValue); + Assert.Equal(2, allScopeValues.Count()); + Assert.Contains(scopeValue1, allScopeValues); + Assert.Contains(scopeValue2, allScopeValues); + } + + [Theory] + [InlineData("Same scope key", "Same scope key")] + [InlineData("Scope key 1", "Scope key 2")] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeIsUsedInLogMethod_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2) + { + // Arrange. + var logRecords = new List(1); + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(options => + { + options.IncludeScopes = true; + options.AddInMemoryExporter(logRecords); + }); + }); + var logger = loggerFactory.CreateLogger(nameof(OtlpLogExporterTests)); + + const string scopeValue1 = "Some scope value"; + const string scopeValue2 = "Some other scope value"; + + // Act. + using (logger.BeginScope(new List> + { + new KeyValuePair(scopeKey1, scopeValue1), + })) + { + logger.Log( + LogLevel.Error, + new EventId(1), + new List> { new KeyValuePair(scopeKey2, scopeValue2) }, + exception: new Exception("Some exception message"), + formatter: (s, e) => string.Empty); + } + + // Assert. + var logRecord = logRecords.Single(); + var otlpLogRecord = logRecord.ToOtlpLog(DefaultSdkLimitOptions); + var allScopeValues = otlpLogRecord.Attributes + .Where(_ => _.Key == scopeKey1 || _.Key == scopeKey2) + .Select(_ => _.Value.StringValue); + Assert.Equal(2, allScopeValues.Count()); + Assert.Contains(scopeValue1, allScopeValues); + Assert.Contains(scopeValue2, allScopeValues); + } + private static OtlpCommon.KeyValue TryGetAttribute(OtlpLogs.LogRecord record, string key) { return record.Attributes.FirstOrDefault(att => att.Key == key); From 8e5e7dde39aec7d2517461373a69297cdf630e10 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Date: Tue, 20 Jun 2023 19:33:01 -0700 Subject: [PATCH 4/7] update HttpSemanticConventions for Instrumentation.AspNetCore (#4537) --- .../Internal/SemanticConventions.cs | 12 ++ .../CHANGELOG.md | 5 + .../Implementation/HttpInListener.cs | 92 ++++++-- .../Implementation/HttpInMetricsListener.cs | 40 +++- ...stsCollectionsIsAccordingToTheSpecTests.cs | 157 +++++++------- ...llectionsIsAccordingToTheSpecTests_Dupe.cs | 204 ++++++++++++++++++ ...ollectionsIsAccordingToTheSpecTests_New.cs | 197 +++++++++++++++++ 7 files changed, 606 insertions(+), 101 deletions(-) create mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_Dupe.cs create mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs diff --git a/src/OpenTelemetry.Api/Internal/SemanticConventions.cs b/src/OpenTelemetry.Api/Internal/SemanticConventions.cs index a9c760378c3..2fb9b0022fe 100644 --- a/src/OpenTelemetry.Api/Internal/SemanticConventions.cs +++ b/src/OpenTelemetry.Api/Internal/SemanticConventions.cs @@ -110,5 +110,17 @@ internal static class SemanticConventions public const string AttributeExceptionType = "exception.type"; public const string AttributeExceptionMessage = "exception.message"; public const string AttributeExceptionStacktrace = "exception.stacktrace"; + + // Http v1.21.0 https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/trace/semantic_conventions/http.md + public const string AttributeClientSocketPort = "client.socket.port"; // replaces: "net.peer.port" (AttributeNetPeerPort) + public const string AttributeHttpRequestMethod = "http.request.method"; // replaces: "http.method" (AttributeHttpMethod) + public const string AttributeHttpResponseStatusCode = "http.response.status_code"; // replaces: "http.status_code" (AttributeHttpStatusCode) + public const string AttributeNetworkProtocolVersion = "network.protocol.version"; // replaces: "http.flavor" (AttributeHttpFlavor) + public const string AttributeServerAddress = "server.address"; // replaces: "net.host.name" (AttributeNetHostName) + public const string AttributeServerPort = "server.port"; // replaces: "net.host.port" (AttributeNetHostPort) + public const string AttributeUrlPath = "url.path"; // replaces: "http.target" (AttributeHttpTarget) + public const string AttributeUrlScheme = "url.scheme"; // replaces: "http.scheme" (AttributeHttpScheme) + public const string AttributeUrlQuery = "url.query"; + public const string AttributeUserAgentOriginal = "user_agent.original"; // replaces: "http.user_agent" (AttributeHttpUserAgent) } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index c40c65cb89f..301d158d93a 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +* Updated [Http Semantic Conventions](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/trace/semantic_conventions/http.md). + * This library can emit either old, new, or both attributes. Users can control + which attributes are emitted by setting the environment variable + `OTEL_SEMCONV_STABILITY_OPT_IN`. + ## 1.5.0-beta.1 Released 2023-Jun-05 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index d50241a3ba8..809473ad3aa 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -197,29 +197,66 @@ public void OnStartActivity(Activity activity, object payload) var path = (request.PathBase.HasValue || request.Path.HasValue) ? (request.PathBase + request.Path).ToString() : "/"; activity.DisplayName = path; - // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md - if (request.Host.HasValue) + // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/semantic_conventions/http.md + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.Old)) { - activity.SetTag(SemanticConventions.AttributeNetHostName, request.Host.Host); + if (request.Host.HasValue) + { + activity.SetTag(SemanticConventions.AttributeNetHostName, request.Host.Host); + + if (request.Host.Port is not null && request.Host.Port != 80 && request.Host.Port != 443) + { + activity.SetTag(SemanticConventions.AttributeNetHostPort, request.Host.Port); + } + } - if (request.Host.Port is not null && request.Host.Port != 80 && request.Host.Port != 443) + activity.SetTag(SemanticConventions.AttributeHttpMethod, request.Method); + activity.SetTag(SemanticConventions.AttributeHttpScheme, request.Scheme); + activity.SetTag(SemanticConventions.AttributeHttpTarget, path); + activity.SetTag(SemanticConventions.AttributeHttpUrl, GetUri(request)); + activity.SetTag(SemanticConventions.AttributeHttpFlavor, HttpTagHelper.GetFlavorTagValueFromProtocol(request.Protocol)); + + if (request.Headers.TryGetValue("User-Agent", out var values)) { - activity.SetTag(SemanticConventions.AttributeNetHostPort, request.Host.Port); + var userAgent = values.Count > 0 ? values[0] : null; + if (!string.IsNullOrEmpty(userAgent)) + { + activity.SetTag(SemanticConventions.AttributeHttpUserAgent, userAgent); + } } } - activity.SetTag(SemanticConventions.AttributeHttpMethod, request.Method); - activity.SetTag(SemanticConventions.AttributeHttpScheme, request.Scheme); - activity.SetTag(SemanticConventions.AttributeHttpTarget, path); - activity.SetTag(SemanticConventions.AttributeHttpUrl, GetUri(request)); - activity.SetTag(SemanticConventions.AttributeHttpFlavor, HttpTagHelper.GetFlavorTagValueFromProtocol(request.Protocol)); - - if (request.Headers.TryGetValue("User-Agent", out var values)) + // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/trace/semantic_conventions/http.md + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.New)) { - var userAgent = values.Count > 0 ? values[0] : null; - if (!string.IsNullOrEmpty(userAgent)) + if (request.Host.HasValue) + { + activity.SetTag(SemanticConventions.AttributeServerAddress, request.Host.Host); + + if (request.Host.Port is not null && request.Host.Port != 80 && request.Host.Port != 443) + { + activity.SetTag(SemanticConventions.AttributeServerPort, request.Host.Port); + } + } + + if (request.QueryString.HasValue) { - activity.SetTag(SemanticConventions.AttributeHttpUserAgent, userAgent); + // QueryString should be sanitized. see: https://github.com/open-telemetry/opentelemetry-dotnet/issues/4571 + activity.SetTag(SemanticConventions.AttributeUrlQuery, request.QueryString.Value); + } + + activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, request.Method); + activity.SetTag(SemanticConventions.AttributeUrlScheme, request.Scheme); + activity.SetTag(SemanticConventions.AttributeUrlPath, path); + activity.SetTag(SemanticConventions.AttributeNetworkProtocolVersion, HttpTagHelper.GetFlavorTagValueFromProtocol(request.Protocol)); + + if (request.Headers.TryGetValue("User-Agent", out var values)) + { + var userAgent = values.Count > 0 ? values[0] : null; + if (!string.IsNullOrEmpty(userAgent)) + { + activity.SetTag(SemanticConventions.AttributeUserAgentOriginal, userAgent); + } } } @@ -247,12 +284,20 @@ public void OnStopActivity(Activity activity, object payload) var response = context.Response; - activity.SetTag(SemanticConventions.AttributeHttpStatusCode, TelemetryHelper.GetBoxedStatusCode(response.StatusCode)); + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.Old)) + { + activity.SetTag(SemanticConventions.AttributeHttpStatusCode, TelemetryHelper.GetBoxedStatusCode(response.StatusCode)); + } + + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.New)) + { + activity.SetTag(SemanticConventions.AttributeHttpResponseStatusCode, TelemetryHelper.GetBoxedStatusCode(response.StatusCode)); + } #if !NETSTANDARD2_0 if (this.options.EnableGrpcAspNetCoreSupport && TryGetGrpcMethod(activity, out var grpcMethod)) { - AddGrpcAttributes(activity, grpcMethod, context); + this.AddGrpcAttributes(activity, grpcMethod, context); } else if (activity.Status == ActivityStatusCode.Unset) { @@ -429,7 +474,7 @@ private static bool TryGetGrpcMethod(Activity activity, out string grpcMethod) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddGrpcAttributes(Activity activity, string grpcMethod, HttpContext context) + private void AddGrpcAttributes(Activity activity, string grpcMethod, HttpContext context) { // The RPC semantic conventions indicate the span name // should not have a leading forward slash. @@ -439,10 +484,19 @@ private static void AddGrpcAttributes(Activity activity, string grpcMethod, Http activity.SetTag(SemanticConventions.AttributeRpcSystem, GrpcTagHelper.RpcSystemGrpc); if (context.Connection.RemoteIpAddress != null) { + // TODO: This attribute was changed in v1.13.0 https://github.com/open-telemetry/opentelemetry-specification/pull/2614 activity.SetTag(SemanticConventions.AttributeNetPeerIp, context.Connection.RemoteIpAddress.ToString()); } - activity.SetTag(SemanticConventions.AttributeNetPeerPort, context.Connection.RemotePort); + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.Old)) + { + activity.SetTag(SemanticConventions.AttributeNetPeerPort, context.Connection.RemotePort); + } + + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.New)) + { + activity.SetTag(SemanticConventions.AttributeServerPort, context.Connection.RemotePort); + } bool validConversion = GrpcTagHelper.TryGetGrpcStatusCodeFromActivity(activity, out int status); if (validConversion) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 810225daa25..17d9e77738e 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -82,20 +82,44 @@ public override void OnEventWritten(string name, object payload) TagList tags = default; - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpFlavor, HttpTagHelper.GetFlavorTagValueFromProtocol(context.Request.Protocol))); - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpScheme, context.Request.Scheme)); - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpMethod, context.Request.Method)); - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpStatusCode, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode))); + // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/semantic_conventions/http.md + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.Old)) + { + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpFlavor, HttpTagHelper.GetFlavorTagValueFromProtocol(context.Request.Protocol))); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpScheme, context.Request.Scheme)); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpMethod, context.Request.Method)); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpStatusCode, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode))); + + if (context.Request.Host.HasValue) + { + tags.Add(new KeyValuePair(SemanticConventions.AttributeNetHostName, context.Request.Host.Host)); + + if (context.Request.Host.Port is not null && context.Request.Host.Port != 80 && context.Request.Host.Port != 443) + { + tags.Add(new KeyValuePair(SemanticConventions.AttributeNetHostPort, context.Request.Host.Port)); + } + } + } - if (context.Request.Host.HasValue) + // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/trace/semantic_conventions/http.md + if (this.httpSemanticConvention.HasFlag(HttpSemanticConvention.New)) { - tags.Add(new KeyValuePair(SemanticConventions.AttributeNetHostName, context.Request.Host.Host)); + tags.Add(new KeyValuePair(SemanticConventions.AttributeNetworkProtocolVersion, HttpTagHelper.GetFlavorTagValueFromProtocol(context.Request.Protocol))); + tags.Add(new KeyValuePair(SemanticConventions.AttributeUrlScheme, context.Request.Scheme)); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, context.Request.Method)); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpResponseStatusCode, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode))); - if (context.Request.Host.Port is not null && context.Request.Host.Port != 80 && context.Request.Host.Port != 443) + if (context.Request.Host.HasValue) { - tags.Add(new KeyValuePair(SemanticConventions.AttributeNetHostPort, context.Request.Host.Port)); + tags.Add(new KeyValuePair(SemanticConventions.AttributeServerAddress, context.Request.Host.Host)); + + if (context.Request.Host.Port is not null && context.Request.Host.Port != 80 && context.Request.Host.Port != 443) + { + tags.Add(new KeyValuePair(SemanticConventions.AttributeServerPort, context.Request.Host.Port)); + } } } + #if NET6_0_OR_GREATER var route = (context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText; if (!string.IsNullOrEmpty(route)) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs index 8500ac00a22..af9e63e2677 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs @@ -43,7 +43,7 @@ public IncomingRequestsCollectionsIsAccordingToTheSpecTests(WebApplicationFactor [InlineData("/api/values", "?query=1", null, 503, null)] [InlineData("/api/exception", null, null, 503, null)] [InlineData("/api/exception", null, null, 503, null, true)] - public async Task SuccessfulTemplateControllerCallGeneratesASpan( + public async Task SuccessfulTemplateControllerCallGeneratesASpan_Old( string urlPath, string query, string userAgent, @@ -51,100 +51,109 @@ public async Task SuccessfulTemplateControllerCallGeneratesASpan( string reasonPhrase, bool recordException = false) { - var exportedItems = new List(); + try + { + Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", "none"); - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices((IServiceCollection services) => + var exportedItems = new List(); + + // Arrange + using (var client = this.factory + .WithWebHostBuilder(builder => { - services.AddSingleton(new TestCallbackMiddlewareImpl(statusCode, reasonPhrase)); - services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation(options => options.RecordException = recordException) - .AddInMemoryExporter(exportedItems)); - }); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - try + builder.ConfigureTestServices((IServiceCollection services) => + { + services.AddSingleton(new TestCallbackMiddlewareImpl(statusCode, reasonPhrase)); + services.AddOpenTelemetry() + .WithTracing(builder => builder + .AddAspNetCoreInstrumentation(options => options.RecordException = recordException) + .AddInMemoryExporter(exportedItems)); + }); + builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); + }) + .CreateClient()) { - if (!string.IsNullOrEmpty(userAgent)) + try { - client.DefaultRequestHeaders.Add("User-Agent", userAgent); + if (!string.IsNullOrEmpty(userAgent)) + { + client.DefaultRequestHeaders.Add("User-Agent", userAgent); + } + + // Act + var path = urlPath; + if (query != null) + { + path += query; + } + + using var response = await client.GetAsync(path).ConfigureAwait(false); + } + catch (Exception) + { + // ignore errors } - // Act - var path = urlPath; - if (query != null) + for (var i = 0; i < 10; i++) { - path += query; + if (exportedItems.Count == 1) + { + break; + } + + // 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)).ConfigureAwait(false); } + } + + Assert.Single(exportedItems); + var activity = exportedItems[0]; - using var response = await client.GetAsync(path).ConfigureAwait(false); + Assert.Equal(ActivityKind.Server, activity.Kind); + Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeNetHostName)); + Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeHttpMethod)); + Assert.Equal("1.1", activity.GetTagValue(SemanticConventions.AttributeHttpFlavor)); + Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeHttpScheme)); + Assert.Equal(urlPath, activity.GetTagValue(SemanticConventions.AttributeHttpTarget)); + Assert.Equal($"http://localhost{urlPath}{query}", activity.GetTagValue(SemanticConventions.AttributeHttpUrl)); + Assert.Equal(statusCode, activity.GetTagValue(SemanticConventions.AttributeHttpStatusCode)); + + if (statusCode == 503) + { + Assert.Equal(ActivityStatusCode.Error, activity.Status); } - catch (Exception) + else { - // ignore errors + Assert.Equal(ActivityStatusCode.Unset, activity.Status); } - for (var i = 0; i < 10; i++) + // Instrumentation is not expected to set status description + // as the reason can be inferred from SemanticConventions.AttributeHttpStatusCode + if (!urlPath.EndsWith("exception")) { - if (exportedItems.Count == 1) - { - break; - } - - // 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)).ConfigureAwait(false); + Assert.True(string.IsNullOrEmpty(activity.StatusDescription)); + } + else + { + Assert.Equal("exception description", activity.StatusDescription); } - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - Assert.Equal(ActivityKind.Server, activity.Kind); - Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeNetHostName)); - Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeHttpMethod)); - Assert.Equal("1.1", activity.GetTagValue(SemanticConventions.AttributeHttpFlavor)); - Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeHttpScheme)); - Assert.Equal(urlPath, activity.GetTagValue(SemanticConventions.AttributeHttpTarget)); - Assert.Equal($"http://localhost{urlPath}{query}", activity.GetTagValue(SemanticConventions.AttributeHttpUrl)); - Assert.Equal(statusCode, activity.GetTagValue(SemanticConventions.AttributeHttpStatusCode)); + if (recordException) + { + Assert.Single(activity.Events); + Assert.Equal("exception", activity.Events.First().Name); + } - if (statusCode == 503) - { - Assert.Equal(ActivityStatusCode.Error, activity.Status); - } - else - { - Assert.Equal(ActivityStatusCode.Unset, activity.Status); - } + ValidateTagValue(activity, SemanticConventions.AttributeHttpUserAgent, userAgent); - // Instrumentation is not expected to set status description - // as the reason can be inferred from SemanticConventions.AttributeHttpStatusCode - if (!urlPath.EndsWith("exception")) - { - Assert.True(string.IsNullOrEmpty(activity.StatusDescription)); + activity.Dispose(); } - else + finally { - Assert.Equal("exception description", activity.StatusDescription); + Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", null); } - - if (recordException) - { - Assert.Single(activity.Events); - Assert.Equal("exception", activity.Events.First().Name); - } - - ValidateTagValue(activity, SemanticConventions.AttributeHttpUserAgent, userAgent); - - activity.Dispose(); } private static void ValidateTagValue(Activity activity, string attribute, string expectedValue) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_Dupe.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_Dupe.cs new file mode 100644 index 00000000000..01e3cb8ce93 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_Dupe.cs @@ -0,0 +1,204 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Trace; +using TestApp.AspNetCore; +using Xunit; + +namespace OpenTelemetry.Instrumentation.AspNetCore.Tests +{ + public class IncomingRequestsCollectionsIsAccordingToTheSpecTests_Dupe + : IClassFixture> + { + private readonly WebApplicationFactory factory; + + public IncomingRequestsCollectionsIsAccordingToTheSpecTests_Dupe(WebApplicationFactory factory) + { + this.factory = factory; + } + + [Theory] + [InlineData("/api/values", null, "user-agent", 503, "503")] + [InlineData("/api/values", "?query=1", null, 503, null)] + [InlineData("/api/exception", null, null, 503, null)] + [InlineData("/api/exception", null, null, 503, null, true)] + public async Task SuccessfulTemplateControllerCallGeneratesASpan_Dupe( + string urlPath, + string query, + string userAgent, + int statusCode, + string reasonPhrase, + bool recordException = false) + { + try + { + Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", "http/dup"); + + var exportedItems = new List(); + + // Arrange + using (var client = this.factory + .WithWebHostBuilder(builder => + { + builder.ConfigureTestServices((IServiceCollection services) => + { + services.AddSingleton(new TestCallbackMiddlewareImpl(statusCode, reasonPhrase)); + services.AddOpenTelemetry() + .WithTracing(builder => builder + .AddAspNetCoreInstrumentation(options => options.RecordException = recordException) + .AddInMemoryExporter(exportedItems)); + }); + builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); + }) + .CreateClient()) + { + try + { + if (!string.IsNullOrEmpty(userAgent)) + { + client.DefaultRequestHeaders.Add("User-Agent", userAgent); + } + + // Act + var path = urlPath; + if (query != null) + { + path += query; + } + + using var response = await client.GetAsync(path).ConfigureAwait(false); + } + catch (Exception) + { + // ignore errors + } + + for (var i = 0; i < 10; i++) + { + if (exportedItems.Count == 1) + { + break; + } + + // 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)).ConfigureAwait(false); + } + } + + Assert.Single(exportedItems); + var activity = exportedItems[0]; + + Assert.Equal(ActivityKind.Server, activity.Kind); + Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeServerAddress)); + Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeNetHostName)); + Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod)); + Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeHttpMethod)); + Assert.Equal("1.1", activity.GetTagValue(SemanticConventions.AttributeNetworkProtocolVersion)); + Assert.Equal("1.1", activity.GetTagValue(SemanticConventions.AttributeHttpFlavor)); + Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeUrlScheme)); + Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeHttpScheme)); + Assert.Equal(urlPath, activity.GetTagValue(SemanticConventions.AttributeUrlPath)); + Assert.Equal(urlPath, activity.GetTagValue(SemanticConventions.AttributeHttpTarget)); + Assert.Equal($"http://localhost{urlPath}{query}", activity.GetTagValue(SemanticConventions.AttributeHttpUrl)); + Assert.Equal(query, activity.GetTagValue(SemanticConventions.AttributeUrlQuery)); + Assert.Equal(statusCode, activity.GetTagValue(SemanticConventions.AttributeHttpResponseStatusCode)); + Assert.Equal(statusCode, activity.GetTagValue(SemanticConventions.AttributeHttpStatusCode)); + + if (statusCode == 503) + { + Assert.Equal(ActivityStatusCode.Error, activity.Status); + } + else + { + Assert.Equal(ActivityStatusCode.Unset, activity.Status); + } + + // Instrumentation is not expected to set status description + // as the reason can be inferred from SemanticConventions.AttributeHttpStatusCode + if (!urlPath.EndsWith("exception")) + { + Assert.True(string.IsNullOrEmpty(activity.StatusDescription)); + } + else + { + Assert.Equal("exception description", activity.StatusDescription); + } + + if (recordException) + { + Assert.Single(activity.Events); + Assert.Equal("exception", activity.Events.First().Name); + } + + ValidateTagValue(activity, SemanticConventions.AttributeUserAgentOriginal, userAgent); + + activity.Dispose(); + } + finally + { + Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", null); + } + } + + private static void ValidateTagValue(Activity activity, string attribute, string expectedValue) + { + if (string.IsNullOrEmpty(expectedValue)) + { + Assert.Null(activity.GetTagValue(attribute)); + } + else + { + Assert.Equal(expectedValue, activity.GetTagValue(attribute)); + } + } + + public class TestCallbackMiddlewareImpl : CallbackMiddleware.CallbackMiddlewareImpl + { + private readonly int statusCode; + private readonly string reasonPhrase; + + public TestCallbackMiddlewareImpl(int statusCode, string reasonPhrase) + { + this.statusCode = statusCode; + this.reasonPhrase = reasonPhrase; + } + + public override async Task ProcessAsync(HttpContext context) + { + context.Response.StatusCode = this.statusCode; + context.Response.HttpContext.Features.Get().ReasonPhrase = this.reasonPhrase; + await context.Response.WriteAsync("empty").ConfigureAwait(false); + + if (context.Request.Path.Value.EndsWith("exception")) + { + throw new Exception("exception description"); + } + + return false; + } + } + } +} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs new file mode 100644 index 00000000000..47895a14754 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs @@ -0,0 +1,197 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Trace; +using TestApp.AspNetCore; +using Xunit; + +namespace OpenTelemetry.Instrumentation.AspNetCore.Tests +{ + public class IncomingRequestsCollectionsIsAccordingToTheSpecTests_New + : IClassFixture> + { + private readonly WebApplicationFactory factory; + + public IncomingRequestsCollectionsIsAccordingToTheSpecTests_New(WebApplicationFactory factory) + { + this.factory = factory; + } + + [Theory] + [InlineData("/api/values", null, "user-agent", 503, "503")] + [InlineData("/api/values", "?query=1", null, 503, null)] + [InlineData("/api/exception", null, null, 503, null)] + [InlineData("/api/exception", null, null, 503, null, true)] + public async Task SuccessfulTemplateControllerCallGeneratesASpan_New( + string urlPath, + string query, + string userAgent, + int statusCode, + string reasonPhrase, + bool recordException = false) + { + try + { + Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", "http"); + + var exportedItems = new List(); + + // Arrange + using (var client = this.factory + .WithWebHostBuilder(builder => + { + builder.ConfigureTestServices((IServiceCollection services) => + { + services.AddSingleton(new TestCallbackMiddlewareImpl(statusCode, reasonPhrase)); + services.AddOpenTelemetry() + .WithTracing(builder => builder + .AddAspNetCoreInstrumentation(options => options.RecordException = recordException) + .AddInMemoryExporter(exportedItems)); + }); + builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); + }) + .CreateClient()) + { + try + { + if (!string.IsNullOrEmpty(userAgent)) + { + client.DefaultRequestHeaders.Add("User-Agent", userAgent); + } + + // Act + var path = urlPath; + if (query != null) + { + path += query; + } + + using var response = await client.GetAsync(path).ConfigureAwait(false); + } + catch (Exception) + { + // ignore errors + } + + for (var i = 0; i < 10; i++) + { + if (exportedItems.Count == 1) + { + break; + } + + // 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)).ConfigureAwait(false); + } + } + + Assert.Single(exportedItems); + var activity = exportedItems[0]; + + Assert.Equal(ActivityKind.Server, activity.Kind); + Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeServerAddress)); + Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod)); + Assert.Equal("1.1", activity.GetTagValue(SemanticConventions.AttributeNetworkProtocolVersion)); + Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeUrlScheme)); + Assert.Equal(urlPath, activity.GetTagValue(SemanticConventions.AttributeUrlPath)); + Assert.Equal(query, activity.GetTagValue(SemanticConventions.AttributeUrlQuery)); + Assert.Equal(statusCode, activity.GetTagValue(SemanticConventions.AttributeHttpResponseStatusCode)); + + if (statusCode == 503) + { + Assert.Equal(ActivityStatusCode.Error, activity.Status); + } + else + { + Assert.Equal(ActivityStatusCode.Unset, activity.Status); + } + + // Instrumentation is not expected to set status description + // as the reason can be inferred from SemanticConventions.AttributeHttpStatusCode + if (!urlPath.EndsWith("exception")) + { + Assert.True(string.IsNullOrEmpty(activity.StatusDescription)); + } + else + { + Assert.Equal("exception description", activity.StatusDescription); + } + + if (recordException) + { + Assert.Single(activity.Events); + Assert.Equal("exception", activity.Events.First().Name); + } + + ValidateTagValue(activity, SemanticConventions.AttributeUserAgentOriginal, userAgent); + + activity.Dispose(); + } + finally + { + Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", null); + } + } + + private static void ValidateTagValue(Activity activity, string attribute, string expectedValue) + { + if (string.IsNullOrEmpty(expectedValue)) + { + Assert.Null(activity.GetTagValue(attribute)); + } + else + { + Assert.Equal(expectedValue, activity.GetTagValue(attribute)); + } + } + + public class TestCallbackMiddlewareImpl : CallbackMiddleware.CallbackMiddlewareImpl + { + private readonly int statusCode; + private readonly string reasonPhrase; + + public TestCallbackMiddlewareImpl(int statusCode, string reasonPhrase) + { + this.statusCode = statusCode; + this.reasonPhrase = reasonPhrase; + } + + public override async Task ProcessAsync(HttpContext context) + { + context.Response.StatusCode = this.statusCode; + context.Response.HttpContext.Features.Get().ReasonPhrase = this.reasonPhrase; + await context.Response.WriteAsync("empty").ConfigureAwait(false); + + if (context.Request.Path.Value.EndsWith("exception")) + { + throw new Exception("exception description"); + } + + return false; + } + } + } +} From de13eb631dcd6ef723106a1520a40d41d5d8dafd Mon Sep 17 00:00:00 2001 From: Timothy Mothra Date: Wed, 21 Jun 2023 12:04:39 -0700 Subject: [PATCH 5/7] update HttpSemanticConventions for Instrumentation.AspNetCore (part2) (#4606) --- src/OpenTelemetry.Api/Internal/SemanticConventions.cs | 3 +-- src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Api/Internal/SemanticConventions.cs b/src/OpenTelemetry.Api/Internal/SemanticConventions.cs index 2fb9b0022fe..17fe6c474b0 100644 --- a/src/OpenTelemetry.Api/Internal/SemanticConventions.cs +++ b/src/OpenTelemetry.Api/Internal/SemanticConventions.cs @@ -112,12 +112,11 @@ internal static class SemanticConventions public const string AttributeExceptionStacktrace = "exception.stacktrace"; // Http v1.21.0 https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/trace/semantic_conventions/http.md - public const string AttributeClientSocketPort = "client.socket.port"; // replaces: "net.peer.port" (AttributeNetPeerPort) public const string AttributeHttpRequestMethod = "http.request.method"; // replaces: "http.method" (AttributeHttpMethod) public const string AttributeHttpResponseStatusCode = "http.response.status_code"; // replaces: "http.status_code" (AttributeHttpStatusCode) public const string AttributeNetworkProtocolVersion = "network.protocol.version"; // replaces: "http.flavor" (AttributeHttpFlavor) public const string AttributeServerAddress = "server.address"; // replaces: "net.host.name" (AttributeNetHostName) - public const string AttributeServerPort = "server.port"; // replaces: "net.host.port" (AttributeNetHostPort) + public const string AttributeServerPort = "server.port"; // replaces: "net.host.port" (AttributeNetHostPort) and "net.peer.port" (AttributeNetPeerPort) public const string AttributeUrlPath = "url.path"; // replaces: "http.target" (AttributeHttpTarget) public const string AttributeUrlScheme = "url.scheme"; // replaces: "http.scheme" (AttributeHttpScheme) public const string AttributeUrlQuery = "url.query"; diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 301d158d93a..3ebe52252f3 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased * Updated [Http Semantic Conventions](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/trace/semantic_conventions/http.md). - * This library can emit either old, new, or both attributes. Users can control + This library can emit either old, new, or both attributes. Users can control which attributes are emitted by setting the environment variable `OTEL_SEMCONV_STABILITY_OPT_IN`. From 938bc5c7949c2f016a302864d40cbef7fd184034 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 21 Jun 2023 15:28:10 -0700 Subject: [PATCH 6/7] Add LogRecordExportProcessorOptions for otlp log exporter (#4575) --- .../.publicApi/net462/PublicAPI.Unshipped.txt | 2 +- .../.publicApi/net6.0/PublicAPI.Unshipped.txt | 2 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 2 +- .../netstandard2.1/PublicAPI.Unshipped.txt | 2 +- .../.publicApi/net462/PublicAPI.Unshipped.txt | 8 ++- .../.publicApi/net6.0/PublicAPI.Unshipped.txt | 8 ++- .../netstandard2.0/PublicAPI.Unshipped.txt | 8 ++- .../netstandard2.1/PublicAPI.Unshipped.txt | 8 ++- ...viderBuilderServiceCollectionExtensions.cs | 11 ++++ .../Logs/LogRecordExportProcessorOptions.cs | 53 +++++++++++++++++++ 10 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 src/OpenTelemetry/Logs/LogRecordExportProcessorOptions.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net462/PublicAPI.Unshipped.txt index e3204a2b2b9..07a63e57297 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net462/PublicAPI.Unshipped.txt @@ -1,3 +1,3 @@ OpenTelemetry.Logs.OtlpLogExporterHelperExtensions static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions -static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions \ No newline at end of file +static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net6.0/PublicAPI.Unshipped.txt index e3204a2b2b9..07a63e57297 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -1,3 +1,3 @@ OpenTelemetry.Logs.OtlpLogExporterHelperExtensions static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions -static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions \ No newline at end of file +static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index e3204a2b2b9..07a63e57297 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,3 +1,3 @@ OpenTelemetry.Logs.OtlpLogExporterHelperExtensions static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions -static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions \ No newline at end of file +static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt index e3204a2b2b9..07a63e57297 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -1,3 +1,3 @@ OpenTelemetry.Logs.OtlpLogExporterHelperExtensions static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions -static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions \ No newline at end of file +static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions loggerOptions, System.Action configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt index 2846b0ff9ea..e75f84d65db 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt @@ -7,6 +7,12 @@ OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverit OpenTelemetry.Logs.LogRecord.Severity.set -> void OpenTelemetry.Logs.LogRecord.SeverityText.get -> string? OpenTelemetry.Logs.LogRecord.SeverityText.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.get -> OpenTelemetry.Logs.BatchExportLogRecordProcessorOptions! +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.LogRecordExportProcessorOptions() -> void OpenTelemetry.Metrics.AlwaysOffExemplarFilter OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void OpenTelemetry.Metrics.AlwaysOnExemplarFilter @@ -41,4 +47,4 @@ static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.Log ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool -~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool \ No newline at end of file +~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt index 2846b0ff9ea..e75f84d65db 100644 --- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -7,6 +7,12 @@ OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverit OpenTelemetry.Logs.LogRecord.Severity.set -> void OpenTelemetry.Logs.LogRecord.SeverityText.get -> string? OpenTelemetry.Logs.LogRecord.SeverityText.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.get -> OpenTelemetry.Logs.BatchExportLogRecordProcessorOptions! +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.LogRecordExportProcessorOptions() -> void OpenTelemetry.Metrics.AlwaysOffExemplarFilter OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void OpenTelemetry.Metrics.AlwaysOnExemplarFilter @@ -41,4 +47,4 @@ static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.Log ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool -~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool \ No newline at end of file +~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 2846b0ff9ea..e75f84d65db 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -7,6 +7,12 @@ OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverit OpenTelemetry.Logs.LogRecord.Severity.set -> void OpenTelemetry.Logs.LogRecord.SeverityText.get -> string? OpenTelemetry.Logs.LogRecord.SeverityText.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.get -> OpenTelemetry.Logs.BatchExportLogRecordProcessorOptions! +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.LogRecordExportProcessorOptions() -> void OpenTelemetry.Metrics.AlwaysOffExemplarFilter OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void OpenTelemetry.Metrics.AlwaysOnExemplarFilter @@ -41,4 +47,4 @@ static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.Log ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool -~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool \ No newline at end of file +~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool diff --git a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt index 2846b0ff9ea..e75f84d65db 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -7,6 +7,12 @@ OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverit OpenTelemetry.Logs.LogRecord.Severity.set -> void OpenTelemetry.Logs.LogRecord.SeverityText.get -> string? OpenTelemetry.Logs.LogRecord.SeverityText.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.get -> OpenTelemetry.Logs.BatchExportLogRecordProcessorOptions! +OpenTelemetry.Logs.LogRecordExportProcessorOptions.BatchExportProcessorOptions.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType +OpenTelemetry.Logs.LogRecordExportProcessorOptions.ExportProcessorType.set -> void +OpenTelemetry.Logs.LogRecordExportProcessorOptions.LogRecordExportProcessorOptions() -> void OpenTelemetry.Metrics.AlwaysOffExemplarFilter OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void OpenTelemetry.Metrics.AlwaysOnExemplarFilter @@ -41,4 +47,4 @@ static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.Log ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool ~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(double value, System.ReadOnlySpan> tags) -> bool -~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool \ No newline at end of file +~override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan> tags) -> bool diff --git a/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs index 2325bf3bec4..3f9c379fd85 100644 --- a/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs +++ b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs @@ -19,6 +19,7 @@ using System.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; using OpenTelemetry; using OpenTelemetry.Internal; using OpenTelemetry.Logs; @@ -36,6 +37,16 @@ public static IServiceCollection AddOpenTelemetryLoggerProviderBuilderServices(t services!.TryAddSingleton(); services!.RegisterOptionsFactory(configuration => new BatchExportLogRecordProcessorOptions(configuration)); + // Note: This registers a factory so that when + // sp.GetRequiredService>().Get(name))) + // is executed the SDK internal + // BatchExportLogRecordProcessorOptions(IConfiguration) ctor is used + // correctly which allows users to control the OTEL_BLRP_* keys using + // IConfiguration (envvars, appSettings, cli, etc.). + services!.RegisterOptionsFactory( + (sp, configuration, name) => new LogRecordExportProcessorOptions( + sp.GetRequiredService>().Get(name))); + return services!; } diff --git a/src/OpenTelemetry/Logs/LogRecordExportProcessorOptions.cs b/src/OpenTelemetry/Logs/LogRecordExportProcessorOptions.cs new file mode 100644 index 00000000000..dc07148c152 --- /dev/null +++ b/src/OpenTelemetry/Logs/LogRecordExportProcessorOptions.cs @@ -0,0 +1,53 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#nullable enable + +using System.Diagnostics; + +namespace OpenTelemetry.Logs; + +/// +/// Options for configuring either a or . +/// +public class LogRecordExportProcessorOptions +{ + /// + /// Initializes a new instance of the class. + /// + public LogRecordExportProcessorOptions() + : this(new()) + { + } + + internal LogRecordExportProcessorOptions( + BatchExportLogRecordProcessorOptions defaultBatchExportLogRecordProcessorOptions) + { + Debug.Assert(defaultBatchExportLogRecordProcessorOptions != null, "defaultBatchExportLogRecordProcessorOptions was null"); + + this.BatchExportProcessorOptions = defaultBatchExportLogRecordProcessorOptions ?? new(); + } + + /// + /// Gets or sets the export processor type to be used. The default value is . + /// + public ExportProcessorType ExportProcessorType { get; set; } = ExportProcessorType.Batch; + + /// + /// Gets or sets the batch export options. Ignored unless is . + /// + public BatchExportLogRecordProcessorOptions BatchExportProcessorOptions { get; set; } +} From f5e213c0efdcd13361d1a92b14866784000bef0e Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 21 Jun 2023 15:31:51 -0700 Subject: [PATCH 7/7] Otlp exporter doc update (#4607) --- .../README.md | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md index 414034e386c..d5f213602a8 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md @@ -46,8 +46,15 @@ var meterProvider = Sdk.CreateMeterProviderBuilder() .Build(); ``` -See the [`TestMetrics.cs`](../../examples/Console/TestMetrics.cs) for -runnable example. +By default, `AddOtlpExporter()` pairs the OTLP MetricExporter with a +[PeriodicExportingMetricReader](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#periodic-exporting-metricreader) +with metric export interval of 60 secs and +[Temporality](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#temporality) +set as `Cumulative`. See +[`TestMetrics.cs`](../../examples/Console/TestMetrics.cs) for example on how to +customize the `MetricReaderOptions` or see the [Environment +Variables](#environment-variables) section below on how to customize using +environment variables. ## Enable Log Exporter @@ -122,10 +129,12 @@ The following environment variables can be used to override the default values of the `PeriodicExportingMetricReaderOptions` (following the [OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.12.0/specification/sdk-environment-variables.md#periodic-exporting-metricreader). -| Environment variable | `PeriodicExportingMetricReaderOptions` property | -| ------------------------------| ------------------------------------------------| -| `OTEL_METRIC_EXPORT_INTERVAL` | `ExportIntervalMilliseconds` | -| `OTEL_METRIC_EXPORT_TIMEOUT` | `ExportTimeoutMilliseconds` | +| Environment variable | `PeriodicExportingMetricReaderOptions` property | +| ----------------------------------------------------| ------------------------------------------------| +| `OTEL_METRIC_EXPORT_INTERVAL` | `ExportIntervalMilliseconds` | +| `OTEL_METRIC_EXPORT_TIMEOUT` | `ExportTimeoutMilliseconds` | + +NOTE: `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` is not supported yet [#3756](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3756). The following environment variables can be used to override the default values of the attribute limits