Skip to content

Commit df49552

Browse files
authored
Issue #125:Adding enricher for Wcf instrumentation (#126)
1 parent e6b225e commit df49552

9 files changed

Lines changed: 214 additions & 6 deletions

File tree

src/OpenTelemetry.Contrib.Instrumentation.Wcf/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22

33
## Unreleased
44

5+
* Added enricher for WCF activity
6+
([#126])(<https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/126>)
7+
58
* Updated OTel SDK package version to 1.1.0-beta1
69
([#100](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/100))

src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ public void RequestFilterException(string exception)
4747
this.WriteEvent(EventIds.RequestFilterException, exception);
4848
}
4949

50+
[NonEvent]
51+
public void EnrichmentException(Exception exception)
52+
{
53+
if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1)))
54+
{
55+
this.EnrichmentException(ToInvariantString(exception));
56+
}
57+
}
58+
59+
[Event(EventIds.EnrichmentException, Message = "Enrichment threw exception. Exception {0}.", Level = EventLevel.Error)]
60+
public void EnrichmentException(string exception)
61+
{
62+
this.WriteEvent(EventIds.EnrichmentException, exception);
63+
}
64+
5065
/// <summary>
5166
/// Returns a culture-independent string representation of the given <paramref name="exception"/> object,
5267
/// appropriate for diagnostics tracing.
@@ -70,6 +85,7 @@ private class EventIds
7085
{
7186
public const int RequestIsFilteredOut = 1;
7287
public const int RequestFilterException = 2;
88+
public const int EnrichmentException = 3;
7389
}
7490
}
7591
}

src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,33 @@ using var openTelemetry = Sdk.CreateTracerProviderBuilder()
2020
.Build();
2121
```
2222

23+
Instrumentation can be configured via options overload for
24+
`AddWcfInstrumentation` method:
25+
26+
```csharp
27+
using var openTelemetry = Sdk.CreateTracerProviderBuilder()
28+
.AddWcfInstrumentation(options =>
29+
{
30+
options.SuppressDownstreamInstrumentation = false;
31+
32+
// Enable enriching an activity after it is created.
33+
options.Enrich = (activity, eventName, message) =>
34+
{
35+
var wcfMessage = message as Message;
36+
37+
// For event names, please refer to string constants in
38+
// WcfEnrichEventNames class.
39+
if (eventName == WcfEnrichEventNames.AfterReceiveReply)
40+
{
41+
activity.SetTag(
42+
"companyname.messagetag",
43+
wcfMessage.Properties["CustomProperty"]);
44+
}
45+
};
46+
})
47+
.Build();
48+
```
49+
2350
## WCF Client Configuration (.NET Framework)
2451

2552
Add the `IClientMessageInspector` instrumentation via a behavior extension on

src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel)
6464
Activity activity = WcfInstrumentationActivitySource.ActivitySource.StartActivity(
6565
WcfInstrumentationActivitySource.OutgoingRequestActivityName,
6666
ActivityKind.Client);
67-
6867
IDisposable suppressionScope = this.SuppressDownstreamInstrumentation();
6968

7069
if (activity != null)
@@ -111,6 +110,15 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel)
111110
{
112111
activity.SetTag(WcfInstrumentationConstants.SoapViaTag, request.Properties.Via.ToString());
113112
}
113+
114+
try
115+
{
116+
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.BeforeSendRequest, request);
117+
}
118+
catch (Exception ex)
119+
{
120+
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
121+
}
114122
}
115123
}
116124

@@ -139,6 +147,14 @@ public void AfterReceiveReply(ref Message reply, object correlationState)
139147
}
140148

141149
activity.SetTag(WcfInstrumentationConstants.SoapReplyActionTag, reply.Headers.Action);
150+
try
151+
{
152+
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.AfterReceiveReply, reply);
153+
}
154+
catch (Exception ex)
155+
{
156+
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
157+
}
142158
}
143159

144160
activity.Stop();

src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I
9696
activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, localAddressUri.Scheme);
9797
activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, localAddressUri.LocalPath);
9898
}
99+
100+
try
101+
{
102+
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.AfterReceiveRequest, request);
103+
}
104+
catch (Exception ex)
105+
{
106+
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
107+
}
99108
}
100109
}
101110

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

123132
activity.SetTag(WcfInstrumentationConstants.SoapReplyActionTag, reply.Headers.Action);
133+
try
134+
{
135+
WcfInstrumentationActivitySource.Options.Enrich?.Invoke(activity, WcfEnrichEventNames.BeforeSendReply, reply);
136+
}
137+
catch (Exception ex)
138+
{
139+
WcfInstrumentationEventSource.Log.EnrichmentException(ex);
140+
}
124141
}
125142

126143
activity.Stop();
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// <copyright file="WcfEnrichEventNames.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
namespace OpenTelemetry.Contrib.Instrumentation.Wcf
18+
{
19+
/// <summary>
20+
/// Constants used for event names when enriching an activity.
21+
/// </summary>
22+
public class WcfEnrichEventNames
23+
{
24+
#if NETFRAMEWORK
25+
/// <summary>
26+
/// WCF service activity, event happens before WCF service method is invoked.
27+
/// </summary>
28+
public const string AfterReceiveRequest = "AfterReceiveRequest";
29+
30+
/// <summary>
31+
/// WCF service activity, event happens after the WCF service method is invoked but before the reply is sent back to the client.
32+
/// </summary>
33+
public const string BeforeSendReply = "BeforeSendReply";
34+
#endif
35+
36+
/// <summary>
37+
/// WCF client activity, event happens before the request is sent across the wire.
38+
/// </summary>
39+
public const string BeforeSendRequest = "BeforeSendRequest";
40+
41+
/// <summary>
42+
/// WCF client activity, event happens after a reply from the WCF service is received.
43+
/// </summary>
44+
public const string AfterReceiveReply = "AfterReceiveReply";
45+
}
46+
}

src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
// </copyright>
1616

1717
using System;
18+
using System.Diagnostics;
1819
using System.ServiceModel.Channels;
1920
using OpenTelemetry.Context.Propagation;
2021

@@ -34,6 +35,18 @@ public class WcfInstrumentationOptions
3435
new BaggagePropagator(),
3536
});
3637

38+
/// <summary>
39+
/// Gets or sets an action to enrich an Activity.
40+
/// </summary>
41+
/// <remarks>
42+
/// <para><see cref="Activity"/>: the activity being enriched.</para>
43+
/// <para>string: the name of the event. Will be one of the constants in <see cref="WcfEnrichEventNames"/>.
44+
/// </para>
45+
/// <para>object: the raw <see cref="Message"/> from which additional information can be extracted to enrich the activity.
46+
/// </para>
47+
/// </remarks>
48+
public Action<Activity, string, object> Enrich { get; set; }
49+
3750
/// <summary>
3851
/// Gets or sets a Filter function to filter instrumentation for requests on a per request basis.
3952
/// The Filter gets the Message, and should return a boolean.

test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,15 @@ public void Dispose()
126126
[InlineData(true, false, false)]
127127
[InlineData(false)]
128128
[InlineData(true, false, true, true)]
129+
[InlineData(true, false, true, true, true)]
130+
[InlineData(true, false, true, true, true, true)]
129131
public async Task OutgoingRequestInstrumentationTest(
130132
bool instrument,
131133
bool filter = false,
132134
bool suppressDownstreamInstrumentation = true,
133-
bool includeVersion = false)
135+
bool includeVersion = false,
136+
bool enrich = false,
137+
bool enrichmentException = false)
134138
{
135139
#if NETFRAMEWORK
136140
const string OutgoingHttpOperationName = "OpenTelemetry.HttpWebRequest.HttpRequestOut";
@@ -147,10 +151,30 @@ public async Task OutgoingRequestInstrumentationTest(
147151
builder
148152
.AddWcfInstrumentation(options =>
149153
{
150-
options.OutgoingRequestFilter = (Message m) =>
154+
if (enrich)
151155
{
152-
return !filter;
153-
};
156+
if (!enrichmentException)
157+
{
158+
options.Enrich = (activity, eventName, message) =>
159+
{
160+
switch (eventName)
161+
{
162+
case WcfEnrichEventNames.BeforeSendRequest:
163+
activity.SetTag("client.beforesendrequest", WcfEnrichEventNames.BeforeSendRequest);
164+
break;
165+
case WcfEnrichEventNames.AfterReceiveReply:
166+
activity.SetTag("client.afterreceivereply", WcfEnrichEventNames.AfterReceiveReply);
167+
break;
168+
}
169+
};
170+
}
171+
else
172+
{
173+
options.Enrich = (activity, eventName, message) => throw new Exception("Error while enriching activity");
174+
}
175+
}
176+
177+
options.OutgoingRequestFilter = (Message m) => !filter;
154178
options.SuppressDownstreamInstrumentation = suppressDownstreamInstrumentation;
155179
options.SetSoapMessageVersion = includeVersion;
156180
})
@@ -226,6 +250,12 @@ public async Task OutgoingRequestInstrumentationTest(
226250
{
227251
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);
228252
}
253+
254+
if (enrich && !enrichmentException)
255+
{
256+
Assert.Equal(WcfEnrichEventNames.BeforeSendRequest, activity.TagObjects.Single(t => t.Key == "client.beforesendrequest").Value);
257+
Assert.Equal(WcfEnrichEventNames.AfterReceiveReply, activity.TagObjects.Single(t => t.Key == "client.afterreceivereply").Value);
258+
}
229259
}
230260
else
231261
{

test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,14 @@ public void Dispose()
8787
[InlineData(true, true)]
8888
[InlineData(false)]
8989
[InlineData(true, false, true)]
90+
[InlineData(true, false, true, true)]
91+
[InlineData(true, false, true, true, true)]
9092
public async Task IncomingRequestInstrumentationTest(
9193
bool instrument,
9294
bool filter = false,
93-
bool includeVersion = false)
95+
bool includeVersion = false,
96+
bool enrich = false,
97+
bool enrichmentExcecption = false)
9498
{
9599
List<Activity> stoppedActivities = new List<Activity>();
96100

@@ -108,6 +112,36 @@ public async Task IncomingRequestInstrumentationTest(
108112
tracerProvider = Sdk.CreateTracerProviderBuilder()
109113
.AddWcfInstrumentation(options =>
110114
{
115+
if (enrich)
116+
{
117+
if (!enrichmentExcecption)
118+
{
119+
options.Enrich = (activity, eventName, message) =>
120+
{
121+
switch (eventName)
122+
{
123+
case WcfEnrichEventNames.AfterReceiveRequest:
124+
activity.SetTag(
125+
"server.afterreceiverequest",
126+
WcfEnrichEventNames.AfterReceiveRequest);
127+
break;
128+
case WcfEnrichEventNames.BeforeSendReply:
129+
activity.SetTag(
130+
"server.beforesendreply",
131+
WcfEnrichEventNames.BeforeSendReply);
132+
break;
133+
}
134+
};
135+
}
136+
else
137+
{
138+
options.Enrich = (activity, eventName, message) =>
139+
{
140+
throw new Exception("Failure whilst enriching activity");
141+
};
142+
}
143+
}
144+
111145
options.IncomingRequestFilter = (Message m) =>
112146
{
113147
return !filter;
@@ -164,6 +198,12 @@ public async Task IncomingRequestInstrumentationTest(
164198
{
165199
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);
166200
}
201+
202+
if (enrich && !enrichmentExcecption)
203+
{
204+
Assert.Equal(WcfEnrichEventNames.AfterReceiveRequest, activity.TagObjects.Single(t => t.Key == "server.afterreceiverequest").Value);
205+
Assert.Equal(WcfEnrichEventNames.BeforeSendReply, activity.TagObjects.Single(t => t.Key == "server.beforesendreply").Value);
206+
}
167207
}
168208
else
169209
{

0 commit comments

Comments
 (0)