178 lines
7.2 KiB
C#
178 lines
7.2 KiB
C#
// <copyright file="InstrumentationWithActivitySource.cs" company="OpenTelemetry Authors">
|
|
// 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.
|
|
// </copyright>
|
|
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Text;
|
|
|
|
namespace Examples.Console
|
|
{
|
|
internal class InstrumentationWithActivitySource : IDisposable
|
|
{
|
|
private const string RequestPath = "/api/request";
|
|
private readonly SampleServer server = new();
|
|
private readonly SampleClient client = new();
|
|
|
|
public void Start(ushort port = 19999)
|
|
{
|
|
var url = $"http://localhost:{port.ToString(CultureInfo.InvariantCulture)}{RequestPath}/";
|
|
this.server.Start(url);
|
|
this.client.Start(url);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
this.client.Dispose();
|
|
this.server.Dispose();
|
|
}
|
|
|
|
private class SampleServer : IDisposable
|
|
{
|
|
private readonly HttpListener listener = new();
|
|
|
|
public void Start(string url)
|
|
{
|
|
this.listener.Prefixes.Add(url);
|
|
this.listener.Start();
|
|
|
|
Task.Run(() =>
|
|
{
|
|
using var source = new ActivitySource("Samples.SampleServer");
|
|
|
|
while (this.listener.IsListening)
|
|
{
|
|
try
|
|
{
|
|
var context = this.listener.GetContext();
|
|
|
|
using var activity = source.StartActivity(
|
|
$"{context.Request.HttpMethod}:{context.Request.Url.AbsolutePath}",
|
|
ActivityKind.Server);
|
|
|
|
var headerKeys = context.Request.Headers.AllKeys;
|
|
foreach (var headerKey in headerKeys)
|
|
{
|
|
string headerValue = context.Request.Headers[headerKey];
|
|
activity?.SetTag($"http.header.{headerKey}", headerValue);
|
|
}
|
|
|
|
string requestContent;
|
|
using (var childSpan = source.StartActivity("ReadStream", ActivityKind.Consumer))
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
{
|
|
requestContent = reader.ReadToEnd();
|
|
childSpan.AddEvent(new ActivityEvent("StreamReader.ReadToEnd"));
|
|
}
|
|
|
|
activity?.SetTag("request.content", requestContent);
|
|
activity?.SetTag("request.length", requestContent.Length.ToString());
|
|
|
|
var echo = Encoding.UTF8.GetBytes("echo: " + requestContent);
|
|
context.Response.ContentEncoding = Encoding.UTF8;
|
|
context.Response.ContentLength64 = echo.Length;
|
|
context.Response.OutputStream.Write(echo, 0, echo.Length);
|
|
context.Response.Close();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// expected when closing the listener.
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
((IDisposable)this.listener).Dispose();
|
|
}
|
|
}
|
|
|
|
private class SampleClient : IDisposable
|
|
{
|
|
private CancellationTokenSource cts;
|
|
private Task requestTask;
|
|
|
|
public void Start(string url)
|
|
{
|
|
this.cts = new CancellationTokenSource();
|
|
var cancellationToken = this.cts.Token;
|
|
|
|
this.requestTask = Task.Run(
|
|
async () =>
|
|
{
|
|
using var source = new ActivitySource("Samples.SampleClient");
|
|
using var client = new HttpClient();
|
|
|
|
var count = 1;
|
|
while (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
var content = new StringContent($"client message: {DateTime.Now}", Encoding.UTF8);
|
|
|
|
using (var activity = source.StartActivity("POST:" + RequestPath, ActivityKind.Client))
|
|
{
|
|
count++;
|
|
|
|
activity?.AddEvent(new ActivityEvent("PostAsync:Started"));
|
|
using var response = await client.PostAsync(url, content, cancellationToken).ConfigureAwait(false);
|
|
activity?.AddEvent(new ActivityEvent("PostAsync:Ended"));
|
|
|
|
activity?.SetTag("http.status_code", (int)response.StatusCode);
|
|
|
|
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
|
activity?.SetTag("response.content", responseContent);
|
|
activity?.SetTag("response.length", responseContent.Length.ToString(CultureInfo.InvariantCulture));
|
|
|
|
foreach (var header in response.Headers)
|
|
{
|
|
if (header.Value is IEnumerable<object> enumerable)
|
|
{
|
|
activity?.SetTag($"http.header.{header.Key}", string.Join(",", enumerable));
|
|
}
|
|
else
|
|
{
|
|
activity?.SetTag($"http.header.{header.Key}", header.Value.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
cancellationToken);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (this.cts != null)
|
|
{
|
|
this.cts.Cancel();
|
|
this.requestTask.Wait();
|
|
this.requestTask.Dispose();
|
|
this.cts.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|