opentelemetry-dotnet/benchmarks/Exporter/ZipkinExporterBenchmarks.cs

233 lines
7.5 KiB
C#

// <copyright file="ZipkinExporterBenchmarks.cs" company="OpenTelemetry Authors">
// Copyright 2018, 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.
// </copyright>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using OpenTelemetry.Exporter.Zipkin;
using OpenTelemetry.Trace;
using OpenTelemetry.Trace.Export;
namespace Benchmarks.Exporter
{
[MemoryDiagnoser]
#if !NET462
[ThreadingDiagnoser]
#endif
public class ZipkinExporterBenchmarks
{
[Params(2000, 5000)]
public int NumberOfSpans { get; set; }
private SpanData testSpan;
private IDisposable server;
private string serverHost;
private int serverPort;
[GlobalSetup]
public void GlobalSetup()
{
this.testSpan = this.CreateTestSpan();
this.server = TestServer.RunServer(
(ctx) =>
{
ctx.Response.StatusCode = 200;
ctx.Response.OutputStream.Close();
},
out this.serverHost,
out this.serverPort);
}
[GlobalCleanup]
public void GlobalCleanup()
{
this.server.Dispose();
}
[Benchmark]
public async Task ZipkinExporter_ExportAsync()
{
var zipkinExporter = new ZipkinTraceExporter(
new ZipkinTraceExporterOptions
{
Endpoint = new Uri($"http://{this.serverHost}:{this.serverPort}"),
});
var spans = new List<SpanData>();
for (int i = 0; i < this.NumberOfSpans; i++)
{
spans.Add(this.testSpan);
}
await zipkinExporter.ExportAsync(spans, CancellationToken.None).ConfigureAwait(false);
}
private SpanData CreateTestSpan()
{
var startTimestamp = new DateTimeOffset(2019, 1, 1, 0, 0, 0, TimeSpan.Zero);
var endTimestamp = startTimestamp.AddSeconds(60);
var eventTimestamp = new DateTimeOffset(2019, 1, 1, 0, 0, 0, TimeSpan.Zero);
var traceId = ActivityTraceId.CreateFromString("e8ea7e9ac72de94e91fabc613f9686b2".AsSpan());
var spanId = ActivitySpanId.CreateFromString("6a69db47429ea340".AsSpan());
var parentSpanId = ActivitySpanId.CreateFromBytes(new byte[] { 12, 23, 34, 45, 56, 67, 78, 89 });
var attributes = new Dictionary<string, object>
{
{ "stringKey", "value"},
{ "longKey", 1L},
{ "longKey2", 1 },
{ "doubleKey", 1D},
{ "doubleKey2", 1F},
{ "boolKey", true},
};
var events = new List<Event>
{
new Event(
"Event1",
eventTimestamp,
new Dictionary<string, object>
{
{ "key", "value" },
}
),
new Event(
"Event2",
eventTimestamp,
new Dictionary<string, object>
{
{ "key", "value" },
}
),
};
var linkedSpanId = ActivitySpanId.CreateFromString("888915b6286b9c41".AsSpan());
var link = new Link(new SpanContext(
traceId,
linkedSpanId,
ActivityTraceFlags.Recorded));
return new SpanData(
"Name",
new SpanContext(traceId, spanId, ActivityTraceFlags.Recorded),
parentSpanId,
SpanKind.Client,
startTimestamp,
attributes,
events,
new[] { link, },
null,
Status.Ok,
endTimestamp);
}
public class TestServer
{
private static readonly Random GlobalRandom = new Random();
private class RunningServer : IDisposable
{
private readonly Task httpListenerTask;
private readonly HttpListener listener;
private readonly CancellationTokenSource cts;
private readonly AutoResetEvent initialized = new AutoResetEvent(false);
public RunningServer(Action<HttpListenerContext> action, string host, int port)
{
this.cts = new CancellationTokenSource();
this.listener = new HttpListener();
var token = this.cts.Token;
this.listener.Prefixes.Add($"http://{host}:{port}/");
this.listener.Start();
this.httpListenerTask = new Task(() =>
{
while (!token.IsCancellationRequested)
{
var ctxTask = this.listener.GetContextAsync();
this.initialized.Set();
try
{
ctxTask.Wait(token);
if (ctxTask.Status == TaskStatus.RanToCompletion)
{
action(ctxTask.Result);
}
}
catch (OperationCanceledException)
{
}
}
});
}
public void Start()
{
this.httpListenerTask.Start();
this.initialized.WaitOne();
}
public void Dispose()
{
try
{
this.listener?.Stop();
this.cts.Cancel();
}
catch (ObjectDisposedException)
{
// swallow this exception just in case
}
}
}
public static IDisposable RunServer(Action<HttpListenerContext> action, out string host, out int port)
{
host = "localhost";
port = 0;
RunningServer server = null;
var retryCount = 5;
while (retryCount > 0)
{
try
{
port = GlobalRandom.Next(2000, 5000);
server = new RunningServer(action, host, port);
server.Start();
break;
}
catch (HttpListenerException)
{
retryCount--;
}
}
return server;
}
}
}
}