-
Notifications
You must be signed in to change notification settings - Fork 167
Expand file tree
/
Copy pathDatadogProvider.cs
More file actions
187 lines (165 loc) · 8.13 KB
/
Copy pathDatadogProvider.cs
File metadata and controls
187 lines (165 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// <copyright file="DatadogProvider.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using OpenFeature;
using OpenFeature.Constant;
using OpenFeature.Model;
namespace Datadog.FeatureFlags.OpenFeature;
/// <summary>
/// OpenFeature V2.0.0+ Provider for Datadog
/// </summary>
public sealed class DatadogProvider : global::OpenFeature.FeatureProvider, IDisposable
{
private static Action? _onNewConfig = null;
private readonly Metadata _metadata = new Metadata("datadog-openfeature-provider");
#if NET6_0_OR_GREATER
private readonly FlagEvalMetricsHook _metricsHook;
#endif
// Span-enrichment hook is constructed ONLY when the gate is on; null otherwise so
// nothing is allocated/registered when the feature is disabled.
private readonly SpanEnrichmentHook? _spanEnrichmentHook;
/// <summary> Initializes a new instance of the <see cref="DatadogProvider"/> class. </summary>
public DatadogProvider()
{
FeatureFlagsSdk.RegisterOnNewConfigEventHandler(() => SignalGeneralUpdate());
#if NET6_0_OR_GREATER
_metricsHook = new FlagEvalMetricsHook();
#endif
if (FeatureFlagsSdk.IsSpanEnrichmentEnabled())
{
_spanEnrichmentHook = new SpanEnrichmentHook();
}
}
/// <summary> Gets a value indicating whether the Datadog's provider is instrumented and available </summary>
public static bool IsAvailable => FeatureFlagsSdk.IsAvailable();
/// <summary> Notifies when a new config is available </summary>
/// <param name="onNewConfig"> Action to be called </param>
public static void RegisterOnNewConfigEventHandler(Action onNewConfig)
{
_onNewConfig = onNewConfig;
}
private void SignalGeneralUpdate()
{
try
{
_onNewConfig?.Invoke();
// You don't have to provide specific flag keys
var payload = new ProviderEventPayload
{
Message = "A backend update occurred, but specific changes are unknown."
};
EventChannel.Writer.TryWrite((object)payload);
}
catch { }
}
/// <summary>
/// Starts flag configuration delivery and waits for the first configuration, so that a ready
/// provider can actually resolve flags. It returns normally when the wait times out instead of
/// throwing: slow delivery is transient, and initialization commonly runs at startup, where an
/// exception would take the application down.
/// </summary>
/// <param name="context"> Evaluation context </param>
/// <param name="cancellationToken"> Async cancellation token </param>
/// <returns> A task that completes when initialization is complete </returns>
public override Task InitializeAsync(EvaluationContext context, CancellationToken cancellationToken = default)
=> FeatureFlagsSdk.InitializeAsync(cancellationToken);
/// <summary> Gets provider metadata </summary>
/// <returns> Returns provider metadata </returns>
public override Metadata? GetMetadata() => _metadata;
/// <summary> Resolve flag as boolean </summary>
/// <param name="flagKey"> Requested flag </param>
/// <param name="defaultValue"> Default value </param>
/// <param name="context"> Evaluation context </param>
/// <param name="cancellationToken"> Async cancelation token </param>
/// <returns> Returns the evaluation result </returns>
public override Task<ResolutionDetails<bool>> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var res = FeatureFlagsSdk.Resolve<bool>(flagKey, Trace.FeatureFlags.ValueType.Boolean, defaultValue, context);
return Task.FromResult(res);
}
/// <summary> Resolve flag as double </summary>
/// <param name="flagKey"> Requested flag </param>
/// <param name="defaultValue"> Default value </param>
/// <param name="context"> Evaluation context </param>
/// <param name="cancellationToken"> Async cancelation token </param>
/// <returns> Returns the evaluation result </returns>
public override Task<ResolutionDetails<double>> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var res = FeatureFlagsSdk.Resolve<double>(flagKey, Trace.FeatureFlags.ValueType.Numeric, defaultValue, context);
return Task.FromResult(res);
}
/// <summary> Resolve flag as integer </summary>
/// <param name="flagKey"> Requested flag </param>
/// <param name="defaultValue"> Default value </param>
/// <param name="context"> Evaluation context </param>
/// <param name="cancellationToken"> Async cancelation token </param>
/// <returns> Returns the evaluation result </returns>
public override Task<ResolutionDetails<int>> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var res = FeatureFlagsSdk.Resolve<int>(flagKey, Trace.FeatureFlags.ValueType.Integer, defaultValue, context);
return Task.FromResult(res);
}
/// <summary> Resolve flag as string </summary>
/// <param name="flagKey"> Requested flag </param>
/// <param name="defaultValue"> Default value </param>
/// <param name="context"> Evaluation context </param>
/// <param name="cancellationToken"> Async cancelation token </param>
/// <returns> Returns the evaluation result </returns>
public override Task<ResolutionDetails<string>> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var res = FeatureFlagsSdk.Resolve<string>(flagKey, Trace.FeatureFlags.ValueType.String, defaultValue, context);
return Task.FromResult(res);
}
/// <summary> Resolve flag as Value </summary>
/// <param name="flagKey"> Requested flag </param>
/// <param name="defaultValue"> Default value </param>
/// <param name="context"> Evaluation context </param>
/// <param name="cancellationToken"> Async cancelation token </param>
/// <returns> Returns the evaluation result </returns>
public override Task<ResolutionDetails<Value>> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var res = FeatureFlagsSdk.Resolve<Value>(flagKey, Trace.FeatureFlags.ValueType.Json, defaultValue, context);
return Task.FromResult(res);
}
/// <summary> Gets provider hooks for flag evaluation metrics tracking. </summary>
/// <returns> Returns the list of provider hooks. </returns>
public override IImmutableList<Hook> GetProviderHooks()
{
#if NET6_0_OR_GREATER
if (_spanEnrichmentHook is not null)
{
return ImmutableList.Create<Hook>(_metricsHook, _spanEnrichmentHook);
}
return ImmutableList.Create<Hook>(_metricsHook);
#else
if (_spanEnrichmentHook is not null)
{
return ImmutableList.Create<Hook>(_spanEnrichmentHook);
}
return ImmutableList<Hook>.Empty;
#endif
}
/// <inheritdoc/>
public void Dispose()
{
#if NET6_0_OR_GREATER
_metricsHook.Dispose();
#endif
// The span-enrichment hook owns no resources and per-trace enrichment state is released with
// the trace context, so there's nothing to dispose on provider close.
}
}