From 9576c15890c69dbd5cb9e706a90304c914bce872 Mon Sep 17 00:00:00 2001 From: Denis Ivanov Date: Thu, 5 Nov 2020 17:55:17 +0100 Subject: [PATCH 01/14] Initial implementation of OpenTelemetry.Extensions.Owin --- .../AppBuilderExtensions.cs | 33 ++++ .../AssemblyInfo.cs | 22 +++ .../CHANGELOG.md | 5 + .../DiagnosticsMiddleware.cs | 158 ++++++++++++++++++ .../Implementation/ActivityExtensions.cs | 122 ++++++++++++++ .../Implementation/Context.cs | 27 +++ .../HeaderDictionaryExtensions.cs | 99 +++++++++++ .../Implementation/HeaderNames.cs | 27 +++ .../OwinExtensionsEventSource.cs | 49 ++++++ .../OpenTelemetry.Extensions.Owin.csproj | 18 ++ src/OpenTelemetry.Extensions.Owin/README.md | 27 +++ .../AssemblyInfo.cs | 22 +++ .../CHANGELOG.md | 5 + .../Implementation/HttpInListener.cs | 34 ++++ .../OwinInstrumentationEventSource.cs | 29 ++++ .../OpenTelemetry.Instrumentation.Owin.csproj | 21 +++ .../OwinInstrumentation.cs | 47 ++++++ .../OwinInstrumentationOptions.cs | 25 +++ .../README.md | 27 +++ .../TracerProviderBuilderExtensions.cs | 50 ++++++ 20 files changed, 847 insertions(+) create mode 100644 src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/AssemblyInfo.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/CHANGELOG.md create mode 100644 src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs create mode 100644 src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj create mode 100644 src/OpenTelemetry.Extensions.Owin/README.md create mode 100644 src/OpenTelemetry.Instrumentation.Owin/AssemblyInfo.cs create mode 100644 src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md create mode 100644 src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.cs create mode 100644 src/OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs create mode 100644 src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj create mode 100644 src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.cs create mode 100644 src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs create mode 100644 src/OpenTelemetry.Instrumentation.Owin/README.md create mode 100644 src/OpenTelemetry.Instrumentation.Owin/TracerProviderBuilderExtensions.cs diff --git a/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs b/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs new file mode 100644 index 0000000000..1931119f8c --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs @@ -0,0 +1,33 @@ +// +// 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 Owin; + +namespace OpenTelemetry +{ + /// + /// Provides extension methods for the class. + /// + public static class AppBuilderExtensions + { + /// Adds a component to the OWIN pipeline for instrumenting incoming request with System.Diagnostics.Activity and notifying listeners with DiagnosticsSource. + /// The application builder. + /// The application builder. + public static void UseOpenTelemetry(this IAppBuilder appBuilder) + => appBuilder.Use(); + + } +} diff --git a/src/OpenTelemetry.Extensions.Owin/AssemblyInfo.cs b/src/OpenTelemetry.Extensions.Owin/AssemblyInfo.cs new file mode 100644 index 0000000000..63a1b38ade --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/AssemblyInfo.cs @@ -0,0 +1,22 @@ +// +// 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.Extensions.Owin.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] +#else +[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Owin.Tests")] +#endif diff --git a/src/OpenTelemetry.Extensions.Owin/CHANGELOG.md b/src/OpenTelemetry.Extensions.Owin/CHANGELOG.md new file mode 100644 index 0000000000..134621e04d --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## Unreleased + +* Initial release diff --git a/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs new file mode 100644 index 0000000000..88e280cefa --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs @@ -0,0 +1,158 @@ +// +// 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; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Microsoft.Owin; +using OpenTelemetry.Implementation; + +namespace OpenTelemetry +{ + /// + /// Instruments incoming request with System.Diagnostics.Activity and notifies listeners with DiagnosticsSource. + /// + public sealed class DiagnosticsMiddleware : OwinMiddleware + { + private const string ActivityName = "OpenTelemetry.Extensions.Owin.HttpRequestIn"; + private const string ActivityStartKey = ActivityName + ".Start"; + private const string ActivityStopKey = ActivityName + ".Stop"; + + private readonly DiagnosticListener diagnosticListener = new DiagnosticListener("OpenTelemetry.Extensions.Owin"); + private readonly Context context = new Context(); + + /// + /// Initializes a new instance of the class. + /// + /// An optional pointer to the next component + public DiagnosticsMiddleware(OwinMiddleware next) + : base(next) + { + } + + /// + public override async Task Invoke(IOwinContext owinContext) + { + try + { + this.BeginRequest(owinContext); + await this.Next.Invoke(owinContext).ConfigureAwait(false); + this.RequestEnd(owinContext, null); + } + catch (Exception ex) + { + this.RequestEnd(owinContext, ex); + } + } + + // Based on https://github.com/dotnet/aspnetcore/blob/v5.0.0-rc.2.20475.17/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L37 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void BeginRequest(IOwinContext owinContext) + { + if (OwinExtensionsEventSource.Log.IsEnabled()) + { + this.context.EventLogEnabled = true; + } + + if (this.diagnosticListener.IsEnabled() && this.diagnosticListener.IsEnabled(ActivityName, owinContext)) + { + this.context.Activity = this.StartActivity(owinContext, out var hasDiagnosticListener); + this.context.HasDiagnosticListener = hasDiagnosticListener; + } + } + + // Based on https://github.com/dotnet/aspnetcore/blob/v5.0.0-rc.2.20475.17/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L89 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RequestEnd(IOwinContext owinContext, Exception exception) + { + var activity = this.context.Activity; + // Always stop activity if it was started + if (activity != null) + { + this.StopActivity(owinContext, activity, this.context.HasDiagnosticListener); + } + + if (this.context.EventLogEnabled && exception != null) + { + // Non-inline + OwinExtensionsEventSource.Log.UnhandledException(); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private Activity StartActivity(IOwinContext owinContext, out bool hasDiagnosticListener) + { + hasDiagnosticListener = false; + + var activity = new Activity(ActivityName); + + // Based on https://github.com/microsoft/ApplicationInsights-dotnet/blob/2.15.0/WEB/Src/Web/Web/Implementation/RequestTrackingExtensions.cs#L41 + if (!activity.Extract(owinContext.Request.Headers)) + { + // Force parsing Correlation-Context in absence of Request-Id or traceparent. + owinContext.Request.Headers.ReadActivityBaggage(activity); + } + + this.diagnosticListener.OnActivityImport(activity, owinContext); + + if (this.diagnosticListener.IsEnabled(ActivityStartKey)) + { + hasDiagnosticListener = true; + this.StartActivity(activity, owinContext); + } + else + { + activity.Start(); + } + + return activity; + } + + // These are versions of DiagnosticSource.Start/StopActivity that don't allocate strings per call (see https://github.com/dotnet/corefx/issues/37055) + private void StartActivity(Activity activity, IOwinContext owinContext) + { + activity.Start(); + this.diagnosticListener.Write(ActivityStartKey, owinContext); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void StopActivity(IOwinContext owinContext, Activity activity, bool hasDiagnosticListener) + { + if (hasDiagnosticListener) + { + this.StopActivity(activity, owinContext); + } + else + { + activity.Stop(); + } + } + + private void StopActivity(Activity activity, IOwinContext owinContext) + { + // Stop sets the end time if it was unset, but we want it set before we issue the write + // so we do it now. + if (activity.Duration == TimeSpan.Zero) + { + activity.SetEndTime(DateTime.UtcNow); + } + + this.diagnosticListener.Write(ActivityStopKey, owinContext); + activity.Stop(); // Resets Activity.Current (we want this after the Write) + } + } +} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs new file mode 100644 index 0000000000..069bbc81e9 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs @@ -0,0 +1,122 @@ +// +// 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.Headers; +using Microsoft.Owin; + +namespace OpenTelemetry.Implementation +{ + internal static class ActivityExtensions + { + /// + /// Maximum length of Correlation-Context header value. + /// + private const int MaxCorrelationContextLength = 1024; + + /// + /// Reads Request-Id and Correlation-Context headers and sets ParentId and Baggage on Activity. + /// Based on the code from https://github.com/aspnet/Microsoft.AspNet.TelemetryCorrelation/blob/master/src/Microsoft.AspNet.TelemetryCorrelation/ActivityExtensions.cs#L48 + /// + /// Instance of activity that has not been started yet. + /// Request headers collection. + /// true if request was parsed successfully, false - otherwise. + public static bool Extract(this Activity activity, IHeaderDictionary requestHeaders) + { + if (activity == null) + { + OwinExtensionsEventSource.Log.ActivityExtractionError("activity is null"); + return false; + } + + if (activity.ParentId != null) + { + OwinExtensionsEventSource.Log.ActivityExtractionError("ParentId is already set on activity"); + return false; + } + + if (activity.Id != null) + { + OwinExtensionsEventSource.Log.ActivityExtractionError("Activity is already started"); + return false; + } + + var traceParents = requestHeaders.GetValues(HeaderNames.TraceParent); + if (traceParents == null || traceParents.Count == 0) + { + traceParents = requestHeaders.GetValues(HeaderNames.RequestId); + } + + if (traceParents != null && traceParents.Count > 0 && !string.IsNullOrEmpty(traceParents[0])) + { + // there may be several Request-Id or traceparent headers, but we only read the first one + activity.SetParentId(traceParents[0]); + + var traceStates = requestHeaders.GetValues(HeaderNames.TraceState); + if (traceStates != null && traceStates.Count > 0) + { + if (traceStates.Count == 1 && !string.IsNullOrEmpty(traceStates[0])) + { + activity.TraceStateString = traceStates[0]; + } + else + { + activity.TraceStateString = string.Join(",", traceStates); + } + } + + // Header format - Correlation-Context: key1=value1, key2=value2 + var baggages = requestHeaders.GetValues(HeaderNames.CorrelationContext); + if (baggages != null) + { + int correlationContextLength = -1; + + // there may be several Correlation-Context headers + foreach (var item in baggages) + { + if (correlationContextLength >= MaxCorrelationContextLength) + { + break; + } + + foreach (var pair in item.Split(',')) + { + correlationContextLength += pair.Length + 1; // pair and comma + + if (correlationContextLength >= MaxCorrelationContextLength) + { + break; + } + + if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem)) + { + activity.AddBaggage(baggageItem.Name, baggageItem.Value); + } + else + { + OwinExtensionsEventSource.Log.HeaderParsingError(HeaderNames.CorrelationContext, pair); + } + } + } + } + + return true; + } + + return false; + } + } +} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs new file mode 100644 index 0000000000..23502a4c66 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs @@ -0,0 +1,27 @@ +// +// 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.Implementation +{ + internal class Context + { + public Activity Activity { get; set; } + internal bool HasDiagnosticListener { get; set; } + public bool EventLogEnabled { get; set; } + } +} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs new file mode 100644 index 0000000000..6fd280a904 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs @@ -0,0 +1,99 @@ +// +// 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; +using System.Diagnostics; +using System.Linq; +using Microsoft.Owin; + +namespace OpenTelemetry.Implementation +{ + internal static class HeaderDictionaryExtensions + { + private const int CorrelationContextHeaderMaxLength = 8192; + private const int CorrelationContextMaxPairs = 180; + + /// + /// Reads Correlation-Context and populates it on Activity.Baggage following https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/HttpCorrelationProtocol.md#correlation-context. + /// Use this method when you want force parsing Correlation-Context is absence of Request-Id or traceparent. + /// Based on the code from https://github.com/microsoft/ApplicationInsights-dotnet/blob/2.15.0/WEB/Src/Common/WebHeaderCollectionExtensions.cs#L135 + /// + /// Header collection. + /// Activity to populate baggage on. + public static void ReadActivityBaggage(this IHeaderDictionary headers, Activity activity) + { + Debug.Assert(headers != null, "Headers must not be null"); + Debug.Assert(activity != null, "Activity must not be null"); + Debug.Assert(!activity.Baggage.Any(), "Baggage must be empty"); + + int itemsCount = 0; + var correlationContexts = headers.GetValues(HeaderNames.CorrelationContext); + if (correlationContexts == null || correlationContexts.Count == 0) + { + return; + } + + int overallLength = 0; + foreach (var cc in correlationContexts) + { + var headerValue = cc.AsSpan(); + int currentLength = 0; + int initialLength = headerValue.Length; + while (itemsCount < CorrelationContextMaxPairs && currentLength < initialLength) + { + var nextSegment = headerValue.Slice(currentLength); + var nextComma = nextSegment.IndexOf(','); + if (nextComma < 0) + { + // last one + nextComma = nextSegment.Length; + } + + if (nextComma == 0) + { + currentLength += 1; + overallLength += 1; + continue; + } + + if (overallLength + nextComma >= CorrelationContextHeaderMaxLength) + { + return; + } + + ReadOnlySpan kvp = nextSegment.Slice(0, nextComma).Trim(); + + var separatorInd = kvp.IndexOf('='); + if (separatorInd > 0 && separatorInd < kvp.Length - 1) + { + var separatorIndNext = kvp.Slice(separatorInd + 1).IndexOf('='); + // check there is just one '=' in key-value-pair + if (separatorIndNext < 0) + { + var baggageKey = kvp.Slice(0, separatorInd).Trim().ToString(); + var baggageValue = kvp.Slice(separatorInd + 1).Trim().ToString(); + activity.AddBaggage(baggageKey, baggageValue); + itemsCount += 1; + } + } + + currentLength += nextComma + 1; + overallLength += nextComma + 1; + } + } + } + } +} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs new file mode 100644 index 0000000000..ec5499a8d0 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs @@ -0,0 +1,27 @@ +// +// 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.Implementation +{ + // // Microsoft.Net.Http.Headers headers (from https://github.com/dotnet/aspnetcore/blob/v5.0.0-rc.2.20475.17/src/Http/Headers/src/HeaderNames.cs) + internal static class HeaderNames + { + public static readonly string CorrelationContext = "Correlation-Context"; + public static readonly string RequestId = "Request-Id"; + public static readonly string TraceParent = "traceparent"; + public static readonly string TraceState = "tracestate"; + } +} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs new file mode 100644 index 0000000000..4fafeafe0e --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs @@ -0,0 +1,49 @@ +// +// 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 System.Runtime.CompilerServices; + +namespace OpenTelemetry.Implementation +{ + /// + /// EventSource events emitted from the project. + /// + [EventSource(Name = "OpenTelemetry-Extensions-Owin")] + internal class OwinExtensionsEventSource : EventSource + { + public static OwinExtensionsEventSource Log = new OwinExtensionsEventSource(); + + [Event(1, Message = "Failed to extract activity, reason '{0}'", Level = EventLevel.Error)] + public void ActivityExtractionError(string reason) + { + this.WriteEvent(1, reason); + } + + [Event(2, Message = "Failed to parse header '{0}', value: '{1}'", Level = EventLevel.Informational)] + public void HeaderParsingError(string headerName, string headerValue) + { + this.WriteEvent(2, headerName, headerValue); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [Event(3, Level = EventLevel.Error)] + public void UnhandledException() + { + this.WriteEvent(3); + } + } +} diff --git a/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj b/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj new file mode 100644 index 0000000000..87454ea2e6 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj @@ -0,0 +1,18 @@ + + + + net461;netstandard2.0 + A component to the OWIN pipeline for instrumenting incoming request with System.Diagnostics.Activity and notifying listeners with DiagnosticsSource. + OpenTelemetry + + + + + + + + + + + + diff --git a/src/OpenTelemetry.Extensions.Owin/README.md b/src/OpenTelemetry.Extensions.Owin/README.md new file mode 100644 index 0000000000..22753806e0 --- /dev/null +++ b/src/OpenTelemetry.Extensions.Owin/README.md @@ -0,0 +1,27 @@ +# Telemetry correlation library for OWIN/Katana + +[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Extensions.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Extensions.Owin) +[![NuGet](https://img.shields.io/nuget/dt/OOpenTelemetry.Extensions.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Extensions.Owin) + +This is an [Instrumentation +Library](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/glossary.md#instrumentation-library), +which instruments the [OWIN/Katana](https://github.com/aspnet/AspNetKatana/) +and notifies listeners about incoming web requests. + +## Steps to enable OpenTelemetry.Extensions.Owin + +### Step 1: Install Package + +Add a reference to the +[`OpenTelemetry.Extensions.Owin`](https://www.nuget.org/packages/opentelemetry.extensions.owin) +package. Also, add any other instrumentations & exporters you will need. + +```shell +dotnet add package OpenTelemetry.Extensions.Owin +``` + +## References + +* [Open Web Interface for .NET](http://owin.org/) +* [Katana Project](https://github.com/aspnet/AspNetKatana/) +* [OpenTelemetry Project](https://opentelemetry.io/) diff --git a/src/OpenTelemetry.Instrumentation.Owin/AssemblyInfo.cs b/src/OpenTelemetry.Instrumentation.Owin/AssemblyInfo.cs new file mode 100644 index 0000000000..bbe3b27636 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/AssemblyInfo.cs @@ -0,0 +1,22 @@ +// +// 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.Instrumentation.Owin.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] +#else +[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.Owin.Tests")] +#endif diff --git a/src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md new file mode 100644 index 0000000000..134621e04d --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## Unreleased + +* Initial release diff --git a/src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.cs new file mode 100644 index 0000000000..442e513556 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.cs @@ -0,0 +1,34 @@ +// +// 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; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Instrumentation.Owin.Implementation +{ + internal class HttpInListener : ListenerHandler + { + private readonly OwinInstrumentationOptions options; + private readonly ActivitySourceAdapter activitySource; + + public HttpInListener(string name, OwinInstrumentationOptions options, ActivitySourceAdapter activitySource) + : base(name) + { + this.options = options ?? throw new ArgumentNullException(nameof(options)); + this.activitySource = activitySource; + } + } +} diff --git a/src/OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs b/src/OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs new file mode 100644 index 0000000000..0bf1b3609d --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs @@ -0,0 +1,29 @@ +// +// 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; + +namespace OpenTelemetry.Instrumentation.Owin.Implementation +{ + /// + /// EventSource events emitted from the project. + /// + [EventSource(Name = "OpenTelemetry-Instrumentation-Owin")] + internal class OwinInstrumentationEventSource : EventSource + { + public static OwinInstrumentationEventSource Log = new OwinInstrumentationEventSource(); + } +} diff --git a/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj b/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj new file mode 100644 index 0000000000..1bcc51c877 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj @@ -0,0 +1,21 @@ + + + net452;net46;netstandard2.0 + OWIN instrumentation for OpenTelemetry .NET + $(PackageTags);distributed-tracing;OWIN + + + + + + + + + + + + + + + + diff --git a/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.cs new file mode 100644 index 0000000000..a18aef0d2e --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.cs @@ -0,0 +1,47 @@ +// +// 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; +using OpenTelemetry.Instrumentation.Owin.Implementation; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Instrumentation.Owin +{ + /// + /// OWIN Requests instrumentation. + /// + internal class OwinInstrumentation : IDisposable + { + private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber; + + /// + /// Initializes a new instance of the class. + /// + /// ActivitySource adapter instance. + /// Configuration options for OWIN instrumentation. + public OwinInstrumentation(ActivitySourceAdapter activitySource, OwinInstrumentationOptions options) + { + this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpInListener("OpenTelemetry.Extensions.Owin", options, activitySource), null); + this.diagnosticSourceSubscriber.Subscribe(); + } + + /// + public void Dispose() + { + this.diagnosticSourceSubscriber?.Dispose(); + } + } +} diff --git a/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs new file mode 100644 index 0000000000..cdd0735e53 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs @@ -0,0 +1,25 @@ +// +// 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.Instrumentation.Owin +{ + /// + /// Options for requests instrumentation. + /// + public class OwinInstrumentationOptions + { + } +} diff --git a/src/OpenTelemetry.Instrumentation.Owin/README.md b/src/OpenTelemetry.Instrumentation.Owin/README.md new file mode 100644 index 0000000000..70ab5bb494 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/README.md @@ -0,0 +1,27 @@ +# OWIN Instrumentation for OpenTelemetry + +[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Owin) +[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Owin) + +This is an [Instrumentation +Library](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/glossary.md#instrumentation-library), +which instruments [OWIN/Katana](https://github.com/aspnet/AspNetKatana/) and +collect telemetry about incoming web requests. + +## Steps to enable OpenTelemetry.Instrumentation.Owin + +### Step 1: Install Package + +Add a reference to the +[`OpenTelemetry.Instrumentation.Owin`](https://www.nuget.org/packages/opentelemetry.instrumentation.owin) +package. Also, add any other instrumentations & exporters you will need. + +```shell +dotnet add package OpenTelemetry.Instrumentation.Owin +``` + +## References + +* [Open Web Interface for .NET](http://owin.org/) +* [Katana Project](https://github.com/aspnet/AspNetKatana/) +* [OpenTelemetry Project](https://opentelemetry.io/) diff --git a/src/OpenTelemetry.Instrumentation.Owin/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Owin/TracerProviderBuilderExtensions.cs new file mode 100644 index 0000000000..a1373dd878 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.Owin/TracerProviderBuilderExtensions.cs @@ -0,0 +1,50 @@ +// +// 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; +using OpenTelemetry.Instrumentation.Owin; + +namespace OpenTelemetry.Trace +{ + /// + /// Extension methods to simplify registering of OWIN request instrumentation. + /// + public static class TracerProviderBuilderExtensions + { + /// + /// Enables the incoming requests automatic data collection for OWIN. + /// + /// being configured. + /// OWIN Request configuration options. + /// The instance of to chain the calls. + public static TracerProviderBuilder AddOwinInstrumentation( + this TracerProviderBuilder builder, + Action configureOwinInstrumentationOptions = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + var owinOptions = new OwinInstrumentationOptions(); + configureOwinInstrumentationOptions?.Invoke(owinOptions); + + builder.AddInstrumentation(activitySource => new OwinInstrumentation(activitySource, owinOptions)); + + return builder; + } + } +} From 4aff47056f084dbc93d6ca33fcf15b960d0a1abc Mon Sep 17 00:00:00 2001 From: Denis Ivanov Date: Thu, 5 Nov 2020 18:22:29 +0100 Subject: [PATCH 02/14] Analyzers warnings fixed. --- src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs | 5 ++--- src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs | 3 ++- .../Implementation/ActivityExtensions.cs | 2 +- src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs | 4 +++- .../Implementation/HeaderDictionaryExtensions.cs | 3 ++- .../OpenTelemetry.Instrumentation.Owin.csproj | 6 +----- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs b/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs index 1931119f8c..67d93b5952 100644 --- a/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs +++ b/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs @@ -25,9 +25,8 @@ public static class AppBuilderExtensions { /// Adds a component to the OWIN pipeline for instrumenting incoming request with System.Diagnostics.Activity and notifying listeners with DiagnosticsSource. /// The application builder. - /// The application builder. - public static void UseOpenTelemetry(this IAppBuilder appBuilder) + /// The application builder instance. + public static IAppBuilder UseOpenTelemetry(this IAppBuilder appBuilder) => appBuilder.Use(); - } } diff --git a/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs index 88e280cefa..3f031a50ba 100644 --- a/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs +++ b/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs @@ -38,7 +38,7 @@ public sealed class DiagnosticsMiddleware : OwinMiddleware /// /// Initializes a new instance of the class. /// - /// An optional pointer to the next component + /// An optional pointer to the next component. public DiagnosticsMiddleware(OwinMiddleware next) : base(next) { @@ -80,6 +80,7 @@ private void BeginRequest(IOwinContext owinContext) private void RequestEnd(IOwinContext owinContext, Exception exception) { var activity = this.context.Activity; + // Always stop activity if it was started if (activity != null) { diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs index 069bbc81e9..bb2cc6af22 100644 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs @@ -29,11 +29,11 @@ internal static class ActivityExtensions /// /// Reads Request-Id and Correlation-Context headers and sets ParentId and Baggage on Activity. - /// Based on the code from https://github.com/aspnet/Microsoft.AspNet.TelemetryCorrelation/blob/master/src/Microsoft.AspNet.TelemetryCorrelation/ActivityExtensions.cs#L48 /// /// Instance of activity that has not been started yet. /// Request headers collection. /// true if request was parsed successfully, false - otherwise. + // Based on the code from https://github.com/aspnet/Microsoft.AspNet.TelemetryCorrelation/blob/master/src/Microsoft.AspNet.TelemetryCorrelation/ActivityExtensions.cs#L48. public static bool Extract(this Activity activity, IHeaderDictionary requestHeaders) { if (activity == null) diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs index 23502a4c66..3c21c83c66 100644 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs @@ -21,7 +21,9 @@ namespace OpenTelemetry.Implementation internal class Context { public Activity Activity { get; set; } - internal bool HasDiagnosticListener { get; set; } + + public bool HasDiagnosticListener { get; set; } + public bool EventLogEnabled { get; set; } } } diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs index 6fd280a904..b8dccfe801 100644 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs +++ b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs @@ -29,10 +29,10 @@ internal static class HeaderDictionaryExtensions /// /// Reads Correlation-Context and populates it on Activity.Baggage following https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/HttpCorrelationProtocol.md#correlation-context. /// Use this method when you want force parsing Correlation-Context is absence of Request-Id or traceparent. - /// Based on the code from https://github.com/microsoft/ApplicationInsights-dotnet/blob/2.15.0/WEB/Src/Common/WebHeaderCollectionExtensions.cs#L135 /// /// Header collection. /// Activity to populate baggage on. + // Based on the code from https://github.com/microsoft/ApplicationInsights-dotnet/blob/2.15.0/WEB/Src/Common/WebHeaderCollectionExtensions.cs#L135. public static void ReadActivityBaggage(this IHeaderDictionary headers, Activity activity) { Debug.Assert(headers != null, "Headers must not be null"); @@ -80,6 +80,7 @@ public static void ReadActivityBaggage(this IHeaderDictionary headers, Activity if (separatorInd > 0 && separatorInd < kvp.Length - 1) { var separatorIndNext = kvp.Slice(separatorInd + 1).IndexOf('='); + // check there is just one '=' in key-value-pair if (separatorIndNext < 0) { diff --git a/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj b/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj index 1bcc51c877..28d79344a4 100644 --- a/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj +++ b/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj @@ -1,6 +1,6 @@ - net452;net46;netstandard2.0 + net461;netstandard2.0 OWIN instrumentation for OpenTelemetry .NET $(PackageTags);distributed-tracing;OWIN @@ -10,10 +10,6 @@ - - - - From 18985fe33e44e7f9341ba92cda3357b022e0a205 Mon Sep 17 00:00:00 2001 From: Denis Ivanov Date: Thu, 5 Nov 2020 18:28:03 +0100 Subject: [PATCH 03/14] Formatting fixes. --- src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs | 2 +- .../OpenTelemetry.Extensions.Owin.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs index 3f031a50ba..3c60523475 100644 --- a/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs +++ b/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj b/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj index 87454ea2e6..fd6e0b24ac 100644 --- a/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj +++ b/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj @@ -5,7 +5,7 @@ A component to the OWIN pipeline for instrumenting incoming request with System.Diagnostics.Activity and notifying listeners with DiagnosticsSource. OpenTelemetry - + From aca7a6e1b2c97a464a05e4e2e329bffb6f2e2587 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 19 Sep 2021 22:08:56 -0700 Subject: [PATCH 04/14] OWIN instrumentation & example project. --- .../package-Instrumentation.Owin.yml | 49 ++++ CODEOWNERS | 6 +- build/Common.nonprod.props | 5 - build/Common.prod.props | 7 - build/Common.props | 1 + .../owin/Controllers/TestController.cs | 19 +- examples/owin/Examples.Owin.csproj | 18 ++ examples/owin/Program.cs | 86 +++++++ examples/owin/README.md | 0 opentelemetry-dotnet-contrib.sln | 18 ++ .../AppBuilderExtensions.cs | 15 +- .../AssemblyInfo.cs | 0 .../CHANGELOG.md | 0 .../Implementation/DiagnosticsMiddleware.cs | 217 ++++++++++++++++++ .../OwinInstrumentationActivitySource.cs | 34 +++ .../OwinInstrumentationEventSource.cs | 94 ++++++++ ...emetry.Contrib.Instrumentation.Owin.csproj | 21 ++ .../OwinEnrichEventNames.cs} | 21 +- .../OwinInstrumentationOptions.cs | 55 +++++ .../README.md | 112 +++++++++ .../TracerProviderBuilderExtensions.cs | 8 +- .../AssemblyInfo.cs | 22 -- .../DiagnosticsMiddleware.cs | 159 ------------- .../Implementation/ActivityExtensions.cs | 122 ---------- .../HeaderDictionaryExtensions.cs | 100 -------- .../Implementation/HeaderNames.cs | 27 --- .../OwinExtensionsEventSource.cs | 49 ---- .../OpenTelemetry.Extensions.Owin.csproj | 18 -- src/OpenTelemetry.Extensions.Owin/README.md | 27 --- .../CHANGELOG.md | 5 - .../Implementation/HttpInListener.cs | 34 --- .../OpenTelemetry.Instrumentation.Owin.csproj | 17 -- .../OwinInstrumentation.cs | 47 ---- .../OwinInstrumentationOptions.cs | 25 -- .../README.md | 27 --- 35 files changed, 745 insertions(+), 720 deletions(-) create mode 100644 .github/workflows/package-Instrumentation.Owin.yml rename src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs => examples/owin/Controllers/TestController.cs (66%) create mode 100644 examples/owin/Examples.Owin.csproj create mode 100644 examples/owin/Program.cs create mode 100644 examples/owin/README.md rename src/{OpenTelemetry.Extensions.Owin => OpenTelemetry.Contrib.Instrumentation.Owin}/AppBuilderExtensions.cs (67%) rename src/{OpenTelemetry.Instrumentation.Owin => OpenTelemetry.Contrib.Instrumentation.Owin}/AssemblyInfo.cs (100%) rename src/{OpenTelemetry.Extensions.Owin => OpenTelemetry.Contrib.Instrumentation.Owin}/CHANGELOG.md (100%) create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Owin/OpenTelemetry.Contrib.Instrumentation.Owin.csproj rename src/{OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs => OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs} (57%) create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md rename src/{OpenTelemetry.Instrumentation.Owin => OpenTelemetry.Contrib.Instrumentation.Owin}/TracerProviderBuilderExtensions.cs (85%) delete mode 100644 src/OpenTelemetry.Extensions.Owin/AssemblyInfo.cs delete mode 100644 src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs delete mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs delete mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs delete mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs delete mode 100644 src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs delete mode 100644 src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj delete mode 100644 src/OpenTelemetry.Extensions.Owin/README.md delete mode 100644 src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md delete mode 100644 src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.cs delete mode 100644 src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj delete mode 100644 src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.cs delete mode 100644 src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs delete mode 100644 src/OpenTelemetry.Instrumentation.Owin/README.md diff --git a/.github/workflows/package-Instrumentation.Owin.yml b/.github/workflows/package-Instrumentation.Owin.yml new file mode 100644 index 0000000000..5ae2534edb --- /dev/null +++ b/.github/workflows/package-Instrumentation.Owin.yml @@ -0,0 +1,49 @@ +name: Pack OpenTelemetry.Contrib.Instrumentation.Owin + +on: + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + push: + tags: + - 'Instrumentation.Owin-*' + +jobs: + build-test-pack: + runs-on: ${{ matrix.os }} + env: + PROJECT: OpenTelemetry.Contrib.Instrumentation.Owin + + strategy: + matrix: + os: [windows-latest] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # fetching all + + - name: Install dependencies + run: dotnet restore + + - name: dotnet build ${{env.PROJECT}} + run: dotnet build src/${{env.PROJECT}} --configuration Release --no-restore -p:Deterministic=true + + - name: dotnet test ${{env.PROJECT}} + run: dotnet test test/${{env.PROJECT}}.Tests + + - name: dotnet pack ${{env.PROJECT}} + run: dotnet pack src/${{env.PROJECT}} --configuration Release --no-build + + - name: Publish Artifacts + uses: actions/upload-artifact@v2 + with: + name: ${{env.PROJECT}}-packages + path: '**/${{env.PROJECT}}/bin/**/*.*nupkg' + + - name: Publish Nuget + run: | + nuget push **/${{env.PROJECT}}/bin/**/*.nupkg -Source https://api.nuget.org/v3/index.json -ApiKey ${{ secrets.NUGET_TOKEN }} -SymbolApiKey ${{ secrets.NUGET_TOKEN }} diff --git a/CODEOWNERS b/CODEOWNERS index 12d3abdfd4..d22258b37e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -8,8 +8,9 @@ src/OpenTelemetry.Contrib.Exporter.Stackdriver/ @ope src/OpenTelemetry.Contrib.Extensions.AWSXRay/ @open-telemetry/dotnet-contrib-approvers @srprash @lupengamzn src/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient/ @open-telemetry/dotnet-contrib-approvers @ejsmith src/OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore/ @open-telemetry/dotnet-contrib-approvers -src/OpenTelemetry.Contrib.Instrumentation.MassTransit/ @open-telemetry/dotnet-contrib-approvers @alexvaluyskiy src/OpenTelemetry.Contrib.Instrumentation.GrpcCore/ @open-telemetry/dotnet-contrib-approvers @pcwiese +src/OpenTelemetry.Contrib.Instrumentation.MassTransit/ @open-telemetry/dotnet-contrib-approvers @alexvaluyskiy +src/OpenTelemetry.Contrib.Instrumentation.Owin/ @open-telemetry/dotnet-contrib-approvers @codeblanch src/OpenTelemetry.Contrib.Instrumentation.Wcf/ @open-telemetry/dotnet-contrib-approvers @codeblanch src/OpenTelemetry.Contrib.Instrumentation.MySqlData/ @open-telemetry/dotnet-contrib-approvers @moonheart src/OpenTelemetry.Contrib.Preview/ @open-telemetry/dotnet-contrib-approvers @codeblanch @@ -18,8 +19,9 @@ test/OpenTelemetry.Contrib.Exporter.Stackdriver.Tests/ @ope test/OpenTelemetry.Contrib.Extensions.AWSXRay.Tests/ @open-telemetry/dotnet-contrib-approvers @srprash @lupengamzn test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ @open-telemetry/dotnet-contrib-approvers @ejsmith test/OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCoreTests/ @open-telemetry/dotnet-contrib-approvers -test/OpenTelemetry.Contrib.Instrumentation.MassTransit.Tests/ @open-telemetry/dotnet-contrib-approvers @alexvaluyskiy test/OpenTelemetry.Contrib.Instrumentation.GrpcCore.Tests/ @open-telemetry/dotnet-contrib-approvers @pcwiese +test/OpenTelemetry.Contrib.Instrumentation.MassTransit.Tests/ @open-telemetry/dotnet-contrib-approvers @alexvaluyskiy +test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/ @open-telemetry/dotnet-contrib-approvers @codeblanch test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/ @open-telemetry/dotnet-contrib-approvers @codeblanch test/OpenTelemetry.Contrib.Instrumentation.MySqlData.Tests/ @open-telemetry/dotnet-contrib-approvers @moonheart test/OpenTelemetry.Contrib.Preview.Tests/ @open-telemetry/dotnet-contrib-approvers @codeblanch diff --git a/build/Common.nonprod.props b/build/Common.nonprod.props index b43fa19ccf..3dd5e62aa5 100644 --- a/build/Common.nonprod.props +++ b/build/Common.nonprod.props @@ -4,11 +4,6 @@ false $(MSBuildThisFileDirectory)/OpenTelemetryContrib.test.ruleset - - true - $(MSBuildThisFileDirectory)/debug.snk - $(DefineConstants);SIGNED - true $(NoWarn),1574,1591 diff --git a/build/Common.prod.props b/build/Common.prod.props index 41dc196e13..fa47e5e8d7 100644 --- a/build/Common.prod.props +++ b/build/Common.prod.props @@ -1,13 +1,6 @@ - - true - $(MSBuildThisFileDirectory)debug.snk - $(DefineConstants);SIGNED - true - - git https://github.com/open-telemetry/opentelemetry-dotnet-contrib diff --git a/build/Common.props b/build/Common.props index 00cea01585..dac6c3f051 100644 --- a/build/Common.props +++ b/build/Common.props @@ -26,6 +26,7 @@ [16.7.1] [2.1.0,5.0) [1.0.0,2.0) + [4.1.1] [3.3.2] [1.0.0,2.0) [1.1.118,2.0) diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs b/examples/owin/Controllers/TestController.cs similarity index 66% rename from src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs rename to examples/owin/Controllers/TestController.cs index 3c21c83c66..4b8e29eddc 100644 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/Context.cs +++ b/examples/owin/Controllers/TestController.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,16 +14,17 @@ // limitations under the License. // -using System.Diagnostics; +using System; +using System.Web.Http; -namespace OpenTelemetry.Implementation +namespace Examples.Owin.Controllers { - internal class Context + public class TestController : ApiController { - public Activity Activity { get; set; } - - public bool HasDiagnosticListener { get; set; } - - public bool EventLogEnabled { get; set; } + // GET api/test/{id} + public string Get(string id = null) + { + return $"id:{id}"; + } } } diff --git a/examples/owin/Examples.Owin.csproj b/examples/owin/Examples.Owin.csproj new file mode 100644 index 0000000000..0fbabb0964 --- /dev/null +++ b/examples/owin/Examples.Owin.csproj @@ -0,0 +1,18 @@ + + + + Exe + net461 + false + + + + + + + + + + + + diff --git a/examples/owin/Program.cs b/examples/owin/Program.cs new file mode 100644 index 0000000000..225a4a078f --- /dev/null +++ b/examples/owin/Program.cs @@ -0,0 +1,86 @@ +// +// 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; +using System.Diagnostics; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.Owin.Hosting; +using OpenTelemetry; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using Owin; + +namespace Examples.Owin +{ + internal static class Program + { + public static void Main() + { + using var host = WebApp.Start( + "http://localhost:9000", + appBuilder => + { + appBuilder.UseOpenTelemetry(); + + HttpConfiguration config = new HttpConfiguration(); + + config.MessageHandlers.Add(new ActivityDisplayNameRouteEnrichingHandler()); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional }); + + appBuilder.UseWebApi(config); + }); + + using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Owin-Example")) + .AddOwinInstrumentation() + .AddConsoleExporter() + .Build(); + + Console.WriteLine("Service listening. Press enter to exit."); + Console.ReadLine(); + } + + private class ActivityDisplayNameRouteEnrichingHandler : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + try + { + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + finally + { + var activity = Activity.Current; + if (activity != null) + { + var routeData = request.GetRouteData(); + if (routeData != null) + { + activity.DisplayName = routeData.Route.RouteTemplate; + } + } + } + } + } + } +} diff --git a/examples/owin/README.md b/examples/owin/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index c78fe4d4ff..fbc71143a3 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -36,6 +36,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\package-Instrumentation.GrpcCore.yml = .github\workflows\package-Instrumentation.GrpcCore.yml .github\workflows\package-Instrumentation.MassTransit.yml = .github\workflows\package-Instrumentation.MassTransit.yml .github\workflows\package-Instrumentation.MySqlData.yml = .github\workflows\package-Instrumentation.MySqlData.yml + .github\workflows\package-Instrumentation.Owin.yml = .github\workflows\package-Instrumentation.Owin.yml .github\workflows\package-Instrumentation.Wcf.yml = .github\workflows\package-Instrumentation.Wcf.yml .github\workflows\package-Preview.yml = .github\workflows\package-Preview.yml .github\workflows\pr_build.yml = .github\workflows\pr_build.yml @@ -143,6 +144,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Exten EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Extensions.AzureMonitor.Tests", "test\OpenTelemetry.Contrib.Extensions.AzureMonitor.Tests\OpenTelemetry.Contrib.Extensions.AzureMonitor.Tests.csproj", "{771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.Owin", "src\OpenTelemetry.Contrib.Instrumentation.Owin\OpenTelemetry.Contrib.Instrumentation.Owin.csproj", "{530255C1-D130-4B43-981D-911E54F7C787}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "owin", "owin", "{8D11A34C-D0EF-4DE1-8230-32168E67044D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Owin", "examples\owin\Examples.Owin.csproj", "{6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -273,6 +280,14 @@ Global {771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}.Release|Any CPU.Build.0 = Release|Any CPU + {530255C1-D130-4B43-981D-911E54F7C787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {530255C1-D130-4B43-981D-911E54F7C787}.Debug|Any CPU.Build.0 = Debug|Any CPU + {530255C1-D130-4B43-981D-911E54F7C787}.Release|Any CPU.ActiveCfg = Release|Any CPU + {530255C1-D130-4B43-981D-911E54F7C787}.Release|Any CPU.Build.0 = Release|Any CPU + {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -314,6 +329,9 @@ Global {4172D671-3AAC-443F-9EB0-A6608B154AF0} = {2097345F-4DD3-477D-BC54-A922F9B2B402} {9C2D6D1A-8580-4527-B718-E206D0690635} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {771651AA-010F-4FF6-9CB2-BAFFE5E04FC2} = {2097345F-4DD3-477D-BC54-A922F9B2B402} + {530255C1-D130-4B43-981D-911E54F7C787} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} + {8D11A34C-D0EF-4DE1-8230-32168E67044D} = {B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA} + {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423} = {8D11A34C-D0EF-4DE1-8230-32168E67044D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/AppBuilderExtensions.cs similarity index 67% rename from src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs rename to src/OpenTelemetry.Contrib.Instrumentation.Owin/AppBuilderExtensions.cs index 67d93b5952..902cfe3286 100644 --- a/src/OpenTelemetry.Extensions.Owin/AppBuilderExtensions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/AppBuilderExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,18 +14,21 @@ // limitations under the License. // -using Owin; +using System.Diagnostics; +using OpenTelemetry.Contrib.Instrumentation.Owin; -namespace OpenTelemetry +namespace Owin { /// /// Provides extension methods for the class. /// public static class AppBuilderExtensions { - /// Adds a component to the OWIN pipeline for instrumenting incoming request with System.Diagnostics.Activity and notifying listeners with DiagnosticsSource. - /// The application builder. - /// The application builder instance. + /// Adds a component to the OWIN pipeline for instrumenting + /// incoming request with and notifying listeners + /// with . + /// . + /// Supplied for chaining. public static IAppBuilder UseOpenTelemetry(this IAppBuilder appBuilder) => appBuilder.Use(); } diff --git a/src/OpenTelemetry.Instrumentation.Owin/AssemblyInfo.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs similarity index 100% rename from src/OpenTelemetry.Instrumentation.Owin/AssemblyInfo.cs rename to src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs diff --git a/src/OpenTelemetry.Extensions.Owin/CHANGELOG.md b/src/OpenTelemetry.Contrib.Instrumentation.Owin/CHANGELOG.md similarity index 100% rename from src/OpenTelemetry.Extensions.Owin/CHANGELOG.md rename to src/OpenTelemetry.Contrib.Instrumentation.Owin/CHANGELOG.md diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs new file mode 100644 index 0000000000..fffaca47a7 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs @@ -0,0 +1,217 @@ +// +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Microsoft.Owin; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Contrib.Instrumentation.Owin +{ + /// + /// Instruments incoming request with and notifies listeners with . + /// + internal sealed class DiagnosticsMiddleware : OwinMiddleware + { + private const string ContextKey = "__OpenTelemetry.Context__"; + private static readonly Func> OwinRequestHeaderValuesGetter + = (request, name) => request.Headers.GetValues(name); + + /// + /// Initializes a new instance of the class. + /// + /// An optional pointer to the next component. + public DiagnosticsMiddleware(OwinMiddleware next) + : base(next) + { + } + + /// + public override async Task Invoke(IOwinContext owinContext) + { + try + { + BeginRequest(owinContext); + await this.Next.Invoke(owinContext).ConfigureAwait(false); + RequestEnd(owinContext, null); + } + catch (Exception ex) + { + RequestEnd(owinContext, ex); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BeginRequest(IOwinContext owinContext) + { + try + { + if (OwinInstrumentationActivitySource.Options == null || OwinInstrumentationActivitySource.Options.Filter?.Invoke(owinContext) == false) + { + OwinInstrumentationEventSource.Log.RequestIsFilteredOut(); + return; + } + } + catch (Exception ex) + { + OwinInstrumentationEventSource.Log.RequestFilterException(ex); + return; + } + + var textMapPropagator = OwinInstrumentationActivitySource.Options.Propagator; + var ctx = textMapPropagator.Extract(default, owinContext.Request, OwinRequestHeaderValuesGetter); + + Activity activity = OwinInstrumentationActivitySource.ActivitySource.StartActivity( + OwinInstrumentationActivitySource.IncomingRequestActivityName, + ActivityKind.Server, + ctx.ActivityContext); + + if (activity != null) + { + var request = owinContext.Request; + + /* + * Note: Display name is intentionally set to a low cardinality + * value because OWIN does not expose any kind of + * route/template. See: + * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name + */ + activity.DisplayName = request.Method switch + { + "GET" => "HTTP GET", + "POST" => "HTTP POST", + "PUT" => "HTTP PUT", + "DELETE" => "HTTP DELETE", + _ => $"HTTP {request.Method}", + }; + + if (activity.IsAllDataRequested) + { + if (request.Uri.Port == 80 || request.Uri.Port == 443) + { + activity.SetTag(SemanticConventions.AttributeHttpHost, request.Uri.Host); + } + else + { + activity.SetTag(SemanticConventions.AttributeHttpHost, request.Uri.Host + ":" + request.Uri.Port); + } + + activity.SetTag(SemanticConventions.AttributeHttpMethod, request.Method); + activity.SetTag(SemanticConventions.AttributeHttpTarget, request.Uri.AbsolutePath); + activity.SetTag(SemanticConventions.AttributeHttpUrl, GetUriTagValueFromRequestUri(request.Uri)); + + if (request.Headers.TryGetValue("User-Agent", out string[] userAgent) && userAgent.Length > 0) + { + activity.SetTag(SemanticConventions.AttributeHttpUserAgent, userAgent[0]); + } + + try + { + OwinInstrumentationActivitySource.Options.Enrich?.Invoke( + activity, + OwinEnrichEventNames.BeginRequest, + owinContext, + null); + } + catch (Exception ex) + { + OwinInstrumentationEventSource.Log.EnrichmentException(ex); + } + } + + if (!(textMapPropagator is TraceContextPropagator)) + { + Baggage.Current = ctx.Baggage; + } + + owinContext.Environment[ContextKey] = activity; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void RequestEnd(IOwinContext owinContext, Exception exception) + { + if (owinContext.Environment.TryGetValue(ContextKey, out object context) + && context is Activity activity) + { + if (Activity.Current != activity) + { + Activity.Current = activity; + } + + if (activity.IsAllDataRequested) + { + var response = owinContext.Response; + + if (exception != null) + { + activity.SetStatus(Status.Error); + + if (OwinInstrumentationActivitySource.Options.RecordException) + { + activity.RecordException(exception); + } + } + else if (activity.GetStatus().StatusCode == StatusCode.Unset) + { + activity.SetStatus(SpanHelper.ResolveSpanStatusForHttpStatusCode(response.StatusCode)); + } + + activity.SetTag(SemanticConventions.AttributeHttpStatusCode, response.StatusCode); + + try + { + OwinInstrumentationActivitySource.Options.Enrich?.Invoke( + activity, + OwinEnrichEventNames.EndRequest, + owinContext, + exception); + } + catch (Exception ex) + { + OwinInstrumentationEventSource.Log.EnrichmentException(ex); + } + } + + activity.Stop(); + + if (!(OwinInstrumentationActivitySource.Options.Propagator is TraceContextPropagator)) + { + Baggage.Current = default; + } + } + } + + /// + /// Gets the OpenTelemetry standard uri tag value for a span based on its request . + /// + /// . + /// Span uri value. + private static string GetUriTagValueFromRequestUri(Uri uri) + { + if (string.IsNullOrEmpty(uri.UserInfo)) + { + return uri.ToString(); + } + + return string.Concat(uri.Scheme, Uri.SchemeDelimiter, uri.Authority, uri.PathAndQuery, uri.Fragment); + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs new file mode 100644 index 0000000000..f34b857336 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs @@ -0,0 +1,34 @@ +// +// 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; +using System.Diagnostics; + +namespace OpenTelemetry.Contrib.Instrumentation.Owin +{ + internal static class OwinInstrumentationActivitySource + { + public const string ActivitySourceName = "OpenTelemetry.OWIN"; + public const string IncomingRequestActivityName = ActivitySourceName + ".IncomingRequest"; + public const string OutgoingRequestActivityName = ActivitySourceName + ".OutgoingRequest"; + + private static readonly Version Version = typeof(OwinInstrumentationActivitySource).Assembly.GetName().Version; + + public static ActivitySource ActivitySource { get; } = new ActivitySource(ActivitySourceName, Version.ToString()); + + public static OwinInstrumentationOptions Options { get; set; } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs new file mode 100644 index 0000000000..d96d268794 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs @@ -0,0 +1,94 @@ +// +// 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; +using System.Diagnostics.Tracing; +using System.Globalization; +using System.Threading; + +namespace OpenTelemetry.Contrib.Instrumentation.Owin +{ + /// + /// EventSource events emitted from the project. + /// + [EventSource(Name = "OpenTelemetry-Instrumentation-Owin")] + internal class OwinInstrumentationEventSource : EventSource + { + public static OwinInstrumentationEventSource Log { get; } = new OwinInstrumentationEventSource(); + + [NonEvent] + public void RequestFilterException(Exception ex) + { + if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) + { + this.RequestFilterException(ToInvariantString(ex)); + } + } + + [Event(EventIds.RequestIsFilteredOut, Message = "Request is filtered out.", Level = EventLevel.Verbose)] + public void RequestIsFilteredOut() + { + this.WriteEvent(EventIds.RequestIsFilteredOut); + } + + [Event(EventIds.RequestFilterException, Message = "InstrumentationFilter threw exception. Request will not be collected. Exception {0}.", Level = EventLevel.Error)] + public void RequestFilterException(string exception) + { + this.WriteEvent(EventIds.RequestFilterException, exception); + } + + [NonEvent] + public void EnrichmentException(Exception exception) + { + if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) + { + this.EnrichmentException(ToInvariantString(exception)); + } + } + + [Event(EventIds.EnrichmentException, Message = "Enrichment threw exception. Exception {0}.", Level = EventLevel.Error)] + public void EnrichmentException(string exception) + { + this.WriteEvent(EventIds.EnrichmentException, exception); + } + + /// + /// Returns a culture-independent string representation of the given object, + /// appropriate for diagnostics tracing. + /// + private static string ToInvariantString(Exception exception) + { + var originalUICulture = Thread.CurrentThread.CurrentUICulture; + + try + { + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + return exception.ToString(); + } + finally + { + Thread.CurrentThread.CurrentUICulture = originalUICulture; + } + } + + private class EventIds + { + public const int RequestIsFilteredOut = 1; + public const int RequestFilterException = 2; + public const int EnrichmentException = 3; + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OpenTelemetry.Contrib.Instrumentation.Owin.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OpenTelemetry.Contrib.Instrumentation.Owin.csproj new file mode 100644 index 0000000000..86e437fbde --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OpenTelemetry.Contrib.Instrumentation.Owin.csproj @@ -0,0 +1,21 @@ + + + net461 + OpenTelemetry instrumentation for OWIN + $(PackageTags);distributed-tracing;OWIN + Instrumentation.Owin- + true + + + + + + + + + + + + + + diff --git a/src/OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs similarity index 57% rename from src/OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs rename to src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs index 0bf1b3609d..0bc4c74e16 100644 --- a/src/OpenTelemetry.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,16 +14,21 @@ // limitations under the License. // -using System.Diagnostics.Tracing; - -namespace OpenTelemetry.Instrumentation.Owin.Implementation +namespace OpenTelemetry.Contrib.Instrumentation.Owin { /// - /// EventSource events emitted from the project. + /// Constants used for event names when enriching an activity. /// - [EventSource(Name = "OpenTelemetry-Instrumentation-Owin")] - internal class OwinInstrumentationEventSource : EventSource + public class OwinEnrichEventNames { - public static OwinInstrumentationEventSource Log = new OwinInstrumentationEventSource(); + /// + /// Begin request. + /// + public const string BeginRequest = "BeginRequest"; + + /// + /// End request. + /// + public const string EndRequest = "BeginRequest"; } } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs new file mode 100644 index 0000000000..cf7371a9d4 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs @@ -0,0 +1,55 @@ +// +// 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; +using System.Diagnostics; +using Microsoft.Owin; +using OpenTelemetry.Context.Propagation; + +namespace OpenTelemetry.Contrib.Instrumentation.Owin +{ + /// + /// Options for requests instrumentation. + /// + public class OwinInstrumentationOptions + { + /// + /// Gets or sets for context propagation. Default value: . + /// + public TextMapPropagator Propagator { get; set; } = Propagators.DefaultTextMapPropagator; + + /// + /// Gets or sets a Filter function that determines whether or not to collect telemetry about requests on a per request basis. + /// The Filter gets the , and should return a boolean. + /// If Filter returns true, the request is collected. + /// If Filter returns false or throw exception, the request is filtered out. + /// + public Func Filter { get; set; } + + /// + /// Gets or sets an action to enrich the created by the instrumentation. + /// + public Action Enrich { get; set; } + + /// + /// Gets or sets a value indicating whether the exception will be recorded as or not. + /// + /// + /// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md. + /// + public bool RecordException { get; set; } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md new file mode 100644 index 0000000000..838a186f93 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md @@ -0,0 +1,112 @@ +# OWIN Instrumentation for OpenTelemetry .NET + +[![nuget](https://img.shields.io/nuget/v/OpenTelemetry.Contrib.Instrumentation.Own.svg)](https://www.nuget.org/packages/OpenTelemetry.Contrib.Instrumentation.Owin/) + +This is an [Instrumentation +Library](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/glossary.md#instrumentation-library), +which instruments [OWIN/Katana](https://github.com/aspnet/AspNetKatana/) and +collects telemetry about incoming requests. + +## Steps to enable OpenTelemetry.Contrib.Instrumentation.Owin + +An example project is available in the +[examples/owin](../../examples/owin/) folder. + +### Step 1: Install Package + +Add a reference to the +[`OpenTelemetry.Contrib.Instrumentation.Owin`](https://www.nuget.org/packages/opentelemetry.contrib.instrumentation.owin) +package. Also, add any other instrumentations & exporters you will need. + +```shell +dotnet add package OpenTelemetry.Contrib.Instrumentation.Owin +``` + +### Step 2: Configure OWIN middleware + +Call the `UseOpenTelemetry` `IAppBuilder` extension to register OpenTelemetry +middleware which emits diagnostic events from th OWIN pipeline. This should be +done before any other middleware registrations. + +```csharp + using var host = WebApp.Start( + "http://localhost:9000", + appBuilder => + { + appBuilder.UseOpenTelemetry(); + }); +``` + +### Step 3: Configure OpenTelemetry TracerProvider + +Call the `AddOwinInstrumentation` `TracerProviderBuilder` extension to register +OpenTelemetry instrumentation which listens to the OWIN diagnostic events. + +```csharp + using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Owin-Example")) + .AddOwinInstrumentation() + .AddConsoleExporter() + .Build(); +``` + +## Customize OWIN span display names + +The OpenTelemetry OWIN instrumentation will create spans with very generic names +based on the http method of the request. For example: `HTTP GET` or `HTTP POST`. +The reason for this is the [OpenTelemetry Specification http semantic +conventions](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name) +call specifically for low cardinality values and OWIN does not expose any kind +of route template. + +To change the span name set `Activity.Current.DisplayName` to the value you want +to display once a route has been resolved. Here is how this can be done using WebAPI: + +```csharp + using var host = WebApp.Start( + "http://localhost:9000", + appBuilder => + { + appBuilder.UseOpenTelemetry(); + + HttpConfiguration config = new HttpConfiguration(); + + config.MessageHandlers.Add(new ActivityDisplayNameRouteEnrichingHandler()); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional }); + + appBuilder.UseWebApi(config); + }); + + private class ActivityDisplayNameRouteEnrichingHandler : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + try + { + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + finally + { + var activity = Activity.Current; + if (activity != null) + { + var routeData = request.GetRouteData(); + if (routeData != null) + { + activity.DisplayName = routeData.Route.RouteTemplate; + } + } + } + } + } +``` + +## References + +* [Open Web Interface for .NET](http://owin.org/) +* [Katana Project](https://github.com/aspnet/AspNetKatana/) +* [OpenTelemetry Project](https://opentelemetry.io/) diff --git a/src/OpenTelemetry.Instrumentation.Owin/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/TracerProviderBuilderExtensions.cs similarity index 85% rename from src/OpenTelemetry.Instrumentation.Owin/TracerProviderBuilderExtensions.cs rename to src/OpenTelemetry.Contrib.Instrumentation.Owin/TracerProviderBuilderExtensions.cs index a1373dd878..f34ecfa534 100644 --- a/src/OpenTelemetry.Instrumentation.Owin/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/TracerProviderBuilderExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,7 +15,7 @@ // using System; -using OpenTelemetry.Instrumentation.Owin; +using OpenTelemetry.Contrib.Instrumentation.Owin; namespace OpenTelemetry.Trace { @@ -42,9 +42,9 @@ public static TracerProviderBuilder AddOwinInstrumentation( var owinOptions = new OwinInstrumentationOptions(); configureOwinInstrumentationOptions?.Invoke(owinOptions); - builder.AddInstrumentation(activitySource => new OwinInstrumentation(activitySource, owinOptions)); + OwinInstrumentationActivitySource.Options = owinOptions; - return builder; + return builder.AddSource(OwinInstrumentationActivitySource.ActivitySourceName); } } } diff --git a/src/OpenTelemetry.Extensions.Owin/AssemblyInfo.cs b/src/OpenTelemetry.Extensions.Owin/AssemblyInfo.cs deleted file mode 100644 index 63a1b38ade..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/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.Extensions.Owin.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] -#else -[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Owin.Tests")] -#endif diff --git a/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs deleted file mode 100644 index 3c60523475..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/DiagnosticsMiddleware.cs +++ /dev/null @@ -1,159 +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; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Microsoft.Owin; -using OpenTelemetry.Implementation; - -namespace OpenTelemetry -{ - /// - /// Instruments incoming request with System.Diagnostics.Activity and notifies listeners with DiagnosticsSource. - /// - public sealed class DiagnosticsMiddleware : OwinMiddleware - { - private const string ActivityName = "OpenTelemetry.Extensions.Owin.HttpRequestIn"; - private const string ActivityStartKey = ActivityName + ".Start"; - private const string ActivityStopKey = ActivityName + ".Stop"; - - private readonly DiagnosticListener diagnosticListener = new DiagnosticListener("OpenTelemetry.Extensions.Owin"); - private readonly Context context = new Context(); - - /// - /// Initializes a new instance of the class. - /// - /// An optional pointer to the next component. - public DiagnosticsMiddleware(OwinMiddleware next) - : base(next) - { - } - - /// - public override async Task Invoke(IOwinContext owinContext) - { - try - { - this.BeginRequest(owinContext); - await this.Next.Invoke(owinContext).ConfigureAwait(false); - this.RequestEnd(owinContext, null); - } - catch (Exception ex) - { - this.RequestEnd(owinContext, ex); - } - } - - // Based on https://github.com/dotnet/aspnetcore/blob/v5.0.0-rc.2.20475.17/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L37 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void BeginRequest(IOwinContext owinContext) - { - if (OwinExtensionsEventSource.Log.IsEnabled()) - { - this.context.EventLogEnabled = true; - } - - if (this.diagnosticListener.IsEnabled() && this.diagnosticListener.IsEnabled(ActivityName, owinContext)) - { - this.context.Activity = this.StartActivity(owinContext, out var hasDiagnosticListener); - this.context.HasDiagnosticListener = hasDiagnosticListener; - } - } - - // Based on https://github.com/dotnet/aspnetcore/blob/v5.0.0-rc.2.20475.17/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L89 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void RequestEnd(IOwinContext owinContext, Exception exception) - { - var activity = this.context.Activity; - - // Always stop activity if it was started - if (activity != null) - { - this.StopActivity(owinContext, activity, this.context.HasDiagnosticListener); - } - - if (this.context.EventLogEnabled && exception != null) - { - // Non-inline - OwinExtensionsEventSource.Log.UnhandledException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private Activity StartActivity(IOwinContext owinContext, out bool hasDiagnosticListener) - { - hasDiagnosticListener = false; - - var activity = new Activity(ActivityName); - - // Based on https://github.com/microsoft/ApplicationInsights-dotnet/blob/2.15.0/WEB/Src/Web/Web/Implementation/RequestTrackingExtensions.cs#L41 - if (!activity.Extract(owinContext.Request.Headers)) - { - // Force parsing Correlation-Context in absence of Request-Id or traceparent. - owinContext.Request.Headers.ReadActivityBaggage(activity); - } - - this.diagnosticListener.OnActivityImport(activity, owinContext); - - if (this.diagnosticListener.IsEnabled(ActivityStartKey)) - { - hasDiagnosticListener = true; - this.StartActivity(activity, owinContext); - } - else - { - activity.Start(); - } - - return activity; - } - - // These are versions of DiagnosticSource.Start/StopActivity that don't allocate strings per call (see https://github.com/dotnet/corefx/issues/37055) - private void StartActivity(Activity activity, IOwinContext owinContext) - { - activity.Start(); - this.diagnosticListener.Write(ActivityStartKey, owinContext); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void StopActivity(IOwinContext owinContext, Activity activity, bool hasDiagnosticListener) - { - if (hasDiagnosticListener) - { - this.StopActivity(activity, owinContext); - } - else - { - activity.Stop(); - } - } - - private void StopActivity(Activity activity, IOwinContext owinContext) - { - // Stop sets the end time if it was unset, but we want it set before we issue the write - // so we do it now. - if (activity.Duration == TimeSpan.Zero) - { - activity.SetEndTime(DateTime.UtcNow); - } - - this.diagnosticListener.Write(ActivityStopKey, owinContext); - activity.Stop(); // Resets Activity.Current (we want this after the Write) - } - } -} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs deleted file mode 100644 index bb2cc6af22..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/ActivityExtensions.cs +++ /dev/null @@ -1,122 +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.Headers; -using Microsoft.Owin; - -namespace OpenTelemetry.Implementation -{ - internal static class ActivityExtensions - { - /// - /// Maximum length of Correlation-Context header value. - /// - private const int MaxCorrelationContextLength = 1024; - - /// - /// Reads Request-Id and Correlation-Context headers and sets ParentId and Baggage on Activity. - /// - /// Instance of activity that has not been started yet. - /// Request headers collection. - /// true if request was parsed successfully, false - otherwise. - // Based on the code from https://github.com/aspnet/Microsoft.AspNet.TelemetryCorrelation/blob/master/src/Microsoft.AspNet.TelemetryCorrelation/ActivityExtensions.cs#L48. - public static bool Extract(this Activity activity, IHeaderDictionary requestHeaders) - { - if (activity == null) - { - OwinExtensionsEventSource.Log.ActivityExtractionError("activity is null"); - return false; - } - - if (activity.ParentId != null) - { - OwinExtensionsEventSource.Log.ActivityExtractionError("ParentId is already set on activity"); - return false; - } - - if (activity.Id != null) - { - OwinExtensionsEventSource.Log.ActivityExtractionError("Activity is already started"); - return false; - } - - var traceParents = requestHeaders.GetValues(HeaderNames.TraceParent); - if (traceParents == null || traceParents.Count == 0) - { - traceParents = requestHeaders.GetValues(HeaderNames.RequestId); - } - - if (traceParents != null && traceParents.Count > 0 && !string.IsNullOrEmpty(traceParents[0])) - { - // there may be several Request-Id or traceparent headers, but we only read the first one - activity.SetParentId(traceParents[0]); - - var traceStates = requestHeaders.GetValues(HeaderNames.TraceState); - if (traceStates != null && traceStates.Count > 0) - { - if (traceStates.Count == 1 && !string.IsNullOrEmpty(traceStates[0])) - { - activity.TraceStateString = traceStates[0]; - } - else - { - activity.TraceStateString = string.Join(",", traceStates); - } - } - - // Header format - Correlation-Context: key1=value1, key2=value2 - var baggages = requestHeaders.GetValues(HeaderNames.CorrelationContext); - if (baggages != null) - { - int correlationContextLength = -1; - - // there may be several Correlation-Context headers - foreach (var item in baggages) - { - if (correlationContextLength >= MaxCorrelationContextLength) - { - break; - } - - foreach (var pair in item.Split(',')) - { - correlationContextLength += pair.Length + 1; // pair and comma - - if (correlationContextLength >= MaxCorrelationContextLength) - { - break; - } - - if (NameValueHeaderValue.TryParse(pair, out NameValueHeaderValue baggageItem)) - { - activity.AddBaggage(baggageItem.Name, baggageItem.Value); - } - else - { - OwinExtensionsEventSource.Log.HeaderParsingError(HeaderNames.CorrelationContext, pair); - } - } - } - } - - return true; - } - - return false; - } - } -} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs deleted file mode 100644 index b8dccfe801..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderDictionaryExtensions.cs +++ /dev/null @@ -1,100 +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; -using System.Diagnostics; -using System.Linq; -using Microsoft.Owin; - -namespace OpenTelemetry.Implementation -{ - internal static class HeaderDictionaryExtensions - { - private const int CorrelationContextHeaderMaxLength = 8192; - private const int CorrelationContextMaxPairs = 180; - - /// - /// Reads Correlation-Context and populates it on Activity.Baggage following https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/HttpCorrelationProtocol.md#correlation-context. - /// Use this method when you want force parsing Correlation-Context is absence of Request-Id or traceparent. - /// - /// Header collection. - /// Activity to populate baggage on. - // Based on the code from https://github.com/microsoft/ApplicationInsights-dotnet/blob/2.15.0/WEB/Src/Common/WebHeaderCollectionExtensions.cs#L135. - public static void ReadActivityBaggage(this IHeaderDictionary headers, Activity activity) - { - Debug.Assert(headers != null, "Headers must not be null"); - Debug.Assert(activity != null, "Activity must not be null"); - Debug.Assert(!activity.Baggage.Any(), "Baggage must be empty"); - - int itemsCount = 0; - var correlationContexts = headers.GetValues(HeaderNames.CorrelationContext); - if (correlationContexts == null || correlationContexts.Count == 0) - { - return; - } - - int overallLength = 0; - foreach (var cc in correlationContexts) - { - var headerValue = cc.AsSpan(); - int currentLength = 0; - int initialLength = headerValue.Length; - while (itemsCount < CorrelationContextMaxPairs && currentLength < initialLength) - { - var nextSegment = headerValue.Slice(currentLength); - var nextComma = nextSegment.IndexOf(','); - if (nextComma < 0) - { - // last one - nextComma = nextSegment.Length; - } - - if (nextComma == 0) - { - currentLength += 1; - overallLength += 1; - continue; - } - - if (overallLength + nextComma >= CorrelationContextHeaderMaxLength) - { - return; - } - - ReadOnlySpan kvp = nextSegment.Slice(0, nextComma).Trim(); - - var separatorInd = kvp.IndexOf('='); - if (separatorInd > 0 && separatorInd < kvp.Length - 1) - { - var separatorIndNext = kvp.Slice(separatorInd + 1).IndexOf('='); - - // check there is just one '=' in key-value-pair - if (separatorIndNext < 0) - { - var baggageKey = kvp.Slice(0, separatorInd).Trim().ToString(); - var baggageValue = kvp.Slice(separatorInd + 1).Trim().ToString(); - activity.AddBaggage(baggageKey, baggageValue); - itemsCount += 1; - } - } - - currentLength += nextComma + 1; - overallLength += nextComma + 1; - } - } - } - } -} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs deleted file mode 100644 index ec5499a8d0..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/HeaderNames.cs +++ /dev/null @@ -1,27 +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.Implementation -{ - // // Microsoft.Net.Http.Headers headers (from https://github.com/dotnet/aspnetcore/blob/v5.0.0-rc.2.20475.17/src/Http/Headers/src/HeaderNames.cs) - internal static class HeaderNames - { - public static readonly string CorrelationContext = "Correlation-Context"; - public static readonly string RequestId = "Request-Id"; - public static readonly string TraceParent = "traceparent"; - public static readonly string TraceState = "tracestate"; - } -} diff --git a/src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs b/src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs deleted file mode 100644 index 4fafeafe0e..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/Implementation/OwinExtensionsEventSource.cs +++ /dev/null @@ -1,49 +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 System.Runtime.CompilerServices; - -namespace OpenTelemetry.Implementation -{ - /// - /// EventSource events emitted from the project. - /// - [EventSource(Name = "OpenTelemetry-Extensions-Owin")] - internal class OwinExtensionsEventSource : EventSource - { - public static OwinExtensionsEventSource Log = new OwinExtensionsEventSource(); - - [Event(1, Message = "Failed to extract activity, reason '{0}'", Level = EventLevel.Error)] - public void ActivityExtractionError(string reason) - { - this.WriteEvent(1, reason); - } - - [Event(2, Message = "Failed to parse header '{0}', value: '{1}'", Level = EventLevel.Informational)] - public void HeaderParsingError(string headerName, string headerValue) - { - this.WriteEvent(2, headerName, headerValue); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - [Event(3, Level = EventLevel.Error)] - public void UnhandledException() - { - this.WriteEvent(3); - } - } -} diff --git a/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj b/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj deleted file mode 100644 index fd6e0b24ac..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/OpenTelemetry.Extensions.Owin.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - net461;netstandard2.0 - A component to the OWIN pipeline for instrumenting incoming request with System.Diagnostics.Activity and notifying listeners with DiagnosticsSource. - OpenTelemetry - - - - - - - - - - - - diff --git a/src/OpenTelemetry.Extensions.Owin/README.md b/src/OpenTelemetry.Extensions.Owin/README.md deleted file mode 100644 index 22753806e0..0000000000 --- a/src/OpenTelemetry.Extensions.Owin/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Telemetry correlation library for OWIN/Katana - -[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Extensions.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Extensions.Owin) -[![NuGet](https://img.shields.io/nuget/dt/OOpenTelemetry.Extensions.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Extensions.Owin) - -This is an [Instrumentation -Library](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/glossary.md#instrumentation-library), -which instruments the [OWIN/Katana](https://github.com/aspnet/AspNetKatana/) -and notifies listeners about incoming web requests. - -## Steps to enable OpenTelemetry.Extensions.Owin - -### Step 1: Install Package - -Add a reference to the -[`OpenTelemetry.Extensions.Owin`](https://www.nuget.org/packages/opentelemetry.extensions.owin) -package. Also, add any other instrumentations & exporters you will need. - -```shell -dotnet add package OpenTelemetry.Extensions.Owin -``` - -## References - -* [Open Web Interface for .NET](http://owin.org/) -* [Katana Project](https://github.com/aspnet/AspNetKatana/) -* [OpenTelemetry Project](https://opentelemetry.io/) diff --git a/src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md deleted file mode 100644 index 134621e04d..0000000000 --- a/src/OpenTelemetry.Instrumentation.Owin/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -# Changelog - -## Unreleased - -* Initial release diff --git a/src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.cs deleted file mode 100644 index 442e513556..0000000000 --- a/src/OpenTelemetry.Instrumentation.Owin/Implementation/HttpInListener.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. -// - -using System; -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Instrumentation.Owin.Implementation -{ - internal class HttpInListener : ListenerHandler - { - private readonly OwinInstrumentationOptions options; - private readonly ActivitySourceAdapter activitySource; - - public HttpInListener(string name, OwinInstrumentationOptions options, ActivitySourceAdapter activitySource) - : base(name) - { - this.options = options ?? throw new ArgumentNullException(nameof(options)); - this.activitySource = activitySource; - } - } -} diff --git a/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj b/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj deleted file mode 100644 index 28d79344a4..0000000000 --- a/src/OpenTelemetry.Instrumentation.Owin/OpenTelemetry.Instrumentation.Owin.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - net461;netstandard2.0 - OWIN instrumentation for OpenTelemetry .NET - $(PackageTags);distributed-tracing;OWIN - - - - - - - - - - - - diff --git a/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.cs deleted file mode 100644 index a18aef0d2e..0000000000 --- a/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentation.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 System; -using OpenTelemetry.Instrumentation.Owin.Implementation; -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Instrumentation.Owin -{ - /// - /// OWIN Requests instrumentation. - /// - internal class OwinInstrumentation : IDisposable - { - private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber; - - /// - /// Initializes a new instance of the class. - /// - /// ActivitySource adapter instance. - /// Configuration options for OWIN instrumentation. - public OwinInstrumentation(ActivitySourceAdapter activitySource, OwinInstrumentationOptions options) - { - this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpInListener("OpenTelemetry.Extensions.Owin", options, activitySource), null); - this.diagnosticSourceSubscriber.Subscribe(); - } - - /// - public void Dispose() - { - this.diagnosticSourceSubscriber?.Dispose(); - } - } -} diff --git a/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs deleted file mode 100644 index cdd0735e53..0000000000 --- a/src/OpenTelemetry.Instrumentation.Owin/OwinInstrumentationOptions.cs +++ /dev/null @@ -1,25 +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.Instrumentation.Owin -{ - /// - /// Options for requests instrumentation. - /// - public class OwinInstrumentationOptions - { - } -} diff --git a/src/OpenTelemetry.Instrumentation.Owin/README.md b/src/OpenTelemetry.Instrumentation.Owin/README.md deleted file mode 100644 index 70ab5bb494..0000000000 --- a/src/OpenTelemetry.Instrumentation.Owin/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# OWIN Instrumentation for OpenTelemetry - -[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Owin) -[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.Owin.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Owin) - -This is an [Instrumentation -Library](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/glossary.md#instrumentation-library), -which instruments [OWIN/Katana](https://github.com/aspnet/AspNetKatana/) and -collect telemetry about incoming web requests. - -## Steps to enable OpenTelemetry.Instrumentation.Owin - -### Step 1: Install Package - -Add a reference to the -[`OpenTelemetry.Instrumentation.Owin`](https://www.nuget.org/packages/opentelemetry.instrumentation.owin) -package. Also, add any other instrumentations & exporters you will need. - -```shell -dotnet add package OpenTelemetry.Instrumentation.Owin -``` - -## References - -* [Open Web Interface for .NET](http://owin.org/) -* [Katana Project](https://github.com/aspnet/AspNetKatana/) -* [OpenTelemetry Project](https://opentelemetry.io/) From 4fbf13b07acbab92badfabfc4f4b9900a950babe Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 19 Sep 2021 22:11:52 -0700 Subject: [PATCH 05/14] README updates. --- examples/owin/README.md | 7 +++++++ src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/owin/README.md b/examples/owin/README.md index e69de29bb2..d50d54a978 100644 --- a/examples/owin/README.md +++ b/examples/owin/README.md @@ -0,0 +1,7 @@ +# OWIN Instrumentation for OpenTelemetry .NET - Example + +An example application that shows how to use +`OpenTelemetry.Contrib.Instrumentation.Owin` to capture telemetry from a +self-hosted WebAPI .NET Framework service. + +Span names are set to the route template resolved by WebAPI. \ No newline at end of file diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md index 838a186f93..e4663a28b5 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md @@ -50,7 +50,7 @@ OpenTelemetry instrumentation which listens to the OWIN diagnostic events. .Build(); ``` -## Customize OWIN span display names +## Customize OWIN span names The OpenTelemetry OWIN instrumentation will create spans with very generic names based on the http method of the request. For example: `HTTP GET` or `HTTP POST`. From 629bbfd1209d8f2defbdc6ab7769e7fd005505df Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 19 Sep 2021 22:27:19 -0700 Subject: [PATCH 06/14] Re-throw exception. --- .../Implementation/DiagnosticsMiddleware.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs index fffaca47a7..3919fc6682 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs @@ -55,6 +55,7 @@ public override async Task Invoke(IOwinContext owinContext) catch (Exception ex) { RequestEnd(owinContext, ex); + throw; } } From 07b2bb59d3319cb2536b4dc671d383d565b3fb1b Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 21 Sep 2021 11:39:25 -0700 Subject: [PATCH 07/14] Unit tests and bug fixes. --- opentelemetry-dotnet-contrib.sln | 7 + .../AssemblyInfo.cs | 4 +- .../OwinInstrumentationActivitySource.cs | 1 - .../OwinEnrichEventNames.cs | 2 +- .../Controllers/TestController.cs | 30 +++ .../DiagnosticsMiddlewareTests.cs | 227 ++++++++++++++++++ ....Contrib.Instrumentation.Owin.Tests.csproj | 27 +++ 7 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/Controllers/TestController.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/OpenTelemetry.Contrib.Instrumentation.Owin.Tests.csproj diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index fbc71143a3..8cf3585a72 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -150,6 +150,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "owin", "owin", "{8D11A34C-D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Owin", "examples\owin\Examples.Owin.csproj", "{6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.Owin.Tests", "test\OpenTelemetry.Contrib.Instrumentation.Owin.Tests\OpenTelemetry.Contrib.Instrumentation.Owin.Tests.csproj", "{D52558C8-B7BF-4F59-A0FA-9AA629E68012}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -288,6 +290,10 @@ Global {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}.Debug|Any CPU.Build.0 = Debug|Any CPU {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423}.Release|Any CPU.Build.0 = Release|Any CPU + {D52558C8-B7BF-4F59-A0FA-9AA629E68012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D52558C8-B7BF-4F59-A0FA-9AA629E68012}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D52558C8-B7BF-4F59-A0FA-9AA629E68012}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D52558C8-B7BF-4F59-A0FA-9AA629E68012}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -332,6 +338,7 @@ Global {530255C1-D130-4B43-981D-911E54F7C787} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {8D11A34C-D0EF-4DE1-8230-32168E67044D} = {B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA} {6B3AA3F2-89A7-433F-918A-1E5E6AAF8423} = {8D11A34C-D0EF-4DE1-8230-32168E67044D} + {D52558C8-B7BF-4F59-A0FA-9AA629E68012} = {2097345F-4DD3-477D-BC54-A922F9B2B402} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs index bbe3b27636..21e046ef2a 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ using System.Runtime.CompilerServices; #if SIGNED -[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.Owin.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] +[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.Instrumentation.Owin.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] #else [assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.Owin.Tests")] #endif diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs index f34b857336..60bfb6e358 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationActivitySource.cs @@ -23,7 +23,6 @@ internal static class OwinInstrumentationActivitySource { public const string ActivitySourceName = "OpenTelemetry.OWIN"; public const string IncomingRequestActivityName = ActivitySourceName + ".IncomingRequest"; - public const string OutgoingRequestActivityName = ActivitySourceName + ".OutgoingRequest"; private static readonly Version Version = typeof(OwinInstrumentationActivitySource).Assembly.GetName().Version; diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs index 0bc4c74e16..4d58e506ad 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs @@ -29,6 +29,6 @@ public class OwinEnrichEventNames /// /// End request. /// - public const string EndRequest = "BeginRequest"; + public const string EndRequest = "EndRequest"; } } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/Controllers/TestController.cs b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/Controllers/TestController.cs new file mode 100644 index 0000000000..661126c47d --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/Controllers/TestController.cs @@ -0,0 +1,30 @@ +// +// 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; +using System.Web.Http; + +namespace OpenTelemetry.Contrib.Instrumentation.Owin.Tests.Controllers +{ + public class TestController : ApiController + { + // GET api/test/{id} + public string Get(string id = null) + { + return $"id:{id}"; + } + } +} diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs new file mode 100644 index 0000000000..e57d5a115f --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs @@ -0,0 +1,227 @@ +// +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.Owin; +using Microsoft.Owin.Hosting; +using OpenTelemetry.Trace; +using Owin; +using Xunit; + +namespace OpenTelemetry.Contrib.Instrumentation.Owin.Tests +{ + public class DiagnosticsMiddlewareTests : IDisposable + { + private readonly Uri serviceBaseUri; + private readonly IDisposable listener; + private readonly EventWaitHandle requestCompleteHandle = new (false, EventResetMode.AutoReset); + + public DiagnosticsMiddlewareTests() + { + Random random = new Random(); + var retryCount = 5; + while (retryCount > 0) + { + try + { + this.serviceBaseUri = new Uri($"http://localhost:{random.Next(2000, 5000)}/"); + + this.listener = WebApp.Start( + this.serviceBaseUri.ToString(), + appBuilder => + { + appBuilder.Use((context, next) => + { + try + { + return next(); + } + finally + { + this.requestCompleteHandle?.Set(); + } + }); + + appBuilder.UseOpenTelemetry(); + + appBuilder.Use((context, next) => + { + if (context.Request.Path == new PathString("/exception")) + { + context.Response.StatusCode = 500; + throw new InvalidOperationException("Unhandled exception requested by caller."); + } + + return next(); + }); + + HttpConfiguration config = new HttpConfiguration(); + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional }); + + appBuilder.UseWebApi(config); + }); + break; + } + catch + { + this.listener.Dispose(); + this.listener = null; + retryCount--; + } + } + + if (this.listener == null) + { + throw new InvalidOperationException("HttpListener could not be started."); + } + } + + public void Dispose() + { + this.listener?.Dispose(); + this.requestCompleteHandle?.Dispose(); + } + + [Theory] + [InlineData(true, false)] + [InlineData(true, true)] + [InlineData(false)] + [InlineData(true, false, true)] + [InlineData(true, false, true, true)] + [InlineData(true, false, false, false, true)] + [InlineData(true, false, false, false, true, true)] + public async Task OutgoingRequestInstrumentationTest( + bool instrument, + bool filter = false, + bool enrich = false, + bool enrichmentException = false, + bool generateRemoteException = false, + bool recordException = false) + { + List stoppedActivities = new List(); + + var builder = Sdk.CreateTracerProviderBuilder() + .AddInMemoryExporter(stoppedActivities); + + if (instrument) + { + builder + .AddOwinInstrumentation(options => + { + if (enrich) + { + if (!enrichmentException) + { + options.Enrich = (activity, eventName, context, exception) => + { + switch (eventName) + { + case OwinEnrichEventNames.BeginRequest: + activity.SetTag("client.beginrequest", OwinEnrichEventNames.BeginRequest); + break; + case OwinEnrichEventNames.EndRequest: + activity.SetTag("client.endrequest", OwinEnrichEventNames.EndRequest); + break; + } + }; + } + else + { + options.Enrich = (activity, eventName, context, exception) => throw new Exception("Error while enriching activity"); + } + } + + options.Filter = _ => !filter; + options.RecordException = recordException; + }); + } + + using TracerProvider tracerProvider = builder.Build(); + + using HttpClient client = new HttpClient(); + + Uri requestUri = generateRemoteException + ? new Uri($"{this.serviceBaseUri}exception") + : new Uri($"{this.serviceBaseUri}api/test"); + + this.requestCompleteHandle.Reset(); + + using var response = await client.GetAsync(requestUri).ConfigureAwait(false); + + /* Note: This code will continue executing as soon as the response + is available but Owin could still be working. We need to wait until + Owin has finished to inspect the activity status. */ + + Assert.True(this.requestCompleteHandle.WaitOne(3000)); + + if (instrument) + { + if (!filter) + { + Assert.NotEmpty(stoppedActivities); + Assert.Single(stoppedActivities); + + Activity activity = stoppedActivities[0]; + Assert.Equal(OwinInstrumentationActivitySource.IncomingRequestActivityName, activity.OperationName); + + Assert.Equal(requestUri.Host + ":" + requestUri.Port, activity.TagObjects.FirstOrDefault(t => t.Key == SemanticConventions.AttributeHttpHost).Value); + Assert.Equal("GET", activity.TagObjects.FirstOrDefault(t => t.Key == SemanticConventions.AttributeHttpMethod).Value); + Assert.Equal(requestUri.AbsolutePath, activity.TagObjects.FirstOrDefault(t => t.Key == SemanticConventions.AttributeHttpTarget).Value); + Assert.Equal(requestUri.ToString(), activity.TagObjects.FirstOrDefault(t => t.Key == SemanticConventions.AttributeHttpUrl).Value); + + Assert.Equal(generateRemoteException ? 500 : 200, activity.TagObjects.FirstOrDefault(t => t.Key == SemanticConventions.AttributeHttpStatusCode).Value); + if (generateRemoteException) + { + Assert.Equal(Status.Error, activity.GetStatus()); + + if (recordException) + { + Assert.Contains(activity.Events, ae => ae.Name == SemanticConventions.AttributeExceptionEventName); + } + } + else + { + Assert.Equal(Status.Unset, activity.GetStatus()); + } + + if (enrich && !enrichmentException) + { + Assert.Equal(OwinEnrichEventNames.BeginRequest, activity.TagObjects.Single(t => t.Key == "client.beginrequest").Value); + Assert.Equal(OwinEnrichEventNames.EndRequest, activity.TagObjects.Single(t => t.Key == "client.endrequest").Value); + } + } + else + { + Assert.Empty(stoppedActivities); + } + } + else + { + Assert.Empty(stoppedActivities); + } + } + } +} diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/OpenTelemetry.Contrib.Instrumentation.Owin.Tests.csproj b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/OpenTelemetry.Contrib.Instrumentation.Owin.Tests.csproj new file mode 100644 index 0000000000..7b9472399a --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/OpenTelemetry.Contrib.Instrumentation.Owin.Tests.csproj @@ -0,0 +1,27 @@ + + + + Unit test project for OpenTelemetry OWIN instrumentation + net461 + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + + + From 46c998963d405711699d92cc050319037165f4d6 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 22 Sep 2021 12:32:47 -0700 Subject: [PATCH 08/14] dotnet format fixes --- build/Common.props | 2 +- .../AWSXRayIdGenerator.cs | 2 +- .../DiagnosticsMiddlewareTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/Common.props b/build/Common.props index dac6c3f051..275cfee36c 100644 --- a/build/Common.props +++ b/build/Common.props @@ -29,7 +29,7 @@ [4.1.1] [3.3.2] [1.0.0,2.0) - [1.1.118,2.0) + [1.2.0-beta.354,2.0) diff --git a/src/OpenTelemetry.Contrib.Extensions.AWSXRay/AWSXRayIdGenerator.cs b/src/OpenTelemetry.Contrib.Extensions.AWSXRay/AWSXRayIdGenerator.cs index 770fe9d18a..5e05d14436 100644 --- a/src/OpenTelemetry.Contrib.Extensions.AWSXRay/AWSXRayIdGenerator.cs +++ b/src/OpenTelemetry.Contrib.Extensions.AWSXRay/AWSXRayIdGenerator.cs @@ -179,7 +179,7 @@ private static ActivitySamplingResult ComputeRootActivitySamplingResult( { SamplingDecision.RecordAndSample => ActivitySamplingResult.AllDataAndRecorded, SamplingDecision.RecordOnly => ActivitySamplingResult.AllData, - _ => ActivitySamplingResult.PropagationData + _ => ActivitySamplingResult.PropagationData, }; if (activitySamplingResult != ActivitySamplingResult.PropagationData) diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs index e57d5a115f..e5e11e3c9d 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs @@ -34,7 +34,7 @@ public class DiagnosticsMiddlewareTests : IDisposable { private readonly Uri serviceBaseUri; private readonly IDisposable listener; - private readonly EventWaitHandle requestCompleteHandle = new (false, EventResetMode.AutoReset); + private readonly EventWaitHandle requestCompleteHandle = new(false, EventResetMode.AutoReset); public DiagnosticsMiddlewareTests() { From 68ec31a13e3928b08d491b7fed9e9c4d7cd7c758 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 22 Sep 2021 12:33:19 -0700 Subject: [PATCH 09/14] MD lint --- examples/owin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/owin/README.md b/examples/owin/README.md index d50d54a978..db8d94a0c8 100644 --- a/examples/owin/README.md +++ b/examples/owin/README.md @@ -4,4 +4,4 @@ An example application that shows how to use `OpenTelemetry.Contrib.Instrumentation.Owin` to capture telemetry from a self-hosted WebAPI .NET Framework service. -Span names are set to the route template resolved by WebAPI. \ No newline at end of file +Span names are set to the route template resolved by WebAPI. From 1a375b63ea7c215f5ff48cfdd16da2c1163ab737 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 22 Sep 2021 13:15:17 -0700 Subject: [PATCH 10/14] Warning cleanup + switch to enum for enrich events --- .../AssemblyInfo.cs | 4 ++++ .../Implementation/DiagnosticsMiddleware.cs | 10 ++++++++-- ...winEnrichEventNames.cs => OwinEnrichEventType.cs} | 12 +++++++----- .../OwinInstrumentationOptions.cs | 2 +- .../DiagnosticsMiddlewareTests.cs | 12 ++++++------ 5 files changed, 26 insertions(+), 14 deletions(-) rename src/OpenTelemetry.Contrib.Instrumentation.Owin/{OwinEnrichEventNames.cs => OwinEnrichEventType.cs} (74%) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs index 21e046ef2a..f4836c41f8 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/AssemblyInfo.cs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // + +using System; using System.Runtime.CompilerServices; #if SIGNED @@ -20,3 +22,5 @@ #else [assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.Owin.Tests")] #endif + +[assembly: CLSCompliant(false)] diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs index 3919fc6682..154866d090 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs @@ -70,7 +70,9 @@ private static void BeginRequest(IOwinContext owinContext) return; } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { OwinInstrumentationEventSource.Log.RequestFilterException(ex); return; @@ -127,11 +129,13 @@ private static void BeginRequest(IOwinContext owinContext) { OwinInstrumentationActivitySource.Options.Enrich?.Invoke( activity, - OwinEnrichEventNames.BeginRequest, + OwinEnrichEventType.BeginRequest, owinContext, null); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { OwinInstrumentationEventSource.Log.EnrichmentException(ex); } @@ -181,11 +185,13 @@ private static void RequestEnd(IOwinContext owinContext, Exception exception) { OwinInstrumentationActivitySource.Options.Enrich?.Invoke( activity, - OwinEnrichEventNames.EndRequest, + OwinEnrichEventType.EndRequest, owinContext, exception); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { OwinInstrumentationEventSource.Log.EnrichmentException(ex); } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventType.cs similarity index 74% rename from src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs rename to src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventType.cs index 4d58e506ad..9d5e3260c3 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventNames.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinEnrichEventType.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,21 +14,23 @@ // limitations under the License. // +using System.Diagnostics; + namespace OpenTelemetry.Contrib.Instrumentation.Owin { /// - /// Constants used for event names when enriching an activity. + /// Describes the possible events fired when enriching an . /// - public class OwinEnrichEventNames + public enum OwinEnrichEventType { /// /// Begin request. /// - public const string BeginRequest = "BeginRequest"; + BeginRequest, /// /// End request. /// - public const string EndRequest = "EndRequest"; + EndRequest, } } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs index cf7371a9d4..c7fbb9ca07 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs @@ -42,7 +42,7 @@ public class OwinInstrumentationOptions /// /// Gets or sets an action to enrich the created by the instrumentation. /// - public Action Enrich { get; set; } + public Action Enrich { get; set; } /// /// Gets or sets a value indicating whether the exception will be recorded as or not. diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs index e5e11e3c9d..858232aa5e 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Owin.Tests/DiagnosticsMiddlewareTests.cs @@ -139,11 +139,11 @@ public async Task OutgoingRequestInstrumentationTest( { switch (eventName) { - case OwinEnrichEventNames.BeginRequest: - activity.SetTag("client.beginrequest", OwinEnrichEventNames.BeginRequest); + case OwinEnrichEventType.BeginRequest: + activity.SetTag("client.beginrequest", nameof(OwinEnrichEventType.BeginRequest)); break; - case OwinEnrichEventNames.EndRequest: - activity.SetTag("client.endrequest", OwinEnrichEventNames.EndRequest); + case OwinEnrichEventType.EndRequest: + activity.SetTag("client.endrequest", nameof(OwinEnrichEventType.EndRequest)); break; } }; @@ -209,8 +209,8 @@ Owin has finished to inspect the activity status. */ if (enrich && !enrichmentException) { - Assert.Equal(OwinEnrichEventNames.BeginRequest, activity.TagObjects.Single(t => t.Key == "client.beginrequest").Value); - Assert.Equal(OwinEnrichEventNames.EndRequest, activity.TagObjects.Single(t => t.Key == "client.endrequest").Value); + Assert.Equal(nameof(OwinEnrichEventType.BeginRequest), activity.TagObjects.Single(t => t.Key == "client.beginrequest").Value); + Assert.Equal(nameof(OwinEnrichEventType.EndRequest), activity.TagObjects.Single(t => t.Key == "client.endrequest").Value); } } else From 846f1ea233bc59414d8727a96798b89dba63d5c1 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 22 Sep 2021 13:21:39 -0700 Subject: [PATCH 11/14] MD lint 2 --- src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md index e4663a28b5..c60f2c6135 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/README.md @@ -83,7 +83,9 @@ to display once a route has been resolved. Here is how this can be done using We private class ActivityDisplayNameRouteEnrichingHandler : DelegatingHandler { - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) { try { From 864cf968f7eb8cfa555ad31513a5e19828c61813 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 28 Sep 2021 09:56:39 -0700 Subject: [PATCH 12/14] Added comment about pipeline placement. --- examples/owin/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/owin/Program.cs b/examples/owin/Program.cs index 225a4a078f..9ee8b2ff03 100644 --- a/examples/owin/Program.cs +++ b/examples/owin/Program.cs @@ -36,6 +36,8 @@ public static void Main() "http://localhost:9000", appBuilder => { + // Add OpenTelemetry early in the pipeline to start timing + // the request as soon as possible. appBuilder.UseOpenTelemetry(); HttpConfiguration config = new HttpConfiguration(); From 950d5f4ae692b528001172f2921d4fc2b5b00ea0 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 28 Sep 2021 12:36:14 -0700 Subject: [PATCH 13/14] Removed the propagator on owin options. --- .../Implementation/DiagnosticsMiddleware.cs | 4 ++-- .../OwinInstrumentationOptions.cs | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs index 154866d090..058500e658 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/DiagnosticsMiddleware.cs @@ -78,7 +78,7 @@ private static void BeginRequest(IOwinContext owinContext) return; } - var textMapPropagator = OwinInstrumentationActivitySource.Options.Propagator; + var textMapPropagator = Propagators.DefaultTextMapPropagator; var ctx = textMapPropagator.Extract(default, owinContext.Request, OwinRequestHeaderValuesGetter); Activity activity = OwinInstrumentationActivitySource.ActivitySource.StartActivity( @@ -199,7 +199,7 @@ private static void RequestEnd(IOwinContext owinContext, Exception exception) activity.Stop(); - if (!(OwinInstrumentationActivitySource.Options.Propagator is TraceContextPropagator)) + if (!(Propagators.DefaultTextMapPropagator is TraceContextPropagator)) { Baggage.Current = default; } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs index c7fbb9ca07..0739e587fd 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/OwinInstrumentationOptions.cs @@ -26,11 +26,6 @@ namespace OpenTelemetry.Contrib.Instrumentation.Owin /// public class OwinInstrumentationOptions { - /// - /// Gets or sets for context propagation. Default value: . - /// - public TextMapPropagator Propagator { get; set; } = Propagators.DefaultTextMapPropagator; - /// /// Gets or sets a Filter function that determines whether or not to collect telemetry about requests on a per request basis. /// The Filter gets the , and should return a boolean. From 09251a0f79d1be774bc44a7cb330db26add3cc55 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 28 Sep 2021 22:45:46 -0700 Subject: [PATCH 14/14] Sealed OwinInstrumentationEventSource. --- .../Implementation/OwinInstrumentationEventSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs index d96d268794..021d137d90 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Owin/Implementation/OwinInstrumentationEventSource.cs @@ -25,7 +25,7 @@ namespace OpenTelemetry.Contrib.Instrumentation.Owin /// EventSource events emitted from the project. /// [EventSource(Name = "OpenTelemetry-Instrumentation-Owin")] - internal class OwinInstrumentationEventSource : EventSource + internal sealed class OwinInstrumentationEventSource : EventSource { public static OwinInstrumentationEventSource Log { get; } = new OwinInstrumentationEventSource();