Skip to content

Commit ea7bc67

Browse files
committed
Add flag evaluation metrics via OpenFeature hook
1 parent 6aadb93 commit ea7bc67

15 files changed

Lines changed: 344 additions & 7490 deletions

tracer/build/PackageVersionsGeneratorDefinitions.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -968,10 +968,10 @@
968968
"IntegrationName": "OpenFeature",
969969
"SampleProjectName": "Samples.OpenFeature",
970970
"NugetPackageSearchName": "OpenFeature",
971-
"MinVersion": "2.0.0",
971+
"MinVersion": "2.3.0",
972972
"MaxVersionExclusive": "3.0.0",
973973
"SpecificVersions": [
974-
"2.0.0",
974+
"2.3.0",
975975
"2.10.0"
976976
]
977977
}

tracer/build/PackageVersionsLatestMajors.g.props

Lines changed: 15 additions & 658 deletions
Large diffs are not rendered by default.

tracer/build/PackageVersionsLatestMinors.g.props

Lines changed: 7 additions & 4474 deletions
Large diffs are not rendered by default.

tracer/build/PackageVersionsLatestSpecific.g.props

Lines changed: 32 additions & 991 deletions
Large diffs are not rendered by default.

tracer/build/nuget_version_cache.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14257,7 +14257,8 @@
1425714257
"2.11.3",
1425814258
"2.11.8",
1425914259
"2.12.1",
14260-
"2.12.4"
14260+
"2.12.4",
14261+
"2.12.8"
1426114262
]
1426214263
},
1426314264
{

tracer/build/supported_versions.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@
258258
"name": "AWSSDK.DynamoDBv2",
259259
"minVersionAvailableInclusive": "3.1.0",
260260
"minVersionSupportedInclusive": "3.1.0",
261-
"minVersionTestedInclusive": "3.1.5.3",
261+
"minVersionTestedInclusive": "3.3.106.47",
262262
"maxVersionSupportedInclusive": "4.0.17.1",
263263
"maxVersionAvailableInclusive": "4.0.17.1",
264264
"maxVersionTestedInclusive": "4.0.17.1"
@@ -292,7 +292,7 @@
292292
"name": "AWSSDK.Kinesis",
293293
"minVersionAvailableInclusive": "3.1.0",
294294
"minVersionSupportedInclusive": "3.1.0",
295-
"minVersionTestedInclusive": "3.1.3.5",
295+
"minVersionTestedInclusive": "3.3.101.1",
296296
"maxVersionSupportedInclusive": "4.0.8.4",
297297
"maxVersionAvailableInclusive": "4.0.8.4",
298298
"maxVersionTestedInclusive": "4.0.8.4"
@@ -343,7 +343,7 @@
343343
"name": "AWSSDK.Core",
344344
"minVersionAvailableInclusive": "3.1.0",
345345
"minVersionSupportedInclusive": "3.1.0",
346-
"minVersionTestedInclusive": "3.1.11",
346+
"minVersionTestedInclusive": "3.3.107.40",
347347
"maxVersionSupportedInclusive": "4.0.3.21",
348348
"maxVersionAvailableInclusive": "4.0.3.21",
349349
"maxVersionTestedInclusive": "4.0.3.21"
@@ -360,7 +360,7 @@
360360
"name": "AWSSDK.SimpleNotificationService",
361361
"minVersionAvailableInclusive": "3.1.0",
362362
"minVersionSupportedInclusive": "3.1.0",
363-
"minVersionTestedInclusive": "3.1.2.1",
363+
"minVersionTestedInclusive": "3.3.102.17",
364364
"maxVersionSupportedInclusive": "4.0.2.20",
365365
"maxVersionAvailableInclusive": "4.0.2.20",
366366
"maxVersionTestedInclusive": "4.0.2.20"
@@ -377,7 +377,7 @@
377377
"name": "AWSSDK.SQS",
378378
"minVersionAvailableInclusive": "3.1.0",
379379
"minVersionSupportedInclusive": "3.1.0",
380-
"minVersionTestedInclusive": "3.1.0.13",
380+
"minVersionTestedInclusive": "3.3.103.26",
381381
"maxVersionSupportedInclusive": "4.0.2.18",
382382
"maxVersionAvailableInclusive": "4.0.2.18",
383383
"maxVersionTestedInclusive": "4.0.2.18"
@@ -886,7 +886,7 @@
886886
"name": "log4net",
887887
"minVersionAvailableInclusive": "1.2.10",
888888
"minVersionSupportedInclusive": "1.2.10",
889-
"minVersionTestedInclusive": "1.2.11",
889+
"minVersionTestedInclusive": "2.0.17",
890890
"maxVersionSupportedInclusive": "3.3.0",
891891
"maxVersionAvailableInclusive": "3.3.0",
892892
"maxVersionTestedInclusive": "3.3.0"
@@ -903,7 +903,7 @@
903903
"name": "MongoDB.Driver",
904904
"minVersionAvailableInclusive": "1.0.3744.30075",
905905
"minVersionSupportedInclusive": "2.1.0",
906-
"minVersionTestedInclusive": "2.0.2",
906+
"minVersionTestedInclusive": "2.30.0",
907907
"maxVersionSupportedInclusive": "3.7.1",
908908
"maxVersionAvailableInclusive": "3.7.1",
909909
"maxVersionTestedInclusive": "3.7.1"
@@ -920,7 +920,7 @@
920920
"name": "MongoDB.Driver",
921921
"minVersionAvailableInclusive": "1.0.3744.30075",
922922
"minVersionSupportedInclusive": "2.1.0",
923-
"minVersionTestedInclusive": "2.0.2",
923+
"minVersionTestedInclusive": "2.30.0",
924924
"maxVersionSupportedInclusive": "3.5.2",
925925
"maxVersionAvailableInclusive": "3.7.1",
926926
"maxVersionTestedInclusive": "3.7.1"
@@ -1059,7 +1059,7 @@
10591059
"name": "NLog",
10601060
"minVersionAvailableInclusive": "1.0.0.505",
10611061
"minVersionSupportedInclusive": "1.0.0.505",
1062-
"minVersionTestedInclusive": "1.0.0.505",
1062+
"minVersionTestedInclusive": "4.7.15",
10631063
"maxVersionSupportedInclusive": "6.1.1",
10641064
"maxVersionAvailableInclusive": "6.1.1",
10651065
"maxVersionTestedInclusive": "6.1.1"
@@ -1230,7 +1230,7 @@
12301230
"name": "Serilog",
12311231
"minVersionAvailableInclusive": "0.1.6",
12321232
"minVersionSupportedInclusive": "1.0.1",
1233-
"minVersionTestedInclusive": "1.4.214",
1233+
"minVersionTestedInclusive": "2.12.0",
12341234
"maxVersionSupportedInclusive": "4.3.1",
12351235
"maxVersionAvailableInclusive": "4.3.1",
12361236
"maxVersionTestedInclusive": "4.3.1"
@@ -1247,7 +1247,7 @@
12471247
"name": "ServiceStack.Redis",
12481248
"minVersionAvailableInclusive": "2.9.0",
12491249
"minVersionSupportedInclusive": "4.0.2",
1250-
"minVersionTestedInclusive": "4.5.14",
1250+
"minVersionTestedInclusive": "6.1.0",
12511251
"maxVersionSupportedInclusive": "10.0.6",
12521252
"maxVersionAvailableInclusive": "10.0.6",
12531253
"maxVersionTestedInclusive": "10.0.6"
@@ -1339,10 +1339,10 @@
13391339
"name": "StackExchange.Redis",
13401340
"minVersionAvailableInclusive": "1.0.187",
13411341
"minVersionSupportedInclusive": "1.0.187",
1342-
"minVersionTestedInclusive": "1.0.488",
1343-
"maxVersionSupportedInclusive": "2.12.4",
1344-
"maxVersionAvailableInclusive": "2.12.4",
1345-
"maxVersionTestedInclusive": "2.12.4"
1342+
"minVersionTestedInclusive": "1.2.6",
1343+
"maxVersionSupportedInclusive": "2.12.8",
1344+
"maxVersionAvailableInclusive": "2.12.8",
1345+
"maxVersionTestedInclusive": "2.12.8"
13461346
}
13471347
]
13481348
},

