Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #125:Adding enricher for Wcf instrumentation #126

Merged
merged 13 commits into from
Jun 15, 2021
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ public void RequestFilterException(string exception)
this.WriteEvent(EventIds.RequestFilterException, exception);
}

[NonEvent]
public void EnrichmentException(Exception exception)
{
if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1)))
{
this.EnrichmentException(ToInvariantString(exception));
}
}

[Event(EventIds.EnrichmentException, Message = "Enrichment threw exception. Exception {0}.", Level = EventLevel.Error)]
public void EnrichmentException(string exception)
{
this.WriteEvent(EventIds.EnrichmentException, exception);
}

/// <summary>
/// Returns a culture-independent string representation of the given <paramref name="exception"/> object,
/// appropriate for diagnostics tracing.
Expand All @@ -70,6 +85,7 @@ private class EventIds
{
public const int RequestIsFilteredOut = 1;
public const int RequestFilterException = 2;
public const int EnrichmentException = 3;
}
}
}
18 changes: 18 additions & 0 deletions src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ using var openTelemetry = Sdk.CreateTracerProviderBuilder()
.Build();
```

Instrumentation can be configured via options overload for `AddWcfInstrumentation` method:

```csharp
using var openTelemetry = Sdk.CreateTracerProviderBuilder()
.AddWcfInstrumentation(options =>
{
options.SuppressDownstreamInstrumentation = false;
options.Enrich = (activity,eventName,message) => // -> Enable enriching an activity after it is created.
{
if(eventName == WcfEnrichEventNames.AfterReceiveRequest) // For event names, please refer to string contstants in WcfEventNames class.
{
activity.AddTag("companyname.customtag", Guid.NewGuid());
epaulsen marked this conversation as resolved.
Show resolved Hide resolved
}
}
})
.Build();
```

## WCF Client Configuration (.NET Framework)

Add the `IClientMessageInspector` instrumentation via a behavior extension on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel)
Activity activity = WcfInstrumentationActivitySource.ActivitySource.StartActivity(
WcfInstrumentationActivitySource.OutgoingRequestActivityName,
ActivityKind.Client);

IDisposable suppressionScope = this.SuppressDownstreamInstrumentation();

if (activity != null)
Expand Down Expand Up @@ -111,6 +110,15 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
activity.SetTag(WcfInstrumentationConstants.SoapViaTag, request.Properties.Via.ToString());
}

try
{
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.BeforeSendRequest, request);
}
catch (Exception ex)
{
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
}
}
}

Expand Down Expand Up @@ -139,6 +147,14 @@ public void AfterReceiveReply(ref Message reply, object correlationState)
}

activity.SetTag(WcfInstrumentationConstants.SoapReplyActionTag, reply.Headers.Action);
try
{
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.AfterReceiveReply, reply);
}
catch (Exception ex)
{
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
}
}

activity.Stop();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I
activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, localAddressUri.Scheme);
activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, localAddressUri.LocalPath);
}

try
{
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.AfterReceiveRequest, request);
}
catch (Exception ex)
{
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
}
}
}

Expand All @@ -121,6 +130,14 @@ public void BeforeSendReply(ref Message reply, object correlationState)
}

activity.SetTag(WcfInstrumentationConstants.SoapReplyActionTag, reply.Headers.Action);
try
{
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.BeforeSendReply, reply);
}
catch (Exception ex)
{
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
}
}

activity.Stop();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// <copyright file="WcfEnrichEventNames.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

namespace OpenTelemetry.Contrib.Instrumentation.Wcf
{
/// <summary>
/// Constants used for event names when enriching an activity.
/// </summary>
public class WcfEnrichEventNames
{
#if NETFRAMEWORK
/// <summary>
/// WCF service activity, event happens before WCF service method is invoked.
/// </summary>
public const string AfterReceiveRequest = "AfterReceiveRequest";

/// <summary>
/// WCF service activity, event happens after the WCF service method is invoked but before the reply is sent back to the client.
/// </summary>
public const string BeforeSendReply = "BeforeSendReply";
#endif

/// <summary>
/// WCF client activity, event happens before the request is sent across the wire.
/// </summary>
public const string BeforeSendRequest = "BeforeSendRequest";

/// <summary>
/// WCF client activity, event happens after a reply from the WCF service is received.
/// </summary>
public const string AfterReceiveReply = "AfterReceiveReply";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// </copyright>

using System;
using System.Diagnostics;
using System.ServiceModel.Channels;
using OpenTelemetry.Context.Propagation;

Expand All @@ -34,6 +35,18 @@ public class WcfInstrumentationOptions
new BaggagePropagator(),
});

/// <summary>
/// Gets or sets an action to enrich an Activity.
/// </summary>
/// <remarks>
/// <para><see cref="Activity"/>: the activity being enriched.</para>
/// <para>string: the name of the event. Will be one of the constants in <see cref="WcfEnrichEventNames"/>.
/// </para>
/// <para>object: the raw <see cref="Message"/> from which additional information can be extracted to enrich the activity.
/// </para>
/// </remarks>
public Action<Activity, string, object> Enrich { get; set; }
epaulsen marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Gets or sets a Filter function to filter instrumentation for requests on a per request basis.
/// The Filter gets the Message, and should return a boolean.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,13 @@ public void Dispose()
[InlineData(true, false, false)]
[InlineData(false)]
[InlineData(true, false, true, true)]
[InlineData(true, false, true, true, true)]
public async Task OutgoingRequestInstrumentationTest(
bool instrument,
bool filter = false,
bool suppressDownstreamInstrumentation = true,
bool includeVersion = false)
bool includeVersion = false,
bool enrich = false)
{
#if NETFRAMEWORK
const string OutgoingHttpOperationName = "OpenTelemetry.HttpWebRequest.HttpRequestOut";
Expand All @@ -147,6 +149,22 @@ public async Task OutgoingRequestInstrumentationTest(
builder
.AddWcfInstrumentation(options =>
{
if (enrich)
{
options.Enrich = (activity, eventName, message) =>
{
switch (eventName)
{
case WcfEnrichEventNames.BeforeSendRequest:
activity.AddTag("client.beforesendrequest", WcfEnrichEventNames.BeforeSendRequest);
epaulsen marked this conversation as resolved.
Show resolved Hide resolved
break;
case WcfEnrichEventNames.AfterReceiveReply:
activity.AddTag("client.afterreceivereply", WcfEnrichEventNames.AfterReceiveReply);
epaulsen marked this conversation as resolved.
Show resolved Hide resolved
break;
}
};
}

options.OutgoingRequestFilter = (Message m) =>
{
return !filter;
Expand Down Expand Up @@ -226,6 +244,12 @@ public async Task OutgoingRequestInstrumentationTest(
{
Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapMessageVersionTag).Value);
}

if (enrich)
{
Assert.Equal(WcfEnrichEventNames.BeforeSendRequest, activity.TagObjects.Single(t => t.Key == "client.beforesendrequest").Value);
Assert.Equal(WcfEnrichEventNames.AfterReceiveReply, activity.TagObjects.Single(t => t.Key == "client.afterreceivereply").Value);
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,12 @@ public void Dispose()
[InlineData(true, true)]
[InlineData(false)]
[InlineData(true, false, true)]
[InlineData(true, false, true, true)]
public async Task IncomingRequestInstrumentationTest(
bool instrument,
bool filter = false,
bool includeVersion = false)
bool includeVersion = false,
bool enrich = false)
{
List<Activity> stoppedActivities = new List<Activity>();

Expand All @@ -108,6 +110,22 @@ public async Task IncomingRequestInstrumentationTest(
tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddWcfInstrumentation(options =>
{
if (enrich)
{
options.Enrich = (activity, eventName, message) =>
{
switch (eventName)
{
case WcfEnrichEventNames.AfterReceiveRequest:
activity.AddTag("server.afterreceiverequest", WcfEnrichEventNames.AfterReceiveRequest);
epaulsen marked this conversation as resolved.
Show resolved Hide resolved
break;
case WcfEnrichEventNames.BeforeSendReply:
activity.AddTag("server.beforesendreply", WcfEnrichEventNames.BeforeSendReply);
epaulsen marked this conversation as resolved.
Show resolved Hide resolved
break;
}
};
}

options.IncomingRequestFilter = (Message m) =>
{
return !filter;
Expand Down Expand Up @@ -164,6 +182,12 @@ public async Task IncomingRequestInstrumentationTest(
{
Assert.Equal("Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapMessageVersionTag).Value);
}

if (enrich)
{
Assert.Equal(WcfEnrichEventNames.AfterReceiveRequest, activity.TagObjects.Single(t => t.Key == "server.afterreceiverequest").Value);
Assert.Equal(WcfEnrichEventNames.BeforeSendReply, activity.TagObjects.Single(t => t.Key == "server.beforesendreply").Value);
}
}
else
{
Expand Down