From da0419e139e0e7b92a5bf0a32305d0fb4b632109 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Thu, 25 Feb 2021 17:41:01 -0600 Subject: [PATCH 01/10] Upgrading Elasticsearch to OTel version to 1.0.1 --- .../Constants.cs | 31 -- .../ElasticsearchClientInstrumentation.cs | 11 +- .../Helpers/ActivityHelperExtensions.cs | 332 ++++++++++++++++++ .../Helpers/ActivityInstrumentationHelper.cs | 44 +++ .../Helpers/DiagnosticSourceListener.cs | 74 ++++ .../Helpers/DiagnosticSourceSubscriber.cs | 112 ++++++ .../Helpers/EnumerationHelper.cs | 180 ++++++++++ .../Helpers/ExceptionExtensions.cs | 46 +++ .../Helpers/IActivityEnumerator.cs | 34 ++ .../Helpers/InstrumentationEventSource.cs | 52 +++ .../Helpers/ListenerHandler.cs | 81 +++++ .../MultiTypePropertyFetcher.cs | 0 .../Helpers/SemanticConventions.cs | 115 ++++++ .../Helpers/SpanAttributeConstants.cs | 34 ++ .../Helpers/SpanHelper.cs | 40 +++ .../Helpers/StatusHelper.cs | 63 ++++ .../ElasticsearchActivitySourceHelper.cs | 37 ++ ...searchRequestPipelineDiagnosticListener.cs | 47 +-- ...Instrumentation.ElasticsearchClient.csproj | 4 +- .../TracerProviderBuilderExtensions.cs | 4 +- .../ElasticsearchClientTests.cs | 180 +++++----- 21 files changed, 1368 insertions(+), 153 deletions(-) delete mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Constants.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityHelperExtensions.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityInstrumentationHelper.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceListener.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceSubscriber.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/EnumerationHelper.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ExceptionExtensions.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/IActivityEnumerator.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/InstrumentationEventSource.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ListenerHandler.cs rename src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/{Implementation => Helpers}/MultiTypePropertyFetcher.cs (100%) create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SemanticConventions.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanAttributeConstants.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanHelper.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/StatusHelper.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Constants.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Constants.cs deleted file mode 100644 index 842172b06b..0000000000 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Constants.cs +++ /dev/null @@ -1,31 +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.Contrib.Instrumentation.ElasticsearchClient -{ - internal class Constants - { - public const string ExceptionCustomPropertyName = "OTel.Elasticsearch.Exception"; - public const string AttributeDbSystem = "db.system"; - public const string AttributeDbName = "db.name"; - public const string AttributeNetPeerIp = "net.peer.ip"; - public const string AttributeNetPeerName = "net.peer.name"; - public const string AttributeNetPeerPort = "net.peer.port"; - public const string AttributeDbMethod = "db.method"; - public const string AttributeDbUrl = "db.url"; - public const string AttributeDbStatement = "db.statement"; - } -} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentation.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentation.cs index 499dc46f71..af42fdcfa7 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentation.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentation.cs @@ -16,7 +16,6 @@ using System; using OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Implementation; using OpenTelemetry.Instrumentation; -using OpenTelemetry.Trace; namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient { @@ -30,16 +29,10 @@ internal class ElasticsearchClientInstrumentation : IDisposable /// /// Initializes a new instance of the class. /// - /// ActivitySource adapter instance. /// Configuration options for Elasticsearch client instrumentation. - public ElasticsearchClientInstrumentation(ActivitySourceAdapter activitySource, ElasticsearchClientInstrumentationOptions options) + public ElasticsearchClientInstrumentation(ElasticsearchClientInstrumentationOptions options) { - if (activitySource == null) - { - throw new ArgumentNullException(nameof(activitySource)); - } - - this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new ElasticsearchRequestPipelineDiagnosticListener(activitySource, options), null); + this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new ElasticsearchRequestPipelineDiagnosticListener(options), null); this.diagnosticSourceSubscriber.Subscribe(); } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityHelperExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityHelperExtensions.cs new file mode 100644 index 0000000000..cdd5d92cd0 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityHelperExtensions.cs @@ -0,0 +1,332 @@ +// +// 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.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Trace +{ + /// + /// Extension methods on Activity. + /// + internal static class ActivityHelperExtensions + { + /// + /// Gets the status of activity execution. + /// Activity class in .NET does not support 'Status'. + /// This extension provides a workaround to retrieve Status from special tags with key name otel.status_code and otel.status_description. + /// + /// Activity instance. + /// . + /// Status description. + /// if was found on the supplied Activity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] + public static bool TryGetStatus(this Activity activity, out StatusCode statusCode, out string statusDescription) + { + Debug.Assert(activity != null, "Activity should not be null"); + + ActivityStatusTagEnumerator state = default; + + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + + if (!state.StatusCode.HasValue) + { + statusCode = default; + statusDescription = null; + return false; + } + + statusCode = state.StatusCode.Value; + statusDescription = state.StatusDescription; + return true; + } + + /// + /// Gets the value of a specific tag on an . + /// + /// Activity instance. + /// Case-sensitive tag name to retrieve. + /// Tag value or null if a match was not found. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] + public static object GetTagValue(this Activity activity, string tagName) + { + Debug.Assert(activity != null, "Activity should not be null"); + + ActivitySingleTagEnumerator state = new ActivitySingleTagEnumerator(tagName); + + ActivityTagsEnumeratorFactory.Enumerate(activity, ref state); + + return state.Value; + } + + /// + /// Enumerates all the key/value pairs on an without performing an allocation. + /// + /// The struct implementation to use for the enumeration. + /// Activity instance. + /// Tag enumerator. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] + public static void EnumerateTags(this Activity activity, ref T tagEnumerator) + where T : struct, IActivityEnumerator> + { + Debug.Assert(activity != null, "Activity should not be null"); + + ActivityTagsEnumeratorFactory.Enumerate(activity, ref tagEnumerator); + } + + /// + /// Enumerates all the s on an without performing an allocation. + /// + /// The struct implementation to use for the enumeration. + /// Activity instance. + /// Link enumerator. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] + public static void EnumerateLinks(this Activity activity, ref T linkEnumerator) + where T : struct, IActivityEnumerator + { + Debug.Assert(activity != null, "Activity should not be null"); + + ActivityLinksEnumeratorFactory.Enumerate(activity, ref linkEnumerator); + } + + /// + /// Enumerates all the key/value pairs on an without performing an allocation. + /// + /// The struct implementation to use for the enumeration. + /// ActivityLink instance. + /// Tag enumerator. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] + public static void EnumerateTags(this ActivityLink activityLink, ref T tagEnumerator) + where T : struct, IActivityEnumerator> + { + ActivityTagsEnumeratorFactory.Enumerate(activityLink, ref tagEnumerator); + } + + /// + /// Enumerates all the s on an without performing an allocation. + /// + /// The struct implementation to use for the enumeration. + /// Activity instance. + /// Event enumerator. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] + public static void EnumerateEvents(this Activity activity, ref T eventEnumerator) + where T : struct, IActivityEnumerator + { + Debug.Assert(activity != null, "Activity should not be null"); + + ActivityEventsEnumeratorFactory.Enumerate(activity, ref eventEnumerator); + } + + /// + /// Enumerates all the key/value pairs on an without performing an allocation. + /// + /// The struct implementation to use for the enumeration. + /// ActivityEvent instance. + /// Tag enumerator. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")] + public static void EnumerateTags(this ActivityEvent activityEvent, ref T tagEnumerator) + where T : struct, IActivityEnumerator> + { + ActivityTagsEnumeratorFactory.Enumerate(activityEvent, ref tagEnumerator); + } + + private struct ActivitySingleTagEnumerator : IActivityEnumerator> + { + public object Value; + + private readonly string tagName; + + public ActivitySingleTagEnumerator(string tagName) + { + this.tagName = tagName; + this.Value = null; + } + + public bool ForEach(KeyValuePair item) + { + if (item.Key == this.tagName) + { + this.Value = item.Value; + return false; + } + + return true; + } + } + + private struct ActivityStatusTagEnumerator : IActivityEnumerator> + { + public StatusCode? StatusCode; + + public string StatusDescription; + + public bool ForEach(KeyValuePair item) + { + switch (item.Key) + { + case SpanAttributeConstants.StatusCodeKey: + this.StatusCode = StatusHelper.GetStatusCodeForTagValue(item.Value as string); + break; + case SpanAttributeConstants.StatusDescriptionKey: + this.StatusDescription = item.Value as string; + break; + } + + return !this.StatusCode.HasValue || this.StatusDescription == null; + } + } + + private static class ActivityTagsEnumeratorFactory + where TState : struct, IActivityEnumerator> + { + private static readonly object EmptyActivityTagObjects = typeof(Activity).GetField("s_emptyTagObjects", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); + + private static readonly object EmptyActivityEventTags = typeof(ActivityEvent).GetField("s_emptyTags", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); + + private static readonly DictionaryEnumerator.AllocationFreeForEachDelegate + ActivityTagObjectsEnumerator = DictionaryEnumerator.BuildAllocationFreeForEachDelegate( + typeof(Activity).GetField("_tags", BindingFlags.Instance | BindingFlags.NonPublic).FieldType); + + private static readonly DictionaryEnumerator.AllocationFreeForEachDelegate + ActivityTagsCollectionEnumerator = DictionaryEnumerator.BuildAllocationFreeForEachDelegate(typeof(ActivityTagsCollection)); + + private static readonly DictionaryEnumerator.ForEachDelegate ForEachTagValueCallbackRef = ForEachTagValueCallback; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Enumerate(Activity activity, ref TState state) + { + var tagObjects = activity.TagObjects; + + if (ReferenceEquals(tagObjects, EmptyActivityTagObjects)) + { + return; + } + + ActivityTagObjectsEnumerator( + tagObjects, + ref state, + ForEachTagValueCallbackRef); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Enumerate(ActivityLink activityLink, ref TState state) + { + var tags = activityLink.Tags; + + if (tags is null) + { + return; + } + + ActivityTagsCollectionEnumerator( + tags, + ref state, + ForEachTagValueCallbackRef); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Enumerate(ActivityEvent activityEvent, ref TState state) + { + var tags = activityEvent.Tags; + + if (ReferenceEquals(tags, EmptyActivityEventTags)) + { + return; + } + + ActivityTagsCollectionEnumerator( + tags, + ref state, + ForEachTagValueCallbackRef); + } + + private static bool ForEachTagValueCallback(ref TState state, KeyValuePair item) + => state.ForEach(item); + } + + private static class ActivityLinksEnumeratorFactory + where TState : struct, IActivityEnumerator + { + private static readonly object EmptyActivityLinks = typeof(Activity).GetField("s_emptyLinks", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); + + private static readonly ListEnumerator.AllocationFreeForEachDelegate + ActivityLinksEnumerator = ListEnumerator.BuildAllocationFreeForEachDelegate( + typeof(Activity).GetField("_links", BindingFlags.Instance | BindingFlags.NonPublic).FieldType); + + private static readonly ListEnumerator.ForEachDelegate ForEachLinkCallbackRef = ForEachLinkCallback; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Enumerate(Activity activity, ref TState state) + { + var activityLinks = activity.Links; + + if (ReferenceEquals(activityLinks, EmptyActivityLinks)) + { + return; + } + + ActivityLinksEnumerator( + activityLinks, + ref state, + ForEachLinkCallbackRef); + } + + private static bool ForEachLinkCallback(ref TState state, ActivityLink item) + => state.ForEach(item); + } + + private static class ActivityEventsEnumeratorFactory + where TState : struct, IActivityEnumerator + { + private static readonly object EmptyActivityEvents = typeof(Activity).GetField("s_emptyEvents", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); + + private static readonly ListEnumerator.AllocationFreeForEachDelegate + ActivityEventsEnumerator = ListEnumerator.BuildAllocationFreeForEachDelegate( + typeof(Activity).GetField("_events", BindingFlags.Instance | BindingFlags.NonPublic).FieldType); + + private static readonly ListEnumerator.ForEachDelegate ForEachEventCallbackRef = ForEachEventCallback; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Enumerate(Activity activity, ref TState state) + { + var activityEvents = activity.Events; + + if (ReferenceEquals(activityEvents, EmptyActivityEvents)) + { + return; + } + + ActivityEventsEnumerator( + activityEvents, + ref state, + ForEachEventCallbackRef); + } + + private static bool ForEachEventCallback(ref TState state, ActivityEvent item) + => state.ForEach(item); + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityInstrumentationHelper.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityInstrumentationHelper.cs new file mode 100644 index 0000000000..4e527812f3 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityInstrumentationHelper.cs @@ -0,0 +1,44 @@ +// +// 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.Expressions; + +namespace OpenTelemetry.Instrumentation +{ + internal static class ActivityInstrumentationHelper + { + internal static readonly Action SetKindProperty = CreateActivityKindSetter(); + internal static readonly Action SetActivitySourceProperty = CreateActivitySourceSetter(); + + private static Action CreateActivitySourceSetter() + { + ParameterExpression instance = Expression.Parameter(typeof(Activity), "instance"); + ParameterExpression propertyValue = Expression.Parameter(typeof(ActivitySource), "propertyValue"); + var body = Expression.Assign(Expression.Property(instance, "Source"), propertyValue); + return Expression.Lambda>(body, instance, propertyValue).Compile(); + } + + private static Action CreateActivityKindSetter() + { + ParameterExpression instance = Expression.Parameter(typeof(Activity), "instance"); + ParameterExpression propertyValue = Expression.Parameter(typeof(ActivityKind), "propertyValue"); + var body = Expression.Assign(Expression.Property(instance, "Kind"), propertyValue); + return Expression.Lambda>(body, instance, propertyValue).Compile(); + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceListener.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceListener.cs new file mode 100644 index 0000000000..0b592b0c4d --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceListener.cs @@ -0,0 +1,74 @@ +// +// 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; + +namespace OpenTelemetry.Instrumentation +{ + internal class DiagnosticSourceListener : IObserver> + { + private readonly ListenerHandler handler; + + public DiagnosticSourceListener(ListenerHandler handler) + { + this.handler = handler ?? throw new ArgumentNullException(nameof(handler)); + } + + public void OnCompleted() + { + } + + public void OnError(Exception error) + { + } + + public void OnNext(KeyValuePair value) + { + if (!this.handler.SupportsNullActivity && Activity.Current == null) + { + InstrumentationEventSource.Log.NullActivity(value.Key); + + return; + } + + try + { + if (value.Key.EndsWith("Start", StringComparison.Ordinal)) + { + this.handler.OnStartActivity(Activity.Current, value.Value); + } + else if (value.Key.EndsWith("Stop", StringComparison.Ordinal)) + { + this.handler.OnStopActivity(Activity.Current, value.Value); + } + else if (value.Key.EndsWith("Exception", StringComparison.Ordinal)) + { + this.handler.OnException(Activity.Current, value.Value); + } + else + { + this.handler.OnCustom(value.Key, Activity.Current, value.Value); + } + } + catch (Exception ex) + { + InstrumentationEventSource.Log.UnknownErrorProcessingEvent(this.handler?.SourceName, value.Key, ex); + } + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceSubscriber.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceSubscriber.cs new file mode 100644 index 0000000000..6fc3fb2855 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceSubscriber.cs @@ -0,0 +1,112 @@ +// +// 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.Threading; + +namespace OpenTelemetry.Instrumentation +{ + internal class DiagnosticSourceSubscriber : IDisposable, IObserver + { + private readonly Func handlerFactory; + private readonly Func diagnosticSourceFilter; + private readonly Func isEnabledFilter; + private long disposed; + private IDisposable allSourcesSubscription; + private List listenerSubscriptions; + + public DiagnosticSourceSubscriber( + ListenerHandler handler, + Func isEnabledFilter) + : this(_ => handler, value => handler.SourceName == value.Name, isEnabledFilter) + { + } + + public DiagnosticSourceSubscriber( + Func handlerFactory, + Func diagnosticSourceFilter, + Func isEnabledFilter) + { + this.listenerSubscriptions = new List(); + this.handlerFactory = handlerFactory ?? throw new ArgumentNullException(nameof(handlerFactory)); + this.diagnosticSourceFilter = diagnosticSourceFilter; + this.isEnabledFilter = isEnabledFilter; + } + + public void Subscribe() + { + if (this.allSourcesSubscription == null) + { + this.allSourcesSubscription = DiagnosticListener.AllListeners.Subscribe(this); + } + } + + public void OnNext(DiagnosticListener value) + { + if ((Interlocked.Read(ref this.disposed) == 0) && + this.diagnosticSourceFilter(value)) + { + var handler = this.handlerFactory(value.Name); + var listener = new DiagnosticSourceListener(handler); + var subscription = this.isEnabledFilter == null ? + value.Subscribe(listener) : + value.Subscribe(listener, this.isEnabledFilter); + + lock (this.listenerSubscriptions) + { + this.listenerSubscriptions.Add(subscription); + } + } + } + + public void OnCompleted() + { + } + + public void OnError(Exception error) + { + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 1) + { + return; + } + + lock (this.listenerSubscriptions) + { + foreach (var listenerSubscription in this.listenerSubscriptions) + { + listenerSubscription?.Dispose(); + } + + this.listenerSubscriptions.Clear(); + } + + this.allSourcesSubscription?.Dispose(); + this.allSourcesSubscription = null; + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/EnumerationHelper.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/EnumerationHelper.cs new file mode 100644 index 0000000000..0c9abc43c9 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/EnumerationHelper.cs @@ -0,0 +1,180 @@ +// +// +// 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; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace OpenTelemetry.Internal +{ + internal class DictionaryEnumerator : Enumerator + >, + KeyValuePair, + TState> + where TState : struct + { + protected DictionaryEnumerator() + { + } + } + + internal class ListEnumerator : Enumerator + , + TValue, + TState> + where TState : struct + { + protected ListEnumerator() + { + } + } + + // A helper class for enumerating over IEnumerable without allocation if a struct enumerator is available. + internal class Enumerator + where TEnumerable : IEnumerable + where TState : struct + { + private static readonly MethodInfo GenericGetEnumeratorMethod = typeof(IEnumerable).GetMethod("GetEnumerator"); + private static readonly MethodInfo GeneircCurrentGetMethod = typeof(IEnumerator).GetProperty("Current").GetMethod; + private static readonly MethodInfo MoveNextMethod = typeof(IEnumerator).GetMethod("MoveNext"); + private static readonly MethodInfo DisposeMethod = typeof(IDisposable).GetMethod("Dispose"); + + public delegate void AllocationFreeForEachDelegate(TEnumerable instance, ref TState state, ForEachDelegate itemCallback); + + public delegate bool ForEachDelegate(ref TState state, TItem item); + + protected Enumerator() + { + } + + /* We want to do this type of logic... + public static void AllocationFreeForEach(Dictionary dictionary, ref TState state, ForEachDelegate itemCallback) + { + using (Dictionary.Enumerator enumerator = dictionary.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + if (!itemCallback(ref state, enumerator.Current)) + break; + } + } + } + ...because it takes advantage of the struct Enumerator on the built-in types which give an allocation-free way to enumerate. + */ + public static AllocationFreeForEachDelegate BuildAllocationFreeForEachDelegate(Type enumerableType) + { + var itemCallbackType = typeof(ForEachDelegate); + + var getEnumeratorMethod = ResolveGetEnumeratorMethodForType(enumerableType); + if (getEnumeratorMethod == null) + { + // Fallback to allocation mode and use IEnumerable.GetEnumerator. + // Primarily for Array.Empty and Enumerable.Empty case, but also for user types. + getEnumeratorMethod = GenericGetEnumeratorMethod; + } + + var enumeratorType = getEnumeratorMethod.ReturnType; + + var dynamicMethod = new DynamicMethod( + nameof(AllocationFreeForEachDelegate), + null, + new[] { typeof(TEnumerable), typeof(TState).MakeByRefType(), itemCallbackType }, + typeof(AllocationFreeForEachDelegate).Module, + skipVisibility: true); + + var generator = dynamicMethod.GetILGenerator(); + + generator.DeclareLocal(enumeratorType); + + var beginLoopLabel = generator.DefineLabel(); + var processCurrentLabel = generator.DefineLabel(); + var returnLabel = generator.DefineLabel(); + var breakLoopLabel = generator.DefineLabel(); + + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Callvirt, getEnumeratorMethod); + generator.Emit(OpCodes.Stloc_0); + + // try + generator.BeginExceptionBlock(); + { + generator.Emit(OpCodes.Br_S, beginLoopLabel); + + generator.MarkLabel(processCurrentLabel); + + generator.Emit(OpCodes.Ldarg_2); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldloca_S, 0); + generator.Emit(OpCodes.Constrained, enumeratorType); + generator.Emit(OpCodes.Callvirt, GeneircCurrentGetMethod); + + generator.Emit(OpCodes.Callvirt, itemCallbackType.GetMethod("Invoke")); + + generator.Emit(OpCodes.Brtrue_S, beginLoopLabel); + + generator.Emit(OpCodes.Leave_S, returnLabel); + + generator.MarkLabel(beginLoopLabel); + + generator.Emit(OpCodes.Ldloca_S, 0); + generator.Emit(OpCodes.Constrained, enumeratorType); + generator.Emit(OpCodes.Callvirt, MoveNextMethod); + + generator.Emit(OpCodes.Brtrue_S, processCurrentLabel); + + generator.MarkLabel(breakLoopLabel); + + generator.Emit(OpCodes.Leave_S, returnLabel); + } + + // finally + generator.BeginFinallyBlock(); + { + if (typeof(IDisposable).IsAssignableFrom(enumeratorType)) + { + generator.Emit(OpCodes.Ldloca_S, 0); + generator.Emit(OpCodes.Constrained, enumeratorType); + generator.Emit(OpCodes.Callvirt, DisposeMethod); + } + } + + generator.EndExceptionBlock(); + + generator.MarkLabel(returnLabel); + + generator.Emit(OpCodes.Ret); + + return (AllocationFreeForEachDelegate)dynamicMethod.CreateDelegate(typeof(AllocationFreeForEachDelegate)); + } + + private static MethodInfo ResolveGetEnumeratorMethodForType(Type type) + { + var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + foreach (var method in methods) + { + if (method.Name == "GetEnumerator" && !method.ReturnType.IsInterface) + { + return method; + } + } + + return null; + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ExceptionExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ExceptionExtensions.cs new file mode 100644 index 0000000000..0254b0cc12 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ExceptionExtensions.cs @@ -0,0 +1,46 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Globalization; +using System.Threading; + +namespace OpenTelemetry.Internal +{ + internal static class ExceptionExtensions + { + /// + /// Returns a culture-independent string representation of the given object, + /// appropriate for diagnostics tracing. + /// + /// Exception to convert to string. + /// Exception as string with no culture. + public static string ToInvariantString(this Exception exception) + { + var originalUICulture = Thread.CurrentThread.CurrentUICulture; + + try + { + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + return exception.ToString(); + } + finally + { + Thread.CurrentThread.CurrentUICulture = originalUICulture; + } + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/IActivityEnumerator.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/IActivityEnumerator.cs new file mode 100644 index 0000000000..b1fd73f3a7 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/IActivityEnumerator.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.Diagnostics; + +namespace OpenTelemetry.Trace +{ + /// + /// An interface used to perform zero-allocation enumeration of elements. Implementation must be a struct. + /// + /// Enumerated item type. + internal interface IActivityEnumerator + { + /// + /// Called for each item while the enumeration is executing. + /// + /// Enumeration item. + /// to continue the enumeration of records or to stop (break) the enumeration. + bool ForEach(T item); + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/InstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/InstrumentationEventSource.cs new file mode 100644 index 0000000000..cb22138aa6 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/InstrumentationEventSource.cs @@ -0,0 +1,52 @@ +// +// 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 OpenTelemetry.Internal; + +namespace OpenTelemetry.Instrumentation +{ + /// + /// EventSource events emitted from the project. + /// + [EventSource(Name = "OpenTelemetry-Instrumentation")] + internal class InstrumentationEventSource : EventSource + { + public static InstrumentationEventSource Log = new InstrumentationEventSource(); + + [Event(1, Message = "Current Activity is NULL in the '{0}' callback. Activity will not be recorded.", Level = EventLevel.Warning)] + public void NullActivity(string eventName) + { + this.WriteEvent(1, eventName); + } + + [NonEvent] + public void UnknownErrorProcessingEvent(string handlerName, string eventName, Exception ex) + { + if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) + { + this.UnknownErrorProcessingEvent(handlerName, eventName, ex.ToInvariantString()); + } + } + + [Event(2, Message = "Unknown error processing event '{1}' from handler '{0}', Exception: {2}", Level = EventLevel.Error)] + public void UnknownErrorProcessingEvent(string handlerName, string eventName, string ex) + { + this.WriteEvent(2, handlerName, eventName, ex); + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ListenerHandler.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ListenerHandler.cs new file mode 100644 index 0000000000..66ae48e0ca --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ListenerHandler.cs @@ -0,0 +1,81 @@ +// +// 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.Instrumentation +{ + /// + /// ListenerHandler base class. + /// + internal abstract class ListenerHandler + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the . + public ListenerHandler(string sourceName) + { + this.SourceName = sourceName; + } + + /// + /// Gets the name of the . + /// + public string SourceName { get; } + + /// + /// Gets a value indicating whether the supports NULL . + /// + public virtual bool SupportsNullActivity { get; } + + /// + /// Method called for an event with the suffix 'Start'. + /// + /// The to be started. + /// An object that represent the value being passed as a payload for the event. + public virtual void OnStartActivity(Activity activity, object payload) + { + } + + /// + /// Method called for an event with the suffix 'Stop'. + /// + /// The to be stopped. + /// An object that represent the value being passed as a payload for the event. + public virtual void OnStopActivity(Activity activity, object payload) + { + } + + /// + /// Method called for an event with the suffix 'Exception'. + /// + /// The . + /// An object that represent the value being passed as a payload for the event. + public virtual void OnException(Activity activity, object payload) + { + } + + /// + /// Method called for an event which does not have 'Start', 'Stop' or 'Exception' as suffix. + /// + /// Custom name. + /// The to be processed. + /// An object that represent the value being passed as a payload for the event. + public virtual void OnCustom(string name, Activity activity, object payload) + { + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/MultiTypePropertyFetcher.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/MultiTypePropertyFetcher.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/MultiTypePropertyFetcher.cs rename to src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/MultiTypePropertyFetcher.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SemanticConventions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SemanticConventions.cs new file mode 100644 index 0000000000..14889bf3b3 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SemanticConventions.cs @@ -0,0 +1,115 @@ +// +// 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.Trace +{ + /// + /// Constants for semantic attribute names outlined by the OpenTelemetry specifications. + /// . + /// + internal static class SemanticConventions + { + // The set of constants matches the specification as of this commit. + // https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + public const string AttributeNetTransport = "net.transport"; + public const string AttributeNetPeerIp = "net.peer.ip"; + public const string AttributeNetPeerPort = "net.peer.port"; + public const string AttributeNetPeerName = "net.peer.name"; + public const string AttributeNetHostIp = "net.host.ip"; + public const string AttributeNetHostPort = "net.host.port"; + public const string AttributeNetHostName = "net.host.name"; + + public const string AttributeEnduserId = "enduser.id"; + public const string AttributeEnduserRole = "enduser.role"; + public const string AttributeEnduserScope = "enduser.scope"; + + public const string AttributePeerService = "peer.service"; + + public const string AttributeHttpMethod = "http.method"; + public const string AttributeHttpUrl = "http.url"; + public const string AttributeHttpTarget = "http.target"; + public const string AttributeHttpHost = "http.host"; + public const string AttributeHttpScheme = "http.scheme"; + public const string AttributeHttpStatusCode = "http.status_code"; + public const string AttributeHttpStatusText = "http.status_text"; + public const string AttributeHttpFlavor = "http.flavor"; + public const string AttributeHttpServerName = "http.server_name"; + public const string AttributeHttpHostName = "host.name"; + public const string AttributeHttpHostPort = "host.port"; + public const string AttributeHttpRoute = "http.route"; + public const string AttributeHttpClientIP = "http.client_ip"; + public const string AttributeHttpUserAgent = "http.user_agent"; + public const string AttributeHttpRequestContentLength = "http.request_content_length"; + public const string AttributeHttpRequestContentLengthUncompressed = "http.request_content_length_uncompressed"; + public const string AttributeHttpResponseContentLength = "http.response_content_length"; + public const string AttributeHttpResponseContentLengthUncompressed = "http.response_content_length_uncompressed"; + + public const string AttributeDbSystem = "db.system"; + public const string AttributeDbConnectionString = "db.connection_string"; + public const string AttributeDbUser = "db.user"; + public const string AttributeDbMsSqlInstanceName = "db.mssql.instance_name"; + public const string AttributeDbJdbcDriverClassName = "db.jdbc.driver_classname"; + public const string AttributeDbName = "db.name"; + public const string AttributeDbStatement = "db.statement"; + public const string AttributeDbOperation = "db.operation"; + public const string AttributeDbInstance = "db.instance"; + public const string AttributeDbUrl = "db.url"; + public const string AttributeDbCassandraKeyspace = "db.cassandra.keyspace"; + public const string AttributeDbHBaseNamespace = "db.hbase.namespace"; + public const string AttributeDbRedisDatabaseIndex = "db.redis.database_index"; + public const string AttributeDbMongoDbCollection = "db.mongodb.collection"; + + public const string AttributeRpcSystem = "rpc.system"; + public const string AttributeRpcService = "rpc.service"; + public const string AttributeRpcMethod = "rpc.method"; + public const string AttributeRpcGrpcStatusCode = "rpc.grpc.status_code"; + + public const string AttributeMessageType = "message.type"; + public const string AttributeMessageId = "message.id"; + public const string AttributeMessageCompressedSize = "message.compressed_size"; + public const string AttributeMessageUncompressedSize = "message.uncompressed_size"; + + public const string AttributeFaasTrigger = "faas.trigger"; + public const string AttributeFaasExecution = "faas.execution"; + public const string AttributeFaasDocumentCollection = "faas.document.collection"; + public const string AttributeFaasDocumentOperation = "faas.document.operation"; + public const string AttributeFaasDocumentTime = "faas.document.time"; + public const string AttributeFaasDocumentName = "faas.document.name"; + public const string AttributeFaasTime = "faas.time"; + public const string AttributeFaasCron = "faas.cron"; + + public const string AttributeMessagingSystem = "messaging.system"; + public const string AttributeMessagingDestination = "messaging.destination"; + public const string AttributeMessagingDestinationKind = "messaging.destination_kind"; + public const string AttributeMessagingTempDestination = "messaging.temp_destination"; + public const string AttributeMessagingProtocol = "messaging.protocol"; + public const string AttributeMessagingProtocolVersion = "messaging.protocol_version"; + public const string AttributeMessagingUrl = "messaging.url"; + public const string AttributeMessagingMessageId = "messaging.message_id"; + public const string AttributeMessagingConversationId = "messaging.conversation_id"; + public const string AttributeMessagingPayloadSize = "messaging.message_payload_size_bytes"; + public const string AttributeMessagingPayloadCompressedSize = "messaging.message_payload_compressed_size_bytes"; + public const string AttributeMessagingOperation = "messaging.operation"; + + public const string AttributeExceptionEventName = "exception"; + public const string AttributeExceptionType = "exception.type"; + public const string AttributeExceptionMessage = "exception.message"; + public const string AttributeExceptionStacktrace = "exception.stacktrace"; +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanAttributeConstants.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanAttributeConstants.cs new file mode 100644 index 0000000000..c7ca70c2e5 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanAttributeConstants.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. +// + +namespace OpenTelemetry.Trace +{ + /// + /// Defines well-known span attribute keys. + /// + internal static class SpanAttributeConstants + { +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + public const string StatusCodeKey = "otel.status_code"; + public const string StatusDescriptionKey = "otel.status_description"; + + public const string HttpPathKey = "http.path"; + + public const string DatabaseStatementTypeKey = "db.statement_type"; + +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanHelper.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanHelper.cs new file mode 100644 index 0000000000..b48a20dbf8 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanHelper.cs @@ -0,0 +1,40 @@ +// +// 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.Trace +{ + /// + /// A collection of helper methods to be used when building spans. + /// + internal static class SpanHelper + { + /// + /// Helper method that populates span properties from http status code according + /// to https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#status. + /// + /// Http status code. + /// Resolved span for the Http status code. + public static Status ResolveSpanStatusForHttpStatusCode(int httpStatusCode) + { + if (httpStatusCode >= 100 && httpStatusCode <= 399) + { + return Status.Unset; + } + + return Status.Error; + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/StatusHelper.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/StatusHelper.cs new file mode 100644 index 0000000000..4ca8fa2b2a --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/StatusHelper.cs @@ -0,0 +1,63 @@ +// +// 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.Runtime.CompilerServices; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Internal +{ + internal static class StatusHelper + { + public const string UnsetStatusCodeTagValue = "UNSET"; + public const string OkStatusCodeTagValue = "OK"; + public const string ErrorStatusCodeTagValue = "ERROR"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetTagValueForStatusCode(StatusCode statusCode) + { + return statusCode switch + { + /* + * Note: Order here does matter for perf. Unset is + * first because assumption is most spans will be + * Unset, then Error. Ok is not set by the SDK. + */ + StatusCode.Unset => UnsetStatusCodeTagValue, + StatusCode.Error => ErrorStatusCodeTagValue, + StatusCode.Ok => OkStatusCodeTagValue, + _ => null, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static StatusCode? GetStatusCodeForTagValue(string statusCodeTagValue) + { + return statusCodeTagValue switch + { + /* + * Note: Order here does matter for perf. Unset is + * first because assumption is most spans will be + * Unset, then Error. Ok is not set by the SDK. + */ + string _ when UnsetStatusCodeTagValue.Equals(statusCodeTagValue, StringComparison.OrdinalIgnoreCase) => StatusCode.Unset, + string _ when ErrorStatusCodeTagValue.Equals(statusCodeTagValue, StringComparison.OrdinalIgnoreCase) => StatusCode.Error, + string _ when OkStatusCodeTagValue.Equals(statusCodeTagValue, StringComparison.OrdinalIgnoreCase) => StatusCode.Ok, + _ => (StatusCode?)null, + }; + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs new file mode 100644 index 0000000000..ae750412a6 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs @@ -0,0 +1,37 @@ +// +// 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.ElasticsearchClient.Implementation +{ + /// + /// Helper class to hold common properties used by both ElasticsearchRequestPipelineDiagnosticListener. + /// + internal class ElasticsearchActivitySourceHelper + { + public const string ActivitySourceName = "Elasticsearch.Net.RequestPipeline"; + + public const string DatabaseSystemName = "elasticsearch"; + public const string ExceptionCustomPropertyName = "OTel.Elasticsearch.Exception"; + public const string AttributeDbMethod = "db.method"; + + private static readonly Version Version = typeof(ElasticsearchActivitySourceHelper).Assembly.GetName().Version; +#pragma warning disable SA1202 // Elements should be ordered by access <- In this case, Version MUST come before ActivitySource otherwise null ref exception is thrown. + internal static readonly ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, Version.ToString()); +#pragma warning restore SA1202 // Elements should be ordered by access + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs index d2b843d6ef..2d0a5aa65f 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs @@ -33,7 +33,6 @@ internal class ElasticsearchRequestPipelineDiagnosticListener : ListenerHandler private static readonly Regex ParseRequest = new Regex(@"\n# Request:\r?\n(\{.*)\n# Response", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly ConcurrentDictionary MethodNameCache = new ConcurrentDictionary(); - private readonly ActivitySourceAdapter activitySource; private readonly ElasticsearchClientInstrumentationOptions options; private readonly MultiTypePropertyFetcher uriFetcher = new MultiTypePropertyFetcher("Uri"); private readonly MultiTypePropertyFetcher methodFetcher = new MultiTypePropertyFetcher("Method"); @@ -43,15 +42,9 @@ internal class ElasticsearchRequestPipelineDiagnosticListener : ListenerHandler private readonly MultiTypePropertyFetcher failureReasonFetcher = new MultiTypePropertyFetcher("FailureReason"); private readonly MultiTypePropertyFetcher responseBodyFetcher = new MultiTypePropertyFetcher("ResponseBodyInBytes"); - public ElasticsearchRequestPipelineDiagnosticListener(ActivitySourceAdapter activitySource, ElasticsearchClientInstrumentationOptions options) + public ElasticsearchRequestPipelineDiagnosticListener(ElasticsearchClientInstrumentationOptions options) : base("Elasticsearch.Net.RequestPipeline") { - if (activitySource == null) - { - throw new ArgumentNullException(nameof(activitySource)); - } - - this.activitySource = activitySource; this.options = options; } @@ -65,11 +58,12 @@ public override void OnStartActivity(Activity activity, object payload) return; } + ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ElasticsearchActivitySourceHelper.ActivitySource); + ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Client); + var method = this.methodFetcher.Fetch(payload); activity.DisplayName = this.GetDisplayName(activity, method); - this.activitySource.Start(activity, ActivityKind.Client); - if (this.options.SuppressDownstreamInstrumentation) { SuppressInstrumentationScope.Enter(); @@ -82,34 +76,34 @@ public override void OnStartActivity(Activity activity, object payload) var elasticIndex = this.GetElasticIndex(uri); activity.DisplayName = this.GetDisplayName(activity, method, elasticIndex); - activity.SetTag(Constants.AttributeDbSystem, "elasticsearch"); + activity.SetTag(SemanticConventions.AttributeDbSystem, ElasticsearchActivitySourceHelper.DatabaseSystemName); if (elasticIndex != null) { - activity.SetTag(Constants.AttributeDbName, elasticIndex); + activity.SetTag(SemanticConventions.AttributeDbName, elasticIndex); } var uriHostNameType = Uri.CheckHostName(uri.Host); if (uriHostNameType == UriHostNameType.IPv4 || uriHostNameType == UriHostNameType.IPv6) { - activity.SetTag(Constants.AttributeNetPeerIp, uri.Host); + activity.SetTag(SemanticConventions.AttributeNetPeerIp, uri.Host); } else { - activity.SetTag(Constants.AttributeNetPeerName, uri.Host); + activity.SetTag(SemanticConventions.AttributeNetPeerName, uri.Host); } if (uri.Port > 0) { - activity.SetTag(Constants.AttributeNetPeerPort, uri.Port); + activity.SetTag(SemanticConventions.AttributeNetPeerPort, uri.Port); } if (method != null) { - activity.SetTag(Constants.AttributeDbMethod, method.ToString()); + activity.SetTag(ElasticsearchActivitySourceHelper.AttributeDbMethod, method.ToString()); } - activity.SetTag(Constants.AttributeDbUrl, uri.OriginalString); + activity.SetTag(SemanticConventions.AttributeDbUrl, uri.OriginalString); } public override void OnStopActivity(Activity activity, object payload) @@ -117,26 +111,23 @@ public override void OnStopActivity(Activity activity, object payload) if (activity.IsAllDataRequested) { var statusCode = this.httpStatusFetcher.Fetch(payload); + activity.SetStatus(SpanHelper.ResolveSpanStatusForHttpStatusCode(statusCode.GetValueOrDefault())); - var status = Status.Error; - - if (statusCode >= 100 && statusCode <= 399) + if (statusCode.HasValue) { - status = Status.Unset; + activity.SetTag(SemanticConventions.AttributeHttpStatusCode, (int)statusCode); } - activity.SetStatus(status); - var debugInformation = this.debugInformationFetcher.Fetch(payload); if (debugInformation != null) { - activity.SetTag(Constants.AttributeDbStatement, this.ParseAndFormatRequest(activity, debugInformation)); + activity.SetTag(SemanticConventions.AttributeDbStatement, this.ParseAndFormatRequest(activity, debugInformation)); } var originalException = this.originalExceptionFetcher.Fetch(payload); if (originalException != null) { - activity.SetCustomProperty(Constants.ExceptionCustomPropertyName, originalException); + activity.SetCustomProperty(ElasticsearchActivitySourceHelper.ExceptionCustomPropertyName, originalException); var failureReason = this.failureReasonFetcher.Fetch(originalException); if (failureReason != null) @@ -170,8 +161,6 @@ public override void OnStopActivity(Activity activity, object payload) } } } - - this.activitySource.Stop(activity); } private string GetDisplayName(Activity activity, object method, string elasticType = null) @@ -233,8 +222,8 @@ private string ParseAndFormatRequest(Activity activity, string debugInformation) return debugInformation; } - string method = activity.GetTagValue(Constants.AttributeDbMethod).ToString(); - string url = activity.GetTagValue(Constants.AttributeDbUrl).ToString(); + string method = activity.GetTagValue(ElasticsearchActivitySourceHelper.AttributeDbMethod).ToString(); + string url = activity.GetTagValue(SemanticConventions.AttributeDbUrl).ToString(); if (method == "GET") { diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj index 076855bb3e..bf30e19a71 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj @@ -6,7 +6,7 @@ Instrumentation.ElasticsearchClient- - - + + diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs index 3ee81c1527..3a515d4667 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs @@ -16,6 +16,7 @@ using System; using OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient; +using OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Implementation; namespace OpenTelemetry.Trace { @@ -42,7 +43,8 @@ public static TracerProviderBuilder AddElasticsearchClientInstrumentation( var elasticsearchClientOptions = new ElasticsearchClientInstrumentationOptions(); configure?.Invoke(elasticsearchClientOptions); - builder.AddInstrumentation((activitySource) => new ElasticsearchClientInstrumentation(activitySource, elasticsearchClientOptions)); + builder.AddInstrumentation(() => new ElasticsearchClientInstrumentation(elasticsearchClientOptions)); + builder.AddSource(ElasticsearchActivitySourceHelper.ActivitySourceName); return builder; } } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs index 2d754e03bc..5c6785badb 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs @@ -21,6 +21,7 @@ using Elasticsearch.Net; using Moq; using Nest; +using OpenTelemetry.Resources; using OpenTelemetry.Trace; using Xunit; using Status = OpenTelemetry.Trace.Status; @@ -37,7 +38,7 @@ public ElasticsearchClientTests() [Fact] public async Task CanCaptureGetById() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -47,7 +48,7 @@ public async Task CanCaptureGetById() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation() - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -60,7 +61,7 @@ public async Task CanCaptureGetById() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -74,23 +75,25 @@ public async Task CanCaptureGetById() Assert.Equal($"Elasticsearch GET customer", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Equal("customer", searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Equal("customer", searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Contains("Successful (200) low level call", debugInfo); Assert.Equal(Status.Unset, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureGetByIdNotFound() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -100,7 +103,7 @@ public async Task CanCaptureGetByIdNotFound() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation() - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -113,7 +116,7 @@ public async Task CanCaptureGetByIdNotFound() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -127,23 +130,25 @@ public async Task CanCaptureGetByIdNotFound() Assert.Equal($"Elasticsearch GET customer", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Equal("customer", searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Equal("customer", searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Contains("Successful (404) low level call", debugInfo); Assert.Equal(Status.Error, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureSearchCall() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -153,7 +158,7 @@ public async Task CanCaptureSearchCall() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation() - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -166,7 +171,7 @@ public async Task CanCaptureSearchCall() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -180,23 +185,25 @@ public async Task CanCaptureSearchCall() Assert.Equal($"Elasticsearch POST customer", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Equal("customer", searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Equal("customer", searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Contains("Successful (200) low level call", debugInfo); Assert.Equal(Status.Unset, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureSearchCallWithDebugMode() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -206,7 +213,7 @@ public async Task CanCaptureSearchCallWithDebugMode() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation() - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -219,7 +226,7 @@ public async Task CanCaptureSearchCallWithDebugMode() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -233,23 +240,25 @@ public async Task CanCaptureSearchCallWithDebugMode() Assert.Equal($"Elasticsearch POST customer", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Equal("customer", searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Equal("customer", searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Contains("Successful (200) low level call", debugInfo); Assert.Equal(Status.Unset, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureSearchCallWithParseAndFormatRequestOption() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -259,7 +268,7 @@ public async Task CanCaptureSearchCallWithParseAndFormatRequestOption() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation(o => o.ParseAndFormatRequest = true) - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -272,7 +281,7 @@ public async Task CanCaptureSearchCallWithParseAndFormatRequestOption() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -286,12 +295,12 @@ public async Task CanCaptureSearchCallWithParseAndFormatRequestOption() Assert.Equal($"Elasticsearch POST customer", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Equal("customer", searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Equal("customer", searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Equal( @"POST http://localhost:9200/customer/_search?pretty=true&error_trace=true&typed_keys=true @@ -313,13 +322,14 @@ public async Task CanCaptureSearchCallWithParseAndFormatRequestOption() debugInfo.Replace("\r\n", "\n")); Assert.Equal(Status.Unset, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureSearchCallWithoutDebugMode() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -329,7 +339,7 @@ public async Task CanCaptureSearchCallWithoutDebugMode() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation(o => o.ParseAndFormatRequest = true) - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -342,7 +352,7 @@ public async Task CanCaptureSearchCallWithoutDebugMode() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -356,23 +366,25 @@ public async Task CanCaptureSearchCallWithoutDebugMode() Assert.Equal($"Elasticsearch POST customer", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Equal("customer", searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Equal("customer", searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.DoesNotContain("123", debugInfo); Assert.Equal(Status.Unset, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureMultipleIndiceSearchCall() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -382,7 +394,7 @@ public async Task CanCaptureMultipleIndiceSearchCall() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation() - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -395,7 +407,7 @@ public async Task CanCaptureMultipleIndiceSearchCall() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -409,23 +421,25 @@ public async Task CanCaptureMultipleIndiceSearchCall() Assert.Equal($"Elasticsearch POST", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Null(searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Null(searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Contains("Successful (200) low level call", debugInfo); Assert.Equal(Status.Unset, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureElasticsearchClientException() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -436,7 +450,7 @@ public async Task CanCaptureElasticsearchClientException() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation() - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -449,7 +463,7 @@ public async Task CanCaptureElasticsearchClientException() Assert.NotEmpty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -463,24 +477,26 @@ public async Task CanCaptureElasticsearchClientException() Assert.Equal($"Elasticsearch POST customer", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Equal("customer", searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Equal("customer", searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Contains("Unsuccessful (500) low level call", debugInfo); var status = searchActivity.GetStatus(); Assert.Equal(Status.Error.StatusCode, status.StatusCode); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } [Fact] public async Task CanCaptureCatRequest() { - var expectedResource = Resources.Resources.CreateServiceResource("test-service"); + var expectedResource = ResourceBuilder.CreateDefault().AddService("test-service"); var processor = new Mock>(); var parent = new Activity("parent").Start(); @@ -490,7 +506,7 @@ public async Task CanCaptureCatRequest() using (Sdk.CreateTracerProviderBuilder() .SetSampler(new AlwaysOnSampler()) .AddElasticsearchClientInstrumentation() - .SetResource(expectedResource) + .SetResourceBuilder(expectedResource) .AddProcessor(processor.Object) .Build()) { @@ -503,7 +519,7 @@ public async Task CanCaptureCatRequest() Assert.Empty(failed); } - // OnStart, OnEnd, OnShutdown, Dispose + // SetParentProvider, OnEnd, OnShutdown, Dispose Assert.Equal(4, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -517,17 +533,19 @@ public async Task CanCaptureCatRequest() Assert.Equal($"Elasticsearch GET", searchActivity.DisplayName); - Assert.Equal("localhost", searchActivity.GetTagValue(Constants.AttributeNetPeerName)); - Assert.Equal(9200, searchActivity.GetTagValue(Constants.AttributeNetPeerPort)); + var tags = searchActivity.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Assert.Equal("localhost", searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + Assert.Equal(9200, searchActivity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); - Assert.Equal("elasticsearch", searchActivity.GetTagValue(Constants.AttributeDbSystem)); - Assert.Null(searchActivity.GetTagValue(Constants.AttributeDbName)); - var debugInfo = (string)searchActivity.GetTagValue(Constants.AttributeDbStatement); + Assert.Equal("elasticsearch", searchActivity.GetTagValue(SemanticConventions.AttributeDbSystem)); + Assert.Null(searchActivity.GetTagValue(SemanticConventions.AttributeDbName)); + var debugInfo = (string)searchActivity.GetTagValue(SemanticConventions.AttributeDbStatement); Assert.NotEmpty(debugInfo); Assert.Contains("Successful (200) low level call", debugInfo); Assert.Equal(Status.Unset, searchActivity.GetStatus()); - Assert.Equal(expectedResource, searchActivity.GetResource()); + + // Assert.Equal(expectedResource, searchActivity.GetResource()); } } } From d5ff10a593f965d7b8cd02fd35ae28ac36aeab39 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Fri, 26 Feb 2021 16:16:36 -0600 Subject: [PATCH 02/10] Update OTel to make use of AddLegacyActivity --- NuGet.config | 1 + build/Common.props | 2 +- .../ElasticsearchActivitySourceHelper.cs | 15 +++++++-------- ...rib.Instrumentation.ElasticsearchClient.csproj | 2 +- .../TracerProviderBuilderExtensions.cs | 2 ++ 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/NuGet.config b/NuGet.config index bb984e4c44..1f022d500c 100644 --- a/NuGet.config +++ b/NuGet.config @@ -3,6 +3,7 @@ + diff --git a/build/Common.props b/build/Common.props index d3be1af0a8..1542b041af 100644 --- a/build/Common.props +++ b/build/Common.props @@ -21,7 +21,7 @@ Please sort alphabetically. Refer to https://docs.microsoft.com/en-us/nuget/concepts/package-versioning for semver syntax. --> - [2.3.0,3.0) + [2.4.0,3.0) [3.3.0] [16.7.1] [2.1.0,5.0) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs index ae750412a6..1ae847eaa9 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs @@ -15,6 +15,7 @@ // using System; using System.Diagnostics; +using System.Reflection; namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Implementation { @@ -23,15 +24,13 @@ namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Implementati /// internal class ElasticsearchActivitySourceHelper { - public const string ActivitySourceName = "Elasticsearch.Net.RequestPipeline"; + internal const string DatabaseSystemName = "elasticsearch"; + internal const string ExceptionCustomPropertyName = "OTel.Elasticsearch.Exception"; + internal const string AttributeDbMethod = "db.method"; - public const string DatabaseSystemName = "elasticsearch"; - public const string ExceptionCustomPropertyName = "OTel.Elasticsearch.Exception"; - public const string AttributeDbMethod = "db.method"; - - private static readonly Version Version = typeof(ElasticsearchActivitySourceHelper).Assembly.GetName().Version; -#pragma warning disable SA1202 // Elements should be ordered by access <- In this case, Version MUST come before ActivitySource otherwise null ref exception is thrown. + internal static readonly AssemblyName AssemblyName = typeof(ElasticsearchRequestPipelineDiagnosticListener).Assembly.GetName(); + internal static readonly string ActivitySourceName = AssemblyName.Name; + internal static readonly Version Version = AssemblyName.Version; internal static readonly ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, Version.ToString()); -#pragma warning restore SA1202 // Elements should be ordered by access } } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj index bf30e19a71..1d71fb358e 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj @@ -6,7 +6,7 @@ Instrumentation.ElasticsearchClient- - + diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs index 3a515d4667..8e4d0af7a9 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs @@ -45,6 +45,8 @@ public static TracerProviderBuilder AddElasticsearchClientInstrumentation( builder.AddInstrumentation(() => new ElasticsearchClientInstrumentation(elasticsearchClientOptions)); builder.AddSource(ElasticsearchActivitySourceHelper.ActivitySourceName); + builder.AddLegacyActivity("Elasticsearch.Net.RequestPipeline.CallElasticsearch"); + return builder; } } From 345778a57668261e8a9a8276879af820ddf732f4 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Fri, 26 Feb 2021 17:33:42 -0600 Subject: [PATCH 03/10] Add shared project for instrumentation helper source --- Directory.Build.props | 18 +++ opentelemetry-dotnet-contrib.sln | 9 +- ...Instrumentation.ElasticsearchClient.csproj | 1 + .../Api}/ActivityHelperExtensions.cs | 0 .../Api}/EnumerationHelper.cs | 0 .../Api}/ExceptionExtensions.cs | 0 .../Api}/IActivityEnumerator.cs | 0 .../Api}/SemanticConventions.cs | 0 .../Api}/SpanAttributeConstants.cs | 0 .../Api}/SpanHelper.cs | 0 .../Api}/StatusHelper.cs | 0 .../ActivityInstrumentationHelper.cs | 0 .../DiagnosticSourceListener.cs | 0 .../DiagnosticSourceSubscriber.cs | 0 .../InstrumentationEventSource.cs | 0 .../ListenerHandler.cs | 0 .../MultiTypePropertyFetcher.cs | 0 .../PropertyFetcher.cs | 136 ++++++++++++++++++ .../OpenTelemetry.Contrib.Shared.csproj | 13 ++ 19 files changed, 176 insertions(+), 1 deletion(-) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/ActivityHelperExtensions.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/EnumerationHelper.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/ExceptionExtensions.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/IActivityEnumerator.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/SemanticConventions.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/SpanAttributeConstants.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/SpanHelper.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/Api}/StatusHelper.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation}/ActivityInstrumentationHelper.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation}/DiagnosticSourceListener.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation}/DiagnosticSourceSubscriber.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation}/InstrumentationEventSource.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation}/ListenerHandler.cs (100%) rename src/{OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers => OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation}/MultiTypePropertyFetcher.cs (100%) create mode 100644 src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs create mode 100644 src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj diff --git a/Directory.Build.props b/Directory.Build.props index 47342704d0..c0598b52a2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,4 +7,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index d55c613fa2..e301ecafa8 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -81,7 +81,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Exten EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.AWS", "src\OpenTelemetry.Contrib.Instrumentation.AWS\OpenTelemetry.Contrib.Instrumentation.AWS.csproj", "{970673DA-F308-4960-A58D-ECCEA44CEF6B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetry.Contrib.Instrumentation.AWS.Tests", "test\OpenTelemetry.Contrib.Instrumentation.AWS.Tests\OpenTelemetry.Contrib.Instrumentation.AWS.Tests.csproj", "{CAB66B50-DAB6-49B8-83F9-6CCF520C4A36}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.AWS.Tests", "test\OpenTelemetry.Contrib.Instrumentation.AWS.Tests\OpenTelemetry.Contrib.Instrumentation.AWS.Tests.csproj", "{CAB66B50-DAB6-49B8-83F9-6CCF520C4A36}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Shared", "src\OpenTelemetry.Contrib.Shared\OpenTelemetry.Contrib.Shared.csproj", "{F52B9D81-2155-433A-B6F2-4CD7CBBEC7E6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -137,6 +139,10 @@ Global {CAB66B50-DAB6-49B8-83F9-6CCF520C4A36}.Debug|Any CPU.Build.0 = Debug|Any CPU {CAB66B50-DAB6-49B8-83F9-6CCF520C4A36}.Release|Any CPU.ActiveCfg = Release|Any CPU {CAB66B50-DAB6-49B8-83F9-6CCF520C4A36}.Release|Any CPU.Build.0 = Release|Any CPU + {F52B9D81-2155-433A-B6F2-4CD7CBBEC7E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F52B9D81-2155-433A-B6F2-4CD7CBBEC7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F52B9D81-2155-433A-B6F2-4CD7CBBEC7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F52B9D81-2155-433A-B6F2-4CD7CBBEC7E6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -157,6 +163,7 @@ Global {9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9} = {2097345F-4DD3-477D-BC54-A922F9B2B402} {970673DA-F308-4960-A58D-ECCEA44CEF6B} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {CAB66B50-DAB6-49B8-83F9-6CCF520C4A36} = {2097345F-4DD3-477D-BC54-A922F9B2B402} + {F52B9D81-2155-433A-B6F2-4CD7CBBEC7E6} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj index 1d71fb358e..99e9c9517a 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj @@ -4,6 +4,7 @@ Elasticsearch instrumentation for OpenTelemetry .NET $(PackageTags);distributed-tracing Instrumentation.ElasticsearchClient- + true diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityHelperExtensions.cs b/src/OpenTelemetry.Contrib.Shared/Api/ActivityHelperExtensions.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityHelperExtensions.cs rename to src/OpenTelemetry.Contrib.Shared/Api/ActivityHelperExtensions.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/EnumerationHelper.cs b/src/OpenTelemetry.Contrib.Shared/Api/EnumerationHelper.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/EnumerationHelper.cs rename to src/OpenTelemetry.Contrib.Shared/Api/EnumerationHelper.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ExceptionExtensions.cs b/src/OpenTelemetry.Contrib.Shared/Api/ExceptionExtensions.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ExceptionExtensions.cs rename to src/OpenTelemetry.Contrib.Shared/Api/ExceptionExtensions.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/IActivityEnumerator.cs b/src/OpenTelemetry.Contrib.Shared/Api/IActivityEnumerator.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/IActivityEnumerator.cs rename to src/OpenTelemetry.Contrib.Shared/Api/IActivityEnumerator.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SemanticConventions.cs b/src/OpenTelemetry.Contrib.Shared/Api/SemanticConventions.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SemanticConventions.cs rename to src/OpenTelemetry.Contrib.Shared/Api/SemanticConventions.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanAttributeConstants.cs b/src/OpenTelemetry.Contrib.Shared/Api/SpanAttributeConstants.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanAttributeConstants.cs rename to src/OpenTelemetry.Contrib.Shared/Api/SpanAttributeConstants.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanHelper.cs b/src/OpenTelemetry.Contrib.Shared/Api/SpanHelper.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/SpanHelper.cs rename to src/OpenTelemetry.Contrib.Shared/Api/SpanHelper.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/StatusHelper.cs b/src/OpenTelemetry.Contrib.Shared/Api/StatusHelper.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/StatusHelper.cs rename to src/OpenTelemetry.Contrib.Shared/Api/StatusHelper.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityInstrumentationHelper.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ActivityInstrumentationHelper.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ActivityInstrumentationHelper.cs rename to src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ActivityInstrumentationHelper.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceListener.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceListener.cs rename to src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceSubscriber.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/DiagnosticSourceSubscriber.cs rename to src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/InstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/InstrumentationEventSource.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/InstrumentationEventSource.cs rename to src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/InstrumentationEventSource.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ListenerHandler.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/ListenerHandler.cs rename to src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/MultiTypePropertyFetcher.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/MultiTypePropertyFetcher.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Helpers/MultiTypePropertyFetcher.cs rename to src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/MultiTypePropertyFetcher.cs diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs new file mode 100644 index 0000000000..933805d3ae --- /dev/null +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs @@ -0,0 +1,136 @@ +// +// 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.Linq; +using System.Reflection; + +namespace OpenTelemetry.Instrumentation +{ + /// + /// PropertyFetcher fetches a property from an object. + /// + /// The type of the property being fetched. + internal class PropertyFetcher + { + private readonly string propertyName; + private PropertyFetch innerFetcher; + + /// + /// Initializes a new instance of the class. + /// + /// Property name to fetch. + public PropertyFetcher(string propertyName) + { + this.propertyName = propertyName; + } + + /// + /// Fetch the property from the object. + /// + /// Object to be fetched. + /// Property fetched. + public T Fetch(object obj) + { + if (!this.TryFetch(obj, out T value)) + { + throw new ArgumentException("Supplied object was null or did not match the expected type.", nameof(obj)); + } + + return value; + } + + /// + /// Try to fetch the property from the object. + /// + /// Object to be fetched. + /// Fetched value. + /// if the property was fetched. + public bool TryFetch(object obj, out T value) + { + if (obj == null) + { + value = default; + return false; + } + + if (this.innerFetcher == null) + { + var type = obj.GetType().GetTypeInfo(); + var property = type.DeclaredProperties.FirstOrDefault(p => string.Equals(p.Name, this.propertyName, StringComparison.InvariantCultureIgnoreCase)); + if (property == null) + { + property = type.GetProperty(this.propertyName); + } + + this.innerFetcher = PropertyFetch.FetcherForProperty(property); + } + + return this.innerFetcher.TryFetch(obj, out value); + } + + // see https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs + private class PropertyFetch + { + /// + /// Create a property fetcher from a .NET Reflection PropertyInfo class that + /// represents a property of a particular type. + /// + public static PropertyFetch FetcherForProperty(PropertyInfo propertyInfo) + { + if (propertyInfo == null || !typeof(T).IsAssignableFrom(propertyInfo.PropertyType)) + { + // returns null on any fetch. + return new PropertyFetch(); + } + + var typedPropertyFetcher = typeof(TypedPropertyFetch<,>); + var instantiatedTypedPropertyFetcher = typedPropertyFetcher.MakeGenericType( + typeof(T), propertyInfo.DeclaringType, propertyInfo.PropertyType); + return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, propertyInfo); + } + + public virtual bool TryFetch(object obj, out T value) + { + value = default; + return false; + } + + private class TypedPropertyFetch : PropertyFetch + where TDeclaredProperty : T + { + private readonly Func propertyFetch; + + public TypedPropertyFetch(PropertyInfo property) + { + this.propertyFetch = (Func)property.GetMethod.CreateDelegate(typeof(Func)); + } + + public override bool TryFetch(object obj, out T value) + { + if (obj is TDeclaredObject o) + { + value = this.propertyFetch(o); + return true; + } + + value = default; + return false; + } + } + } + } +} diff --git a/src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj b/src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj new file mode 100644 index 0000000000..34eb23557b --- /dev/null +++ b/src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + + + + + + + + + From 1519a1a8b115cb1629be9d5091fb264c21e29443 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Fri, 26 Feb 2021 18:06:15 -0600 Subject: [PATCH 04/10] Added an Elasticsearch sampler tests (currently fails) --- Directory.Build.props | 18 ----- build/Common.props | 26 +++++++ .../OpenTelemetry.Contrib.Shared.csproj | 2 +- .../Test/TestActivityExportProcessor.cs | 36 ++++++++++ .../Test/TestActivityProcessor.cs | 70 +++++++++++++++++++ .../Test/TestExporter.cs | 38 ++++++++++ .../Test/TestSampler.cs | 34 +++++++++ .../ElasticsearchClientTests.cs | 59 ++++++++++++++++ ...mentation.ElasticsearchClient.Tests.csproj | 1 + 9 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs create mode 100644 src/OpenTelemetry.Contrib.Shared/Test/TestActivityProcessor.cs create mode 100644 src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs create mode 100644 src/OpenTelemetry.Contrib.Shared/Test/TestSampler.cs diff --git a/Directory.Build.props b/Directory.Build.props index c0598b52a2..47342704d0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,22 +7,4 @@ - - - - - - - - - - - - - - - - - - diff --git a/build/Common.props b/build/Common.props index 1542b041af..9493fd3ad2 100644 --- a/build/Common.props +++ b/build/Common.props @@ -44,4 +44,30 @@ all --> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj b/src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj index 34eb23557b..0a6ecfe92f 100644 --- a/src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj +++ b/src/OpenTelemetry.Contrib.Shared/OpenTelemetry.Contrib.Shared.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs b/src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs new file mode 100644 index 0000000000..64894b6cf1 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs @@ -0,0 +1,36 @@ +// +// 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.Collections.Generic; +using System.Diagnostics; + +namespace OpenTelemetry.Tests +{ + internal class TestActivityExportProcessor : SimpleActivityExportProcessor + { + public List ExportedItems = new List(); + + public TestActivityExportProcessor(BaseExporter exporter) + : base(exporter) + { + } + + protected override void OnExport(Activity data) + { + this.ExportedItems.Add(data); + } + } +} diff --git a/src/OpenTelemetry.Contrib.Shared/Test/TestActivityProcessor.cs b/src/OpenTelemetry.Contrib.Shared/Test/TestActivityProcessor.cs new file mode 100644 index 0000000000..c4815ef5b2 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Shared/Test/TestActivityProcessor.cs @@ -0,0 +1,70 @@ +// +// 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.Tests +{ + internal class TestActivityProcessor : BaseProcessor + { + public Action StartAction; + public Action EndAction; + + public TestActivityProcessor() + { + } + + public TestActivityProcessor(Action onStart, Action onEnd) + { + this.StartAction = onStart; + this.EndAction = onEnd; + } + + public bool ShutdownCalled { get; private set; } = false; + + public bool ForceFlushCalled { get; private set; } = false; + + public bool DisposedCalled { get; private set; } = false; + + public override void OnStart(Activity span) + { + this.StartAction?.Invoke(span); + } + + public override void OnEnd(Activity span) + { + this.EndAction?.Invoke(span); + } + + protected override bool OnForceFlush(int timeoutMilliseconds) + { + this.ForceFlushCalled = true; + return true; + } + + protected override bool OnShutdown(int timeoutMilliseconds) + { + this.ShutdownCalled = true; + return true; + } + + protected override void Dispose(bool disposing) + { + this.DisposedCalled = true; + } + } +} diff --git a/src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs b/src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs new file mode 100644 index 0000000000..a4f4d94272 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs @@ -0,0 +1,38 @@ +// +// 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; + +namespace OpenTelemetry.Tests +{ + internal class TestExporter : BaseExporter + where T : class + { + private readonly Action> processBatchAction; + + public TestExporter(Action> processBatchAction) + { + this.processBatchAction = processBatchAction ?? throw new ArgumentNullException(nameof(processBatchAction)); + } + + public override ExportResult Export(in Batch batch) + { + this.processBatchAction(batch); + + return ExportResult.Success; + } + } +} diff --git a/src/OpenTelemetry.Contrib.Shared/Test/TestSampler.cs b/src/OpenTelemetry.Contrib.Shared/Test/TestSampler.cs new file mode 100644 index 0000000000..eff653f063 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Shared/Test/TestSampler.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.Tests +{ + internal class TestSampler : Sampler + { + public Func SamplingAction { get; set; } + + public SamplingParameters LatestSamplingParameters { get; private set; } + + public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) + { + this.LatestSamplingParameters = samplingParameters; + return this.SamplingAction?.Invoke(samplingParameters) ?? new SamplingResult(SamplingDecision.RecordAndSample); + } + } +} diff --git a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs index 5c6785badb..b5b5959ad5 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs @@ -22,6 +22,7 @@ using Moq; using Nest; using OpenTelemetry.Resources; +using OpenTelemetry.Tests; using OpenTelemetry.Trace; using Xunit; using Status = OpenTelemetry.Trace.Status; @@ -200,6 +201,64 @@ public async Task CanCaptureSearchCall() // Assert.Equal(expectedResource, searchActivity.GetResource()); } + [Fact] + public async Task CanSampleSearchCall() + { + bool samplerCalled = false; + + var sampler = new TestSampler + { + SamplingAction = + (samplingParameters) => + { + samplerCalled = true; + return new SamplingResult(SamplingDecision.RecordAndSample); + }, + }; + + using TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); + + bool startCalled = false; + bool endCalled = false; + + testActivityProcessor.StartAction = + (a) => + { + Assert.True(samplerCalled); + Assert.False(Sdk.SuppressInstrumentation); + Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true + startCalled = true; + }; + + testActivityProcessor.EndAction = + (a) => + { + Assert.False(Sdk.SuppressInstrumentation); + Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true + endCalled = true; + }; + + var client = new ElasticClient(new ConnectionSettings(new InMemoryConnection()).DefaultIndex("customer").EnableDebugMode()); + + using (Sdk.CreateTracerProviderBuilder() + .SetSampler(new AlwaysOffSampler()) + .AddElasticsearchClientInstrumentation() + .AddProcessor(testActivityProcessor) + .Build()) + { + var searchResponse = await client.SearchAsync(s => s.Query(q => q.Bool(b => b.Must(m => m.Term(f => f.Id, "123"))))); + Assert.NotNull(searchResponse); + Assert.True(searchResponse.ApiCall.Success); + Assert.NotEmpty(searchResponse.ApiCall.AuditTrail); + + var failed = searchResponse.ApiCall.AuditTrail.Where(a => a.Event == AuditEvent.BadResponse); + Assert.Empty(failed); + } + + Assert.True(startCalled); // Processor.OnStart is called since we added a legacy OperationName + Assert.True(endCalled); // Processor.OnEnd is called since we added a legacy OperationName + } + [Fact] public async Task CanCaptureSearchCallWithDebugMode() { diff --git a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests.csproj b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests.csproj index ac39a97f04..53e67fc62d 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests.csproj +++ b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests.csproj @@ -2,6 +2,7 @@ Unit test project for OpenTelemetry Elasticsearch client instrumentation netcoreapp3.1 + true From 24ea994baf9ace3ea504e04ba719a19b43ef8ab7 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Mon, 1 Mar 2021 13:00:15 -0600 Subject: [PATCH 05/10] Got tests passing and add tests to show that sampling is working --- .../TracerProviderBuilderExtensions.cs | 2 +- .../ElasticsearchClientTests.cs | 100 ++++++++++++++---- 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs index 8e4d0af7a9..bb3dce3fc9 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs @@ -45,7 +45,7 @@ public static TracerProviderBuilder AddElasticsearchClientInstrumentation( builder.AddInstrumentation(() => new ElasticsearchClientInstrumentation(elasticsearchClientOptions)); builder.AddSource(ElasticsearchActivitySourceHelper.ActivitySourceName); - builder.AddLegacyActivity("Elasticsearch.Net.RequestPipeline.CallElasticsearch"); + builder.AddLegacyActivity("CallElasticsearch"); return builder; } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs index b5b5959ad5..6adf0db0fa 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs @@ -62,8 +62,8 @@ public async Task CanCaptureGetById() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -117,8 +117,8 @@ public async Task CanCaptureGetByIdNotFound() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -172,8 +172,8 @@ public async Task CanCaptureSearchCall() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -202,7 +202,7 @@ public async Task CanCaptureSearchCall() } [Fact] - public async Task CanSampleSearchCall() + public async Task CanRecordAndSampleSearchCall() { bool samplerCalled = false; @@ -241,8 +241,8 @@ public async Task CanSampleSearchCall() var client = new ElasticClient(new ConnectionSettings(new InMemoryConnection()).DefaultIndex("customer").EnableDebugMode()); using (Sdk.CreateTracerProviderBuilder() - .SetSampler(new AlwaysOffSampler()) - .AddElasticsearchClientInstrumentation() + .SetSampler(sampler) + .AddElasticsearchClientInstrumentation((opt) => opt.SuppressDownstreamInstrumentation = false) .AddProcessor(testActivityProcessor) .Build()) { @@ -259,6 +259,64 @@ public async Task CanSampleSearchCall() Assert.True(endCalled); // Processor.OnEnd is called since we added a legacy OperationName } + [Fact] + public async Task CanDropSearchCall() + { + bool samplerCalled = false; + + var sampler = new TestSampler + { + SamplingAction = + (samplingParameters) => + { + samplerCalled = true; + return new SamplingResult(SamplingDecision.Drop); + }, + }; + + using TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); + + bool startCalled = false; + bool endCalled = false; + + testActivityProcessor.StartAction = + (a) => + { + Assert.True(samplerCalled); + Assert.False(Sdk.SuppressInstrumentation); + Assert.False(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true + startCalled = true; + }; + + testActivityProcessor.EndAction = + (a) => + { + Assert.False(Sdk.SuppressInstrumentation); + Assert.False(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true + endCalled = true; + }; + + var client = new ElasticClient(new ConnectionSettings(new InMemoryConnection()).DefaultIndex("customer").EnableDebugMode()); + + using (Sdk.CreateTracerProviderBuilder() + .SetSampler(sampler) + .AddElasticsearchClientInstrumentation((opt) => opt.SuppressDownstreamInstrumentation = false) + .AddProcessor(testActivityProcessor) + .Build()) + { + var searchResponse = await client.SearchAsync(s => s.Query(q => q.Bool(b => b.Must(m => m.Term(f => f.Id, "123"))))); + Assert.NotNull(searchResponse); + Assert.True(searchResponse.ApiCall.Success); + Assert.NotEmpty(searchResponse.ApiCall.AuditTrail); + + var failed = searchResponse.ApiCall.AuditTrail.Where(a => a.Event == AuditEvent.BadResponse); + Assert.Empty(failed); + } + + Assert.False(startCalled); // Processor.OnStart is called since we added a legacy OperationName + Assert.False(endCalled); // Processor.OnEnd is called since we added a legacy OperationName + } + [Fact] public async Task CanCaptureSearchCallWithDebugMode() { @@ -285,8 +343,8 @@ public async Task CanCaptureSearchCallWithDebugMode() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -340,8 +398,8 @@ public async Task CanCaptureSearchCallWithParseAndFormatRequestOption() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -411,8 +469,8 @@ public async Task CanCaptureSearchCallWithoutDebugMode() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -466,8 +524,8 @@ public async Task CanCaptureMultipleIndiceSearchCall() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -522,8 +580,8 @@ public async Task CanCaptureElasticsearchClientException() Assert.NotEmpty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); @@ -578,8 +636,8 @@ public async Task CanCaptureCatRequest() Assert.Empty(failed); } - // SetParentProvider, OnEnd, OnShutdown, Dispose - Assert.Equal(4, processor.Invocations.Count); + // SetParentProvider, OnStart, OnEnd, OnShutdown, Dispose + Assert.Equal(5, processor.Invocations.Count); var activities = processor.Invocations.Where(i => i.Method.Name == "OnEnd").Select(i => i.Arguments[0]).Cast().ToArray(); Assert.Single(activities); From df7947a874c4905d5df378de0a0a3744a55ef687 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Tue, 2 Mar 2021 17:37:22 -0600 Subject: [PATCH 06/10] Move constants into ElasticsearchRequestPipelineDiagnosticListener class. Fix formatting issues. --- .../ElasticsearchActivitySourceHelper.cs | 36 ------------------- ...searchRequestPipelineDiagnosticListener.cs | 20 ++++++++--- .../TracerProviderBuilderExtensions.cs | 2 +- .../Api/ActivityHelperExtensions.cs | 2 +- .../Api/ExceptionExtensions.cs | 2 +- .../Api/IActivityEnumerator.cs | 2 +- .../Api/SemanticConventions.cs | 2 +- .../Api/SpanAttributeConstants.cs | 2 +- .../Api/SpanHelper.cs | 2 +- .../Api/StatusHelper.cs | 2 +- .../ActivityInstrumentationHelper.cs | 2 +- .../DiagnosticSourceSubscriber.cs | 2 +- .../InstrumentationEventSource.cs | 2 +- .../ListenerHandler.cs | 2 +- .../PropertyFetcher.cs | 2 +- .../Test/TestActivityExportProcessor.cs | 2 +- .../Test/TestExporter.cs | 2 +- .../Test/TestSampler.cs | 2 +- 18 files changed, 31 insertions(+), 57 deletions(-) delete mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs deleted file mode 100644 index 1ae847eaa9..0000000000 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchActivitySourceHelper.cs +++ /dev/null @@ -1,36 +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.Reflection; - -namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Implementation -{ - /// - /// Helper class to hold common properties used by both ElasticsearchRequestPipelineDiagnosticListener. - /// - internal class ElasticsearchActivitySourceHelper - { - internal const string DatabaseSystemName = "elasticsearch"; - internal const string ExceptionCustomPropertyName = "OTel.Elasticsearch.Exception"; - internal const string AttributeDbMethod = "db.method"; - - internal static readonly AssemblyName AssemblyName = typeof(ElasticsearchRequestPipelineDiagnosticListener).Assembly.GetName(); - internal static readonly string ActivitySourceName = AssemblyName.Name; - internal static readonly Version Version = AssemblyName.Version; - internal static readonly ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, Version.ToString()); - } -} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs index 2d0a5aa65f..3643cf87c1 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs @@ -20,6 +20,7 @@ using System.Linq; using System.Net.Http; using System.Net.Sockets; +using System.Reflection; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; @@ -30,6 +31,15 @@ namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Implementati { internal class ElasticsearchRequestPipelineDiagnosticListener : ListenerHandler { + internal const string DatabaseSystemName = "elasticsearch"; + internal const string ExceptionCustomPropertyName = "OTel.Elasticsearch.Exception"; + internal const string AttributeDbMethod = "db.method"; + + internal static readonly AssemblyName AssemblyName = typeof(ElasticsearchRequestPipelineDiagnosticListener).Assembly.GetName(); + internal static readonly string ActivitySourceName = AssemblyName.Name; + internal static readonly Version Version = AssemblyName.Version; + internal static readonly ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, Version.ToString()); + private static readonly Regex ParseRequest = new Regex(@"\n# Request:\r?\n(\{.*)\n# Response", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly ConcurrentDictionary MethodNameCache = new ConcurrentDictionary(); @@ -58,7 +68,7 @@ public override void OnStartActivity(Activity activity, object payload) return; } - ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ElasticsearchActivitySourceHelper.ActivitySource); + ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ActivitySource); ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Client); var method = this.methodFetcher.Fetch(payload); @@ -76,7 +86,7 @@ public override void OnStartActivity(Activity activity, object payload) var elasticIndex = this.GetElasticIndex(uri); activity.DisplayName = this.GetDisplayName(activity, method, elasticIndex); - activity.SetTag(SemanticConventions.AttributeDbSystem, ElasticsearchActivitySourceHelper.DatabaseSystemName); + activity.SetTag(SemanticConventions.AttributeDbSystem, DatabaseSystemName); if (elasticIndex != null) { @@ -100,7 +110,7 @@ public override void OnStartActivity(Activity activity, object payload) if (method != null) { - activity.SetTag(ElasticsearchActivitySourceHelper.AttributeDbMethod, method.ToString()); + activity.SetTag(AttributeDbMethod, method.ToString()); } activity.SetTag(SemanticConventions.AttributeDbUrl, uri.OriginalString); @@ -127,7 +137,7 @@ public override void OnStopActivity(Activity activity, object payload) var originalException = this.originalExceptionFetcher.Fetch(payload); if (originalException != null) { - activity.SetCustomProperty(ElasticsearchActivitySourceHelper.ExceptionCustomPropertyName, originalException); + activity.SetCustomProperty(ExceptionCustomPropertyName, originalException); var failureReason = this.failureReasonFetcher.Fetch(originalException); if (failureReason != null) @@ -222,7 +232,7 @@ private string ParseAndFormatRequest(Activity activity, string debugInformation) return debugInformation; } - string method = activity.GetTagValue(ElasticsearchActivitySourceHelper.AttributeDbMethod).ToString(); + string method = activity.GetTagValue(AttributeDbMethod).ToString(); string url = activity.GetTagValue(SemanticConventions.AttributeDbUrl).ToString(); if (method == "GET") diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs index bb3dce3fc9..06eb27466d 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs @@ -44,7 +44,7 @@ public static TracerProviderBuilder AddElasticsearchClientInstrumentation( configure?.Invoke(elasticsearchClientOptions); builder.AddInstrumentation(() => new ElasticsearchClientInstrumentation(elasticsearchClientOptions)); - builder.AddSource(ElasticsearchActivitySourceHelper.ActivitySourceName); + builder.AddSource(ElasticsearchRequestPipelineDiagnosticListener.ActivitySourceName); builder.AddLegacyActivity("CallElasticsearch"); return builder; diff --git a/src/OpenTelemetry.Contrib.Shared/Api/ActivityHelperExtensions.cs b/src/OpenTelemetry.Contrib.Shared/Api/ActivityHelperExtensions.cs index cdd5d92cd0..5a60a1900b 100644 --- a/src/OpenTelemetry.Contrib.Shared/Api/ActivityHelperExtensions.cs +++ b/src/OpenTelemetry.Contrib.Shared/Api/ActivityHelperExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Api/ExceptionExtensions.cs b/src/OpenTelemetry.Contrib.Shared/Api/ExceptionExtensions.cs index 0254b0cc12..3de2891300 100644 --- a/src/OpenTelemetry.Contrib.Shared/Api/ExceptionExtensions.cs +++ b/src/OpenTelemetry.Contrib.Shared/Api/ExceptionExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Api/IActivityEnumerator.cs b/src/OpenTelemetry.Contrib.Shared/Api/IActivityEnumerator.cs index b1fd73f3a7..4b368e8ec6 100644 --- a/src/OpenTelemetry.Contrib.Shared/Api/IActivityEnumerator.cs +++ b/src/OpenTelemetry.Contrib.Shared/Api/IActivityEnumerator.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Api/SemanticConventions.cs b/src/OpenTelemetry.Contrib.Shared/Api/SemanticConventions.cs index 14889bf3b3..4cd4357baa 100644 --- a/src/OpenTelemetry.Contrib.Shared/Api/SemanticConventions.cs +++ b/src/OpenTelemetry.Contrib.Shared/Api/SemanticConventions.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Api/SpanAttributeConstants.cs b/src/OpenTelemetry.Contrib.Shared/Api/SpanAttributeConstants.cs index c7ca70c2e5..32f0621e4d 100644 --- a/src/OpenTelemetry.Contrib.Shared/Api/SpanAttributeConstants.cs +++ b/src/OpenTelemetry.Contrib.Shared/Api/SpanAttributeConstants.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Api/SpanHelper.cs b/src/OpenTelemetry.Contrib.Shared/Api/SpanHelper.cs index b48a20dbf8..25771f11de 100644 --- a/src/OpenTelemetry.Contrib.Shared/Api/SpanHelper.cs +++ b/src/OpenTelemetry.Contrib.Shared/Api/SpanHelper.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Api/StatusHelper.cs b/src/OpenTelemetry.Contrib.Shared/Api/StatusHelper.cs index 4ca8fa2b2a..940746c720 100644 --- a/src/OpenTelemetry.Contrib.Shared/Api/StatusHelper.cs +++ b/src/OpenTelemetry.Contrib.Shared/Api/StatusHelper.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ActivityInstrumentationHelper.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ActivityInstrumentationHelper.cs index 4e527812f3..8a006394b6 100644 --- a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ActivityInstrumentationHelper.cs +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ActivityInstrumentationHelper.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs index 6fc3fb2855..5177ef48f9 100644 --- a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/InstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/InstrumentationEventSource.cs index cb22138aa6..277162d439 100644 --- a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/InstrumentationEventSource.cs +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/InstrumentationEventSource.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs index 66ae48e0ca..ce159ee92e 100644 --- a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs index 933805d3ae..3bb1bb6347 100644 --- a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/PropertyFetcher.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs b/src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs index 64894b6cf1..d85cf7689c 100644 --- a/src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs +++ b/src/OpenTelemetry.Contrib.Shared/Test/TestActivityExportProcessor.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs b/src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs index a4f4d94272..d2a6603293 100644 --- a/src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs +++ b/src/OpenTelemetry.Contrib.Shared/Test/TestExporter.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/OpenTelemetry.Contrib.Shared/Test/TestSampler.cs b/src/OpenTelemetry.Contrib.Shared/Test/TestSampler.cs index eff653f063..1d1a69e0d1 100644 --- a/src/OpenTelemetry.Contrib.Shared/Test/TestSampler.cs +++ b/src/OpenTelemetry.Contrib.Shared/Test/TestSampler.cs @@ -1,4 +1,4 @@ -// +// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); From d985d7b40d8af725791d7020c6cba778a8363653 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Tue, 2 Mar 2021 18:04:19 -0600 Subject: [PATCH 07/10] Adding Enrich option for Elasticsearch instrumentation --- ...lasticsearchClientInstrumentationOptions.cs | 14 ++++++++++++++ .../ElasticsearchInstrumentationEventSource.cs | 17 +++++++++++++++++ ...csearchRequestPipelineDiagnosticListener.cs | 18 ++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentationOptions.cs index 709b2c5d92..8ee329b412 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentationOptions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/ElasticsearchClientInstrumentationOptions.cs @@ -14,6 +14,9 @@ // limitations under the License. // +using System; +using System.Diagnostics; + namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient { /// @@ -30,5 +33,16 @@ public class ElasticsearchClientInstrumentationOptions /// Gets or sets a value indicating whether the JSON request body should be parsed out of the request debug information and formatted as indented JSON. /// public bool ParseAndFormatRequest { get; set; } = false; + + /// + /// Gets or sets an action to enrich an Activity. + /// + /// + /// : the activity being enriched. + /// string: the name of the event. + /// object: the raw object from which additional information can be extracted to enrich the activity. + /// The type of this object depends on the event, which is given by the above parameter. + /// + public Action Enrich { get; set; } } } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchInstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchInstrumentationEventSource.cs index cc9e7fa391..d2c4768633 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchInstrumentationEventSource.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchInstrumentationEventSource.cs @@ -14,7 +14,9 @@ // limitations under the License. // +using System; using System.Diagnostics.Tracing; +using OpenTelemetry.Internal; namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Implementation { @@ -31,5 +33,20 @@ public void NullPayload(string handlerName, string eventName) { this.WriteEvent(1, handlerName, eventName); } + + [NonEvent] + public void EnrichmentException(Exception ex) + { + if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) + { + this.EnrichmentException(ex.ToInvariantString()); + } + } + + [Event(2, Message = "Enrichment threw exception. Exception {0}.", Level = EventLevel.Error)] + public void EnrichmentException(string exception) + { + this.WriteEvent(2, exception); + } } } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs index 3643cf87c1..0ddb27a457 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/Implementation/ElasticsearchRequestPipelineDiagnosticListener.cs @@ -114,6 +114,15 @@ public override void OnStartActivity(Activity activity, object payload) } activity.SetTag(SemanticConventions.AttributeDbUrl, uri.OriginalString); + + try + { + this.options.Enrich?.Invoke(activity, "OnStartActivity", payload); + } + catch (Exception ex) + { + ElasticsearchInstrumentationEventSource.Log.EnrichmentException(ex); + } } public override void OnStopActivity(Activity activity, object payload) @@ -170,6 +179,15 @@ public override void OnStopActivity(Activity activity, object payload) } } } + + try + { + this.options.Enrich?.Invoke(activity, "OnStopActivity", payload); + } + catch (Exception ex) + { + ElasticsearchInstrumentationEventSource.Log.EnrichmentException(ex); + } } } From ca07b7bd7618d34bb0834816711f0759717dcfdd Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Tue, 2 Mar 2021 18:19:04 -0600 Subject: [PATCH 08/10] Skip shared project test coverage --- .github/codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/codecov.yml b/.github/codecov.yml index d8537bfc48..cf7b065a15 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -22,3 +22,4 @@ comment: ignore: - "test/**/*" # ignore test folder - "**.md" # ignore md files + - "src/OpenTelemetry.Contrib.Shared" # copied from main OTel project and has code coverage there From a940525eae6c320913d3433e4bc3558c4a01f67f Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Mon, 8 Mar 2021 10:58:20 -0600 Subject: [PATCH 09/10] Add tests to verify downstream suppression. Change DiagnosticSourceListener to use it's own RuntimeContextSlot. --- .../DiagnosticSourceListener.cs | 26 +++++- .../ElasticsearchClientTests.cs | 93 ++++++++++++++++--- ...nMemoryConnectionWithDownstreamActivity.cs | 37 ++++++++ 3 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/InMemoryConnectionWithDownstreamActivity.cs diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs index 0b592b0c4d..e0d50d2357 100644 --- a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs @@ -17,11 +17,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using OpenTelemetry.Context; namespace OpenTelemetry.Instrumentation { internal class DiagnosticSourceListener : IObserver> { + private static readonly RuntimeContextSlot Slot = RuntimeContext.RegisterSlot("otel.suppress_instrumentation_listener"); private readonly ListenerHandler handler; public DiagnosticSourceListener(ListenerHandler handler) @@ -50,17 +52,33 @@ public void OnNext(KeyValuePair value) { if (value.Key.EndsWith("Start", StringComparison.Ordinal)) { - this.handler.OnStartActivity(Activity.Current, value.Value); + if (!Sdk.SuppressInstrumentation) + { + this.handler.OnStartActivity(Activity.Current, value.Value); + } + else + { + var supressionCount = Slot.Get(); + Slot.Set(++supressionCount); + } } else if (value.Key.EndsWith("Stop", StringComparison.Ordinal)) { - this.handler.OnStopActivity(Activity.Current, value.Value); + var supressionCount = Slot.Get(); + if (supressionCount <= 0) + { + this.handler.OnStopActivity(Activity.Current, value.Value); + } + else if (supressionCount > 0) + { + Slot.Set(--supressionCount); + } } - else if (value.Key.EndsWith("Exception", StringComparison.Ordinal)) + else if (value.Key.EndsWith("Exception", StringComparison.Ordinal) && !Sdk.SuppressInstrumentation) { this.handler.OnException(Activity.Current, value.Value); } - else + else if (!Sdk.SuppressInstrumentation) { this.handler.OnCustom(value.Key, Activity.Current, value.Value); } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs index 6adf0db0fa..2a6fc730e8 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/ElasticsearchClientTests.cs @@ -17,6 +17,7 @@ using System.Diagnostics; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Elasticsearch.Net; using Moq; @@ -218,8 +219,8 @@ public async Task CanRecordAndSampleSearchCall() using TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); - bool startCalled = false; - bool endCalled = false; + int startCalled = 0; + int endCalled = 0; testActivityProcessor.StartAction = (a) => @@ -227,7 +228,7 @@ public async Task CanRecordAndSampleSearchCall() Assert.True(samplerCalled); Assert.False(Sdk.SuppressInstrumentation); Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true - startCalled = true; + startCalled++; }; testActivityProcessor.EndAction = @@ -235,13 +236,15 @@ public async Task CanRecordAndSampleSearchCall() { Assert.False(Sdk.SuppressInstrumentation); Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true - endCalled = true; + endCalled++; }; - var client = new ElasticClient(new ConnectionSettings(new InMemoryConnection()).DefaultIndex("customer").EnableDebugMode()); + var client = new ElasticClient(new ConnectionSettings(new InMemoryConnectionWithDownstreamActivity()).DefaultIndex("customer").EnableDebugMode()); using (Sdk.CreateTracerProviderBuilder() .SetSampler(sampler) + .AddSource("Downstream") + .AddSource("NestedDownstream") .AddElasticsearchClientInstrumentation((opt) => opt.SuppressDownstreamInstrumentation = false) .AddProcessor(testActivityProcessor) .Build()) @@ -255,8 +258,68 @@ public async Task CanRecordAndSampleSearchCall() Assert.Empty(failed); } - Assert.True(startCalled); // Processor.OnStart is called since we added a legacy OperationName - Assert.True(endCalled); // Processor.OnEnd is called since we added a legacy OperationName + Assert.Equal(3, startCalled); // Processor.OnStart is called since we added a legacy OperationName + Assert.Equal(3, endCalled); // Processor.OnEnd is called since we added a legacy OperationName + } + + [Fact] + public async Task CanSupressDownstreamActivities() + { + bool samplerCalled = false; + + var sampler = new TestSampler + { + SamplingAction = + (samplingParameters) => + { + samplerCalled = true; + return new SamplingResult(SamplingDecision.RecordAndSample); + }, + }; + + using TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); + + int startCalled = 0; + int endCalled = 0; + + testActivityProcessor.StartAction = + (a) => + { + Assert.True(samplerCalled); + Assert.False(Sdk.SuppressInstrumentation); + Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true + startCalled++; + }; + + testActivityProcessor.EndAction = + (a) => + { + Assert.False(Sdk.SuppressInstrumentation); + Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true + endCalled++; + }; + + var client = new ElasticClient(new ConnectionSettings(new InMemoryConnectionWithDownstreamActivity()).DefaultIndex("customer").EnableDebugMode()); + + using (Sdk.CreateTracerProviderBuilder() + .SetSampler(sampler) + .AddSource("Downstream") + .AddSource("NestedDownstream") + .AddElasticsearchClientInstrumentation((opt) => opt.SuppressDownstreamInstrumentation = true) + .AddProcessor(testActivityProcessor) + .Build()) + { + var searchResponse = await client.SearchAsync(s => s.Query(q => q.Bool(b => b.Must(m => m.Term(f => f.Id, "123"))))); + Assert.NotNull(searchResponse); + Assert.True(searchResponse.ApiCall.Success); + Assert.NotEmpty(searchResponse.ApiCall.AuditTrail); + + var failed = searchResponse.ApiCall.AuditTrail.Where(a => a.Event == AuditEvent.BadResponse); + Assert.Empty(failed); + } + + Assert.Equal(1, startCalled); // Processor.OnStart is called since we added a legacy OperationName + Assert.Equal(1, endCalled); // Processor.OnEnd is called since we added a legacy OperationName } [Fact] @@ -276,8 +339,8 @@ public async Task CanDropSearchCall() using TestActivityProcessor testActivityProcessor = new TestActivityProcessor(); - bool startCalled = false; - bool endCalled = false; + int startCalled = 0; + int endCalled = 0; testActivityProcessor.StartAction = (a) => @@ -285,7 +348,7 @@ public async Task CanDropSearchCall() Assert.True(samplerCalled); Assert.False(Sdk.SuppressInstrumentation); Assert.False(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true - startCalled = true; + startCalled++; }; testActivityProcessor.EndAction = @@ -293,13 +356,15 @@ public async Task CanDropSearchCall() { Assert.False(Sdk.SuppressInstrumentation); Assert.False(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true - endCalled = true; + endCalled++; }; - var client = new ElasticClient(new ConnectionSettings(new InMemoryConnection()).DefaultIndex("customer").EnableDebugMode()); + var client = new ElasticClient(new ConnectionSettings(new InMemoryConnectionWithDownstreamActivity()).DefaultIndex("customer").EnableDebugMode()); using (Sdk.CreateTracerProviderBuilder() .SetSampler(sampler) + .AddSource("Downstream") + .AddSource("NestedDownstream") .AddElasticsearchClientInstrumentation((opt) => opt.SuppressDownstreamInstrumentation = false) .AddProcessor(testActivityProcessor) .Build()) @@ -313,8 +378,8 @@ public async Task CanDropSearchCall() Assert.Empty(failed); } - Assert.False(startCalled); // Processor.OnStart is called since we added a legacy OperationName - Assert.False(endCalled); // Processor.OnEnd is called since we added a legacy OperationName + Assert.Equal(0, startCalled); // Processor.OnStart is called since we added a legacy OperationName + Assert.Equal(0, endCalled); // Processor.OnEnd is called since we added a legacy OperationName } [Fact] diff --git a/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/InMemoryConnectionWithDownstreamActivity.cs b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/InMemoryConnectionWithDownstreamActivity.cs new file mode 100644 index 0000000000..68bbb5fe4e --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests/InMemoryConnectionWithDownstreamActivity.cs @@ -0,0 +1,37 @@ +// +// 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.Threading; +using System.Threading.Tasks; +using Elasticsearch.Net; + +namespace OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.Tests +{ + public class InMemoryConnectionWithDownstreamActivity : InMemoryConnection + { + internal static readonly ActivitySource ActivitySource = new ActivitySource("Downstream"); + internal static readonly ActivitySource NestedActivitySource = new ActivitySource("NestedDownstream"); + + public override Task RequestAsync(RequestData requestData, CancellationToken cancellationToken) + { + using var a1 = ActivitySource.StartActivity("downstream"); + using var a2 = NestedActivitySource.StartActivity("nested-downstream"); + + return base.RequestAsync(requestData, cancellationToken); + } + } +} From 7e611951afeb26392357bd9b3956209e582fc3c1 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Tue, 9 Mar 2021 22:37:03 -0600 Subject: [PATCH 10/10] Update to latest OTel --- ...Instrumentation.ElasticsearchClient.csproj | 2 +- .../TracerProviderBuilderExtensions.cs | 2 +- .../DiagnosticSourceListener.cs | 41 ++++++++----------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj index 99e9c9517a..c68acf893f 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/OpenTelemetry.Contrib.Instrumentation.ElasticsearchClient.csproj @@ -7,7 +7,7 @@ true - + diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs index 06eb27466d..3e894633b2 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Elasticsearch/TracerProviderBuilderExtensions.cs @@ -45,7 +45,7 @@ public static TracerProviderBuilder AddElasticsearchClientInstrumentation( builder.AddInstrumentation(() => new ElasticsearchClientInstrumentation(elasticsearchClientOptions)); builder.AddSource(ElasticsearchRequestPipelineDiagnosticListener.ActivitySourceName); - builder.AddLegacyActivity("CallElasticsearch"); + builder.AddLegacySource("CallElasticsearch"); return builder; } diff --git a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs index e0d50d2357..be488f2f12 100644 --- a/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs +++ b/src/OpenTelemetry.Contrib.Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs @@ -17,13 +17,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using OpenTelemetry.Context; namespace OpenTelemetry.Instrumentation { internal class DiagnosticSourceListener : IObserver> { - private static readonly RuntimeContextSlot Slot = RuntimeContext.RegisterSlot("otel.suppress_instrumentation_listener"); private readonly ListenerHandler handler; public DiagnosticSourceListener(ListenerHandler handler) @@ -43,7 +41,10 @@ public void OnNext(KeyValuePair value) { if (!this.handler.SupportsNullActivity && Activity.Current == null) { - InstrumentationEventSource.Log.NullActivity(value.Key); + if (!Sdk.SuppressInstrumentation) + { + InstrumentationEventSource.Log.NullActivity(value.Key); + } return; } @@ -52,35 +53,25 @@ public void OnNext(KeyValuePair value) { if (value.Key.EndsWith("Start", StringComparison.Ordinal)) { - if (!Sdk.SuppressInstrumentation) - { - this.handler.OnStartActivity(Activity.Current, value.Value); - } - else - { - var supressionCount = Slot.Get(); - Slot.Set(++supressionCount); - } + this.handler.OnStartActivity(Activity.Current, value.Value); } else if (value.Key.EndsWith("Stop", StringComparison.Ordinal)) { - var supressionCount = Slot.Get(); - if (supressionCount <= 0) - { - this.handler.OnStopActivity(Activity.Current, value.Value); - } - else if (supressionCount > 0) - { - Slot.Set(--supressionCount); - } + this.handler.OnStopActivity(Activity.Current, value.Value); } - else if (value.Key.EndsWith("Exception", StringComparison.Ordinal) && !Sdk.SuppressInstrumentation) + else if (value.Key.EndsWith("Exception", StringComparison.Ordinal)) { - this.handler.OnException(Activity.Current, value.Value); + if (!Sdk.SuppressInstrumentation) + { + this.handler.OnException(Activity.Current, value.Value); + } } - else if (!Sdk.SuppressInstrumentation) + else { - this.handler.OnCustom(value.Key, Activity.Current, value.Value); + if (!Sdk.SuppressInstrumentation) + { + this.handler.OnCustom(value.Key, Activity.Current, value.Value); + } } } catch (Exception ex)