tracer/dependabot/integrations/Datadog.Dependabot.StackExchange.Redis.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
<ItemGroup>
99
<!-- Integration: StackExchange.Redis -->
1010
<!-- Assembly: StackExchange.Redis -->
11-
<!-- Latest package https://www.nuget.org/packages/StackExchange.Redis/2.12.4 -->
12-
<PackageReference Include="StackExchange.Redis" Version="2.12.4" />
11+
<!-- Latest package https://www.nuget.org/packages/StackExchange.Redis/2.12.8 -->
12+
<PackageReference Include="StackExchange.Redis" Version="2.12.8" />
1313
</ItemGroup>
1414

1515
</Project>

tracer/src/Datadog.FeatureFlags.OpenFeature/Datadog.FeatureFlags.OpenFeature.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747
<ItemGroup>
4848
<!-- If you update this version, be sure to update the TargetFrameworks element above-->
49-
<PackageReference Include="OpenFeature" Version="2.0.0" />
49+
<PackageReference Include="OpenFeature" Version="2.3.0" />
5050
</ItemGroup>
5151

5252
<PropertyGroup>

tracer/src/Datadog.FeatureFlags.OpenFeature/DatadogProvider.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
using System;
99
using System.Collections.Generic;
10+
using System.Collections.Immutable;
1011
using System.Linq;
1112
using System.Threading;
1213
using System.Threading.Channels;
@@ -20,15 +21,21 @@ namespace Datadog.FeatureFlags.OpenFeature;
2021
/// <summary>
2122
/// OpenFeature V2.0.0+ Provider for Datadog
2223
/// </summary>
23-
public sealed class DatadogProvider : global::OpenFeature.FeatureProvider
24+
public sealed class DatadogProvider : global::OpenFeature.FeatureProvider, IDisposable
2425
{
2526
private static Action? _onNewConfig = null;
26-
private Metadata _metadata = new Metadata("datadog-openfeature-provider");
27+
private readonly Metadata _metadata = new Metadata("datadog-openfeature-provider");
28+
#if NET6_0_OR_GREATER
29+
private readonly FlagEvalMetricsHook _metricsHook;
30+
#endif
2731

2832
/// <summary> Initializes a new instance of the <see cref="DatadogProvider"/> class. </summary>
2933
public DatadogProvider()
3034
{
3135
FeatureFlagsSdk.RegisterOnNewConfigEventHandler(() => SignalGeneralUpdate());
36+
#if NET6_0_OR_GREATER
37+
_metricsHook = new FlagEvalMetricsHook();
38+
#endif
3239
}
3340

3441
/// <summary> Gets a value indicating whether the Datadog's provider is instrumented and available </summary>
@@ -126,4 +133,23 @@ public override Task<ResolutionDetails<Value>> ResolveStructureValueAsync(string
126133
var res = FeatureFlagsSdk.Resolve<Value>(flagKey, Trace.FeatureFlags.ValueType.Json, defaultValue, context);
127134
return Task.FromResult(res);
128135
}
136+
137+
/// <summary> Gets provider hooks for flag evaluation metrics tracking. </summary>
138+
/// <returns> Returns the list of provider hooks. </returns>
139+
public override IImmutableList<Hook> GetProviderHooks()
140+
{
141+
#if NET6_0_OR_GREATER
142+
return ImmutableList.Create<Hook>(_metricsHook);
143+
#else
144+
return ImmutableList<Hook>.Empty;
145+
#endif
146+
}
147+
148+
/// <inheritdoc/>
149+
public void Dispose()
150+
{
151+
#if NET6_0_OR_GREATER
152+
_metricsHook.Dispose();
153+
#endif
154+
}
129155
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// <copyright file="FlagEvalMetrics.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#nullable enable
7+
8+
#if NET6_0_OR_GREATER
9+
using System;
10+
using System.Collections.Generic;
11+
using System.Diagnostics.Metrics;
12+
13+
namespace Datadog.FeatureFlags.OpenFeature;
14+
15+
/// <summary>
16+
/// Manages OTel metric instruments for flag evaluation tracking.
17+
/// Uses System.Diagnostics.Metrics which is the standard .NET metrics API.
18+
/// </summary>
19+
internal sealed class FlagEvalMetrics : IDisposable
20+
{
21+
internal const string MeterName = "Datadog.FeatureFlags.OpenFeature";
22+
internal const string MetricName = "feature_flag.evaluations";
23+
internal const string MetricUnit = "{evaluation}";
24+
internal const string MetricDescription = "Number of feature flag evaluations";
25+
26+
internal const string TagFlagKey = "feature_flag.key";
27+
internal const string TagVariant = "feature_flag.result.variant";
28+
internal const string TagReason = "feature_flag.result.reason";
29+
internal const string TagErrorType = "error.type";
30+
internal const string TagAllocationKey = "feature_flag.result.allocation_key";
31+
32+
internal const string MetadataAllocationKey = "dd_allocationKey";
33+
34+
private readonly Meter _meter;
35+
private readonly Counter<long> _counter;
36+
37+
/// <summary>
38+
/// Initializes a new instance of the <see cref="FlagEvalMetrics"/> class.
39+
/// </summary>
40+
public FlagEvalMetrics()
41+
{
42+
_meter = new Meter(MeterName);
43+
_counter = _meter.CreateCounter<long>(
44+
MetricName,
45+
unit: MetricUnit,
46+
description: MetricDescription);
47+
}
48+
49+
/// <summary>
50+
/// Records a single flag evaluation metric with the specified attributes.
51+
/// </summary>
52+
/// <param name="flagKey">The flag key that was evaluated.</param>
53+
/// <param name="variant">The resolved variant, or empty string if none.</param>
54+
/// <param name="reason">The evaluation reason (e.g., "targeting_match", "error").</param>
55+
/// <param name="errorType">The error type if reason is error, or null.</param>
56+
/// <param name="allocationKey">The allocation key if available, or null.</param>
57+
public void Record(
58+
string flagKey,
59+
string? variant,
60+
string reason,
61+
string? errorType,
62+
string? allocationKey)
63+
{
64+
var tags = new List<KeyValuePair<string, object?>>
65+
{
66+
new(TagFlagKey, flagKey),
67+
new(TagVariant, variant ?? string.Empty),
68+
new(TagReason, reason)
69+
};
70+
71+
if (!string.IsNullOrEmpty(errorType))
72+
{
73+
tags.Add(new(TagErrorType, errorType));
74+
}
75+
76+
if (!string.IsNullOrEmpty(allocationKey))
77+
{
78+
tags.Add(new(TagAllocationKey, allocationKey));
79+
}
80+
81+
_counter.Add(1, tags.ToArray());
82+
}
83+
84+
/// <inheritdoc/>
85+
public void Dispose()
86+
{
87+
_meter.Dispose();
88+
}
89+
}
90+
#endif

0 commit comments

Comments
 (0)