-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add OpenTelemetry Instrumentation Provider (#68)
Add OpenTelemetry Instrumentation Provider, and MessageEnvelope.Metadata is now serialized and uses JsonElements explicitly
- Loading branch information
Showing
19 changed files
with
605 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
src/AWS.Messaging.Telemetry.OpenTelemetry/AWS.Messaging.Telemetry.OpenTelemetry.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<IsPackable>true</IsPackable> | ||
<Product>AWS Message Processing Framework Instrumention for OpenTelemetry</Product> | ||
<PackageProjectUrl>https://github.com/awslabs/aws-dotnet-messaging</PackageProjectUrl> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<RollForward>Major</RollForward> | ||
<PackageReadmeFile>README.md</PackageReadmeFile> | ||
<WarningsAsErrors>CA1727</WarningsAsErrors> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="OpenTelemetry" Version="1.6.0" /> | ||
<PackageReference Include="OpenTelemetry.Api" Version="1.6.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\AWS.Messaging\AWS.Messaging.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" /> | ||
<None Include="..\..\NOTICE" Pack="true" PackagePath="" /> | ||
<None Include="..\..\THIRD_PARTY_LICENSES" Pack="true" PackagePath="" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include=".\README.md" Pack="true" PackagePath="" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
namespace AWS.Messaging.Telemetry.OpenTelemetry; | ||
|
||
/// <summary> | ||
/// Constants related to the OpenTelemetry instrumentation for AWS.Messaging | ||
/// </summary> | ||
public class Constants | ||
{ | ||
/// <summary> | ||
/// OpenTelemetry activity source name | ||
/// </summary> | ||
public const string SourceName = "AWS.Messaging"; | ||
} |
83 changes: 83 additions & 0 deletions
83
src/AWS.Messaging.Telemetry.OpenTelemetry/OpenTelemetryProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
using System.Diagnostics; | ||
using OpenTelemetry; | ||
using OpenTelemetry.Context.Propagation; | ||
|
||
namespace AWS.Messaging.Telemetry.OpenTelemetry; | ||
|
||
/// <summary> | ||
/// Creates OpenTelemetry traces | ||
/// </summary> | ||
public class OpenTelemetryProvider : ITelemetryProvider | ||
{ | ||
private static readonly ActivitySource _activitySource = new ActivitySource(Constants.SourceName, TelemetryKeys.AWSMessagingAssemblyVersion); | ||
|
||
/// <inheritdoc/> | ||
public ITelemetryTrace Trace(string traceName) | ||
{ | ||
var activity = _activitySource.StartActivity(traceName, ActivityKind.Producer); | ||
if (activity != null) | ||
{ | ||
return new OpenTelemetryTrace(activity); | ||
} | ||
|
||
// If we initially failed to create an activity, attempt to force creation with | ||
// a link to the current activity, see https://opentelemetry.io/docs/instrumentation/net/manual/#creating-new-root-activities | ||
var parentActivity = Activity.Current; | ||
Activity.Current = null; | ||
ActivityLink[]? links = null; | ||
if (parentActivity != null) | ||
{ | ||
links = new[] { new ActivityLink(parentActivity.Context) }; | ||
} | ||
|
||
activity = _activitySource.StartActivity(traceName, ActivityKind.Producer, parentContext: default, links: links); | ||
|
||
return new OpenTelemetryTrace(activity, parentActivity); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public ITelemetryTrace Trace(string traceName, MessageEnvelope envelope) | ||
{ | ||
var propogatedContext = Propagators.DefaultTextMapPropagator.Extract(default, envelope, ExtractTraceContextFromEnvelope); | ||
Baggage.Current = propogatedContext.Baggage; | ||
|
||
var activity = _activitySource.StartActivity(traceName, ActivityKind.Consumer, parentContext: propogatedContext.ActivityContext); | ||
if (activity != null) | ||
{ | ||
return new OpenTelemetryTrace(activity); | ||
} | ||
|
||
// If we initially failed to create an activity, attempt to force creation with | ||
// a link to the current activity, see https://opentelemetry.io/docs/instrumentation/net/manual/#creating-new-root-activities | ||
var parentActivity = Activity.Current; | ||
Activity.Current = null; | ||
ActivityLink[]? links = null; | ||
if (parentActivity != null) | ||
{ | ||
links = new[] { new ActivityLink(parentActivity.Context) }; | ||
} | ||
|
||
activity = _activitySource.StartActivity(traceName, ActivityKind.Consumer, parentContext: propogatedContext.ActivityContext, links: links); | ||
|
||
return new OpenTelemetryTrace(activity, parentActivity); | ||
} | ||
|
||
/// <summary> | ||
/// Extracts propagation context from a <see cref="MessageEnvelope"/>, meant to be used with <see cref="TextMapPropagator"/> | ||
/// </summary> | ||
/// <param name="envelope">Inbound message envelope</param> | ||
/// <param name="key">Context key</param> | ||
/// <returns>Context value</returns> | ||
private IEnumerable<string> ExtractTraceContextFromEnvelope(MessageEnvelope envelope, string key) | ||
{ | ||
if (envelope.Metadata.TryGetValue(key, out var jsonElement)) | ||
{ | ||
return new string[] { jsonElement.ToString() }; | ||
} | ||
|
||
return Enumerable.Empty<string>(); | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
src/AWS.Messaging.Telemetry.OpenTelemetry/OpenTelemetryTrace.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
using System.Diagnostics; | ||
using System.Text.Json; | ||
using OpenTelemetry; | ||
using OpenTelemetry.Context.Propagation; | ||
using OpenTelemetry.Trace; | ||
|
||
namespace AWS.Messaging.Telemetry.OpenTelemetry; | ||
|
||
/// <summary> | ||
/// An OpenTelemetry trace (wrapper around a <see cref="Activity"/>) | ||
/// </summary> | ||
public class OpenTelemetryTrace : ITelemetryTrace | ||
{ | ||
private readonly Activity? _activity; | ||
private readonly Activity? _parentToRestore; | ||
|
||
/// <summary> | ||
/// Creates a new trace | ||
/// </summary> | ||
/// <param name="activity">New trace</param> | ||
/// <param name="parentToRestore">Optional parent activity that will be set as <see cref="Activity.Current"/> when this trace is disposed</param> | ||
public OpenTelemetryTrace(Activity? activity, Activity? parentToRestore = null) | ||
{ | ||
_activity = activity; | ||
_parentToRestore = parentToRestore; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void AddException(Exception exception, bool fatal = true) | ||
{ | ||
_activity?.RecordException(exception); | ||
|
||
if (fatal) | ||
{ | ||
_activity?.SetStatus(ActivityStatusCode.Error, exception.Message); | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void AddMetadata(string key, object value) | ||
{ | ||
if (_activity != null && _activity.IsAllDataRequested) | ||
{ | ||
_activity.SetTag(key, value); | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void RecordTelemetryContext(MessageEnvelope envelope) | ||
{ | ||
ActivityContext contextToInject = default; | ||
if (_activity != null) | ||
{ | ||
contextToInject = _activity.Context; | ||
} | ||
// Even if an "AWS.Messaging" activity was not created, we still | ||
// propogate the current activity (if it exists) through the message envelope | ||
else if (Activity.Current != null) | ||
{ | ||
contextToInject = Activity.Current.Context; | ||
} | ||
|
||
Propagators.DefaultTextMapPropagator.Inject(new PropagationContext(contextToInject, Baggage.Current), envelope, InjectTraceContextIntoEnvelope); | ||
} | ||
|
||
/// <summary> | ||
/// Stores propagation context in the <see cref="MessageEnvelope"/>, meant to be used with <see cref="TextMapPropagator"/> | ||
/// </summary> | ||
/// <param name="envelope">Outbound message envelope</param> | ||
/// <param name="key">Context key</param> | ||
/// <param name="value">Context value</param> | ||
private void InjectTraceContextIntoEnvelope(MessageEnvelope envelope, string key, string value) | ||
{ | ||
envelope.Metadata[key] = JsonSerializer.SerializeToElement(value); | ||
} | ||
|
||
private bool _disposed; | ||
|
||
/// <summary> | ||
/// Disposes the inner <see cref="Activity"/>, and also restores the parent activity if set | ||
/// </summary> | ||
/// <param name="disposing">Indicates whether the call comes from Dispose (true) or a finalizer (false)</param> | ||
protected virtual void Dispose(bool disposing) | ||
{ | ||
if (!_disposed) | ||
{ | ||
_activity?.Dispose(); | ||
if (_parentToRestore != null) | ||
{ | ||
Activity.Current = _parentToRestore; | ||
} | ||
_disposed = true; | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
} |
Oops, something went wrong.