From adb94a48f124d8e9ceb84fe10a04e22b93eacb22 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 16 Mar 2023 11:01:37 -0700 Subject: [PATCH 1/3] Add OneCollector benchmark project. --- opentelemetry-dotnet-contrib.sln | 7 + ...ecordCommonSchemaJsonHttpPostBenchmarks.cs | 194 ++++++++++++++++++ ...ry.Exporter.OneCollector.Benchmarks.csproj | 23 +++ .../Program.cs | 46 +++++ 4 files changed, 270 insertions(+) create mode 100644 test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs create mode 100644 test/OpenTelemetry.Exporter.OneCollector.Benchmarks/OpenTelemetry.Exporter.OneCollector.Benchmarks.csproj create mode 100644 test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index 7996cef694..0cd96af5e0 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -253,6 +253,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.PersistentSto EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.PersistentStorage.FileSystem.Tests", "test\OpenTelemetry.PersistentStorage.FileSystem.Tests\OpenTelemetry.PersistentStorage.FileSystem.Tests.csproj", "{C7215B69-0D77-4D52-AFBB-A6662249B3AC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.OneCollector.Benchmarks", "test\OpenTelemetry.Exporter.OneCollector.Benchmarks\OpenTelemetry.Exporter.OneCollector.Benchmarks.csproj", "{C42868C8-968A-473F-AC39-AC97C5D47E84}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -539,6 +541,10 @@ Global {C7215B69-0D77-4D52-AFBB-A6662249B3AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7215B69-0D77-4D52-AFBB-A6662249B3AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {C7215B69-0D77-4D52-AFBB-A6662249B3AC}.Release|Any CPU.Build.0 = Release|Any CPU + {C42868C8-968A-473F-AC39-AC97C5D47E84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C42868C8-968A-473F-AC39-AC97C5D47E84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C42868C8-968A-473F-AC39-AC97C5D47E84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C42868C8-968A-473F-AC39-AC97C5D47E84}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -622,6 +628,7 @@ Global {EC73B88E-D544-4A6A-90D5-E291035FEA35} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {74EF2C61-9176-4D4E-91D3-E17E28054FF8} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {C7215B69-0D77-4D52-AFBB-A6662249B3AC} = {2097345F-4DD3-477D-BC54-A922F9B2B402} + {C42868C8-968A-473F-AC39-AC97C5D47E84} = {2097345F-4DD3-477D-BC54-A922F9B2B402} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs new file mode 100644 index 0000000000..1fc6e46c24 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs @@ -0,0 +1,194 @@ +// +// 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; +using System.Reflection; +using BenchmarkDotNet.Attributes; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; + +namespace OpenTelemetry.Exporter.OneCollector.Benchmarks; + +[MemoryDiagnoser] +public class LogRecordCommonSchemaJsonHttpPostBenchmarks +{ + private static readonly MethodInfo LogRecordSetScopeProviderMethodInfo = typeof(LogRecord).GetProperty("ScopeProvider", BindingFlags.Instance | BindingFlags.NonPublic)?.SetMethod + ?? throw new InvalidOperationException("LogRecord.ScopeProvider.Set could not be found reflectively."); + + private LogRecord[]? logRecords; + private OneCollectorExporter? exporter; + + [Params(1, 10, 100)] + public int NumberOfBatches { get; set; } + + [Params(1000, 10_000)] + public int NumberOfLogRecordsPerBatch { get; set; } + + [Params(true, false)] + public bool EnableCompression { get; set; } + + [GlobalSetup] + public void GlobalSetup() + { + this.logRecords = new LogRecord[this.NumberOfLogRecordsPerBatch]; + + for (int i = 0; i < this.NumberOfLogRecordsPerBatch; i++) + { + this.logRecords[i] = CreateLogRecord(i); + } + + var exporterOptions = new OneCollectorLogExporterOptions + { + ConnectionString = "InstrumentationKey=token-extrainformation", + }; + + exporterOptions.Validate(); + + var transportOptions = exporterOptions.TransportOptions; + + transportOptions.HttpCompression = this.EnableCompression + ? OneCollectorExporterHttpTransportCompressionType.Deflate + : OneCollectorExporterHttpTransportCompressionType.None; + + var sink = new WriteDirectlyToTransportSink( + new LogRecordCommonSchemaJsonSerializer( + new EventNameManager(exporterOptions.DefaultEventNamespace, exporterOptions.DefaultEventName), + exporterOptions.TenantToken!, + exporterOptions.SerializationOptions.ExceptionStackTraceHandling, + transportOptions.MaxPayloadSizeInBytes == -1 ? int.MaxValue : transportOptions.MaxPayloadSizeInBytes, + transportOptions.MaxNumberOfItemsPerPayload == -1 ? int.MaxValue : transportOptions.MaxNumberOfItemsPerPayload), + new HttpJsonPostTransport( + exporterOptions.InstrumentationKey!, + transportOptions.Endpoint, + transportOptions.HttpCompression, + new NoopHttpClient())); + + this.exporter = new OneCollectorExporter(sink); + } + + [GlobalCleanup] + public void GlobalCleanup() + { + this.exporter?.Dispose(); + } + + [Benchmark] + public void Export() + { + for (int i = 0; i < this.NumberOfBatches; i++) + { + this.exporter!.Export(new Batch(this.logRecords!, this.logRecords!.Length)); + } + } + + private static LogRecord CreateLogRecord(int index) + { + var logRecord = (LogRecord)Activator.CreateInstance(typeof(LogRecord), nonPublic: true)!; + + logRecord.Timestamp = DateTime.UtcNow; + logRecord.CategoryName = typeof(LogRecordCommonSchemaJsonHttpPostBenchmarks).FullName; + logRecord.LogLevel = LogLevel.Information; + + if (index % 2 == 0) + { + if (index % 4 == 0) + { + logRecord.EventId = new EventId(2, "MyEvent"); + } + else + { + logRecord.EventId = new EventId(1); + } + + logRecord.StateValues = new List> + { + new KeyValuePair("userId", 18), + new KeyValuePair("greeting", "hello world"), + new KeyValuePair("{OriginalFormat}", "Structured logging {userId} {greeting}"), + }; + + if (index % 3 == 0) + { + var scopeProvider = new ScopeProvider( + new List> { new KeyValuePair("scope1Key1", "scope1Value1"), new KeyValuePair("scope1Key2", "scope1Value2") }, + new List> { new KeyValuePair("scope2Key1", "scope2Value1") }); + + LogRecordSetScopeProviderMethodInfo.Invoke(logRecord, new object[] { scopeProvider }); + } + } + else + { + logRecord.FormattedMessage = "Non-structured log message"; + } + + if (index % 3 == 0) + { + logRecord.TraceId = ActivityTraceId.CreateRandom(); + logRecord.SpanId = ActivitySpanId.CreateRandom(); + + if (index % 6 == 0) + { + logRecord.TraceFlags = ActivityTraceFlags.None; + } + else + { + logRecord.TraceFlags = ActivityTraceFlags.Recorded; + } + } + + if (index % 9 == 0) + { + logRecord.Exception = new InvalidOperationException(); + } + + return logRecord; + } + + private sealed class NoopHttpClient : IHttpClient + { + public HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + { + return new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + }; + } + } + + private sealed class ScopeProvider : IExternalScopeProvider + { + private readonly List>[] scopes; + + public ScopeProvider(params List>[] scopes) + { + this.scopes = scopes; + } + + public void ForEachScope(Action callback, TState state) + { + foreach (var scope in this.scopes) + { + callback(scope, state); + } + } + + public IDisposable Push(object state) + { + throw new NotImplementedException(); + } + } +} diff --git a/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/OpenTelemetry.Exporter.OneCollector.Benchmarks.csproj b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/OpenTelemetry.Exporter.OneCollector.Benchmarks.csproj new file mode 100644 index 0000000000..8e257e1321 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/OpenTelemetry.Exporter.OneCollector.Benchmarks.csproj @@ -0,0 +1,23 @@ + + + + Exe + Benchmark project for OpenTelemetry .NET OneCollectorExporter. + + net7.0;net6.0 + $(TargetFrameworks);net48;net472;net471;net47;net462 + enable + true + true + + + + + + + + + + + + diff --git a/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs new file mode 100644 index 0000000000..7eba51c3c4 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs @@ -0,0 +1,46 @@ +// +// 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 BenchmarkDotNet.Running; + +namespace OpenTelemetry.Exporter.OneCollector.Benchmarks; + +internal static class Program +{ + public static void Main(string[] args) + { + if (Debugger.IsAttached) + { + var benchmarks = new LogRecordCommonSchemaJsonHttpPostBenchmarks + { + NumberOfBatches = 10_000, + NumberOfLogRecordsPerBatch = 1000, + EnableCompression = true, + }; + + benchmarks.GlobalSetup(); + + benchmarks.Export(); + + benchmarks.GlobalCleanup(); + } + else + { + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + } + } +} From b1f65a17b05cc73c9eb51777e5e022791b6568bf Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 16 Mar 2023 11:20:19 -0700 Subject: [PATCH 2/3] Update component_owners for new project. --- .github/component_owners.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/component_owners.yml b/.github/component_owners.yml index e851415414..b56276d2b2 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -91,6 +91,9 @@ components: - Yun-Ting test/OpenTelemetry.Exporter.Instana.Tests/: - zivaninstana + test/OpenTelemetry.Exporter.OneCollector.Benchmarks/: + - codeblanch + - reyang test/OpenTelemetry.Exporter.OneCollector.Tests/: - codeblanch - reyang From ece616111585530a8a8f02c4455236eedbc73a94 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 16 Mar 2023 12:49:34 -0700 Subject: [PATCH 3/3] Added note/comment. --- test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs index 7eba51c3c4..85eef601f1 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/Program.cs @@ -25,6 +25,9 @@ public static void Main(string[] args) { if (Debugger.IsAttached) { + // Note: This block is so you can start the project with debugger + // attached and step through the code. It is helpful when working on + // it. var benchmarks = new LogRecordCommonSchemaJsonHttpPostBenchmarks { NumberOfBatches = 10_000,