-
Notifications
You must be signed in to change notification settings - Fork 21
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
feat: Move OTEL hooks to the SDK #338
Open
askpt
wants to merge
20
commits into
main
Choose a base branch
from
askpt/175-promote-otel-hooks
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e7d40c7
Added metrics hook.
askpt 46e5711
Adding the traces hook.
askpt ced2d34
Adding Metrics Hook tests.
askpt 633859c
Adding tracing hook tests.
askpt 6d4378f
Fix typos.
askpt 8a32ca9
Simplify code.
askpt 629b900
Cleanup tracing hook tests.
askpt 766caa5
More test cleanup.
askpt 703f418
Cleanup tests.
askpt 729bd65
Adding empty span.
askpt 7f5af18
Adding .ConfigureAwait
askpt c0b6cc0
Fix dotnet format.
askpt d0157ae
Removed comma
askpt 902a102
Adding README information.
askpt 60813c5
Merge branch 'main' into askpt/175-promote-otel-hooks
askpt 6a3d911
Using the new API.
askpt 52b47da
Fix subscribed Meter.
askpt 8e1e884
Merge branch 'main' into askpt/175-promote-otel-hooks
askpt 1a0c16d
Merge branch 'main' into askpt/175-promote-otel-hooks
askpt 41c915d
Fix breaking change.
askpt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
namespace OpenFeature.Hooks; | ||
|
||
internal static class MetricsConstants | ||
{ | ||
internal const string ActiveCountName = "feature_flag.evaluation_active_count"; | ||
internal const string RequestsTotalName = "feature_flag.evaluation_requests_total"; | ||
internal const string SuccessTotalName = "feature_flag.evaluation_success_total"; | ||
internal const string ErrorTotalName = "feature_flag.evaluation_error_total"; | ||
|
||
internal const string ActiveDescription = "active flag evaluations counter"; | ||
internal const string RequestsDescription = "feature flag evaluation request counter"; | ||
internal const string SuccessDescription = "feature flag evaluation success counter"; | ||
internal const string ErrorDescription = "feature flag evaluation error counter"; | ||
|
||
internal const string KeyAttr = "key"; | ||
internal const string ProviderNameAttr = "provider_name"; | ||
internal const string VariantAttr = "variant"; | ||
internal const string ReasonAttr = "reason"; | ||
internal const string ExceptionAttr = "exception"; | ||
} |
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,103 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Diagnostics.Metrics; | ||
using System.Reflection; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using OpenFeature.Model; | ||
|
||
namespace OpenFeature.Hooks; | ||
|
||
/// <summary> | ||
/// Represents a hook for capturing metrics related to flag evaluations. | ||
/// The meter instrumentation name is "OpenFeature". | ||
/// </summary> | ||
public class MetricsHook : Hook | ||
{ | ||
private static readonly AssemblyName AssemblyName = typeof(MetricsHook).Assembly.GetName(); | ||
private static readonly string InstrumentationName = AssemblyName.Name ?? "OpenFeature"; | ||
private static readonly string InstrumentationVersion = AssemblyName.Version?.ToString() ?? "1.0.0"; | ||
|
||
private readonly UpDownCounter<long> _evaluationActiveUpDownCounter; | ||
private readonly Counter<long> _evaluationRequestCounter; | ||
private readonly Counter<long> _evaluationSuccessCounter; | ||
private readonly Counter<long> _evaluationErrorCounter; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MetricsHook"/> class. | ||
/// </summary> | ||
public MetricsHook() | ||
{ | ||
var meter = new Meter(InstrumentationName, InstrumentationVersion); | ||
|
||
this._evaluationActiveUpDownCounter = meter.CreateUpDownCounter<long>(MetricsConstants.ActiveCountName, description: MetricsConstants.ActiveDescription); | ||
this._evaluationRequestCounter = meter.CreateCounter<long>(MetricsConstants.RequestsTotalName, "{request}", MetricsConstants.RequestsDescription); | ||
this._evaluationSuccessCounter = meter.CreateCounter<long>(MetricsConstants.SuccessTotalName, "{impression}", MetricsConstants.SuccessDescription); | ||
this._evaluationErrorCounter = meter.CreateCounter<long>(MetricsConstants.ErrorTotalName, description: MetricsConstants.ErrorDescription); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override ValueTask<EvaluationContext> BeforeAsync<T>(HookContext<T> context, IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default) | ||
{ | ||
var tagList = new TagList | ||
{ | ||
{ MetricsConstants.KeyAttr, context.FlagKey }, | ||
{ MetricsConstants.ProviderNameAttr, context.ProviderMetadata.Name } | ||
}; | ||
|
||
this._evaluationActiveUpDownCounter.Add(1, tagList); | ||
this._evaluationRequestCounter.Add(1, tagList); | ||
|
||
return base.BeforeAsync(context, hints, cancellationToken); | ||
} | ||
|
||
|
||
/// <inheritdoc/> | ||
public override ValueTask AfterAsync<T>(HookContext<T> context, FlagEvaluationDetails<T> details, IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default) | ||
{ | ||
var tagList = new TagList | ||
{ | ||
{ MetricsConstants.KeyAttr, context.FlagKey }, | ||
{ MetricsConstants.ProviderNameAttr, context.ProviderMetadata.Name }, | ||
{ MetricsConstants.VariantAttr, details.Variant ?? details.Value?.ToString() }, | ||
{ MetricsConstants.ReasonAttr, details.Reason ?? "UNKNOWN" } | ||
}; | ||
|
||
this._evaluationSuccessCounter.Add(1, tagList); | ||
|
||
return base.AfterAsync(context, details, hints, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override ValueTask ErrorAsync<T>(HookContext<T> context, Exception error, IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default) | ||
{ | ||
var tagList = new TagList | ||
{ | ||
{ MetricsConstants.KeyAttr, context.FlagKey }, | ||
{ MetricsConstants.ProviderNameAttr, context.ProviderMetadata.Name }, | ||
{ MetricsConstants.ExceptionAttr, error.Message } | ||
}; | ||
|
||
this._evaluationErrorCounter.Add(1, tagList); | ||
|
||
return base.ErrorAsync(context, error, hints, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override ValueTask FinallyAsync<T>(HookContext<T> context, | ||
FlagEvaluationDetails<T> evaluationDetails, | ||
IReadOnlyDictionary<string, object>? hints = null, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
var tagList = new TagList | ||
{ | ||
{ MetricsConstants.KeyAttr, context.FlagKey }, | ||
{ MetricsConstants.ProviderNameAttr, context.ProviderMetadata.Name } | ||
}; | ||
|
||
this._evaluationActiveUpDownCounter.Add(-1, tagList); | ||
|
||
return base.FinallyAsync(context, evaluationDetails, hints, cancellationToken); | ||
} | ||
} |
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,9 @@ | ||
namespace OpenFeature.Hooks; | ||
|
||
internal static class TracingConstants | ||
{ | ||
internal const string AttributeExceptionEventName = "exception"; | ||
internal const string AttributeExceptionType = "exception.type"; | ||
internal const string AttributeExceptionMessage = "exception.message"; | ||
internal const string AttributeExceptionStacktrace = "exception.stacktrace"; | ||
} |
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,54 @@ | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using OpenFeature.Model; | ||
|
||
namespace OpenFeature.Hooks; | ||
|
||
/// <summary> | ||
/// Stub. | ||
/// </summary> | ||
public class TracingHook : Hook | ||
{ | ||
/// <inheritdoc/> | ||
public override ValueTask AfterAsync<T>(HookContext<T> context, FlagEvaluationDetails<T> details, | ||
IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default) | ||
{ | ||
Activity.Current? | ||
.SetTag("feature_flag.key", details.FlagKey) | ||
.SetTag("feature_flag.variant", details.Variant) | ||
.SetTag("feature_flag.provider_name", context.ProviderMetadata.Name) | ||
.AddEvent(new ActivityEvent("feature_flag", tags: new ActivityTagsCollection | ||
{ | ||
["feature_flag.key"] = details.FlagKey, | ||
["feature_flag.variant"] = details.Variant, | ||
["feature_flag.provider_name"] = context.ProviderMetadata.Name | ||
})); | ||
|
||
return default; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override ValueTask ErrorAsync<T>(HookContext<T> context, System.Exception error, | ||
IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default) | ||
{ | ||
#if NET9_0_OR_GREATER | ||
// For dotnet9 we should use the new API https://learn.microsoft.com/en-gb/dotnet/api/system.diagnostics.activity.addexception?view=net-9.0 | ||
Activity.Current?.AddException(error); | ||
#else | ||
var tagsCollection = new ActivityTagsCollection | ||
{ | ||
{ TracingConstants.AttributeExceptionType, error.GetType().FullName }, | ||
{ TracingConstants.AttributeExceptionStacktrace, error.ToString() }, | ||
}; | ||
if (!string.IsNullOrWhiteSpace(error.Message)) | ||
{ | ||
tagsCollection.Add(TracingConstants.AttributeExceptionMessage, error.Message); | ||
} | ||
|
||
Activity.Current?.AddEvent(new ActivityEvent(TracingConstants.AttributeExceptionEventName, default, tagsCollection)); | ||
#endif | ||
return default; | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doc doesn't appear correct.