// // 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.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 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(); } } } } }