From 00f75fcd9ec00b39f9c75ba8178dbaa688a12ebe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 6 Mar 2025 16:37:19 +0000
Subject: [PATCH 01/20] feat: Add TelemetryAttributes for OpenTelemetry
compliant event logging
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/TelemetryAttributes.cs | 53 +++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 src/OpenFeature/Telemetry/TelemetryAttributes.cs
diff --git a/src/OpenFeature/Telemetry/TelemetryAttributes.cs b/src/OpenFeature/Telemetry/TelemetryAttributes.cs
new file mode 100644
index 00000000..fa60cf15
--- /dev/null
+++ b/src/OpenFeature/Telemetry/TelemetryAttributes.cs
@@ -0,0 +1,53 @@
+namespace OpenFeature.Telemetry;
+
+///
+/// The attributes of an OpenTelemetry compliant event for flag evaluation.
+///
+///
+public static class TelemetryAttributes
+{
+ ///
+ /// The lookup key of the feature flag.
+ ///
+ public const string Key = "feature_flag.key";
+
+ ///
+ /// Describes a class of error the operation ended with.
+ ///
+ public const string ErrorCode = "error.type";
+
+ ///
+ /// A semantic identifier for an evaluated flag value.
+ ///
+ public const string Variant = "feature_flag.variant";
+
+ ///
+ /// The unique identifier for the flag evaluation context. For example, the targeting key.
+ ///
+ public const string ContextId = "feature_flag.context.id";
+
+ ///
+ /// A message explaining the nature of an error occurring during flag evaluation.
+ ///
+ public const string ErrorMessage = "feature_flag.evaluation.error.message";
+
+ ///
+ /// The reason code which shows how a feature flag value was determined.
+ ///
+ public const string Reason = "feature_flag.evaluation.reason";
+
+ ///
+ /// Describes a class of error the operation ended with.
+ ///
+ public const string Provider = "feature_flag.provider_name";
+
+ ///
+ /// The identifier of the flag set to which the feature flag belongs.
+ ///
+ public const string FlagSetId = "feature_flag.set.id";
+
+ ///
+ /// The version of the ruleset used during the evaluation. This may be any stable value which uniquely identifies the ruleset.
+ ///
+ public const string Version = "feature_flag.version";
+}
From c6287eb54030903a4d3b67f5c302a26f8da7f2ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 6 Mar 2025 16:38:56 +0000
Subject: [PATCH 02/20] feat: Add TelemetryEvaluationData for feature flag
event logging
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/TelemetryEvaluationData.cs | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 src/OpenFeature/Telemetry/TelemetryEvaluationData.cs
diff --git a/src/OpenFeature/Telemetry/TelemetryEvaluationData.cs b/src/OpenFeature/Telemetry/TelemetryEvaluationData.cs
new file mode 100644
index 00000000..f143dc02
--- /dev/null
+++ b/src/OpenFeature/Telemetry/TelemetryEvaluationData.cs
@@ -0,0 +1,20 @@
+namespace OpenFeature.Telemetry;
+
+/**
+* Event data, sometimes referred to as "body", is specific to a specific event.
+* In this case, the event is `feature_flag.evaluation`. That's why the prefix
+* is omitted from the values.
+* @see https://opentelemetry.io/docs/specs/semconv/feature-flags/feature-flags-logs/
+*/
+public static class TelemetryEvaluationData
+{
+ /**
+ * The evaluated value of the feature flag.
+ *
+ * - type: `undefined`
+ * - requirement level: `conditionally required`
+ * - condition: variant is not defined on the evaluation details
+ * - example: `#ff0000`; `1`; `true`
+ */
+ public const string Value = "value";
+}
From 38a110a2954431c2d8e6f268a97ff8e6263faa28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 6 Mar 2025 16:40:32 +0000
Subject: [PATCH 03/20] feat: Add TelemetryFlagMetadata for flag metadata
attributes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/TelemetryFlagMetadata.cs | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
diff --git a/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs b/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
new file mode 100644
index 00000000..3bd0a258
--- /dev/null
+++ b/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
@@ -0,0 +1,25 @@
+namespace OpenFeature.Telemetry;
+
+/**
+ * Well-known flag metadata attributes for telemetry events.
+ * @see https://openfeature.dev/specification/appendix-d#flag-metadata
+ */
+public static class TelemetryFlagMetadata
+{
+ /**
+ * The context identifier returned in the flag metadata uniquely identifies
+ * the subject of the flag evaluation. If not available, the targeting key
+ * should be used.
+ */
+ public const string ContextId = "contextId";
+
+ /**
+ * A logical identifier for the flag set.
+ */
+ public const string FlagSetId = "flagSetId";
+
+ /**
+ * A version string (format unspecified) for the flag or flag set.
+ */
+ public const string Version = "version";
+}
From 75330fe69e5e28b0140386247ffc8425be97148f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 6 Mar 2025 17:08:43 +0000
Subject: [PATCH 04/20] feat: Rename TelemetryAttributes to TelemetryConstants
and add EvaluationEventFactory for event creation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventFactory.cs | 57 +++++++++++++++++++
...tryAttributes.cs => TelemetryConstants.cs} | 2 +-
2 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 src/OpenFeature/Telemetry/EvaluationEventFactory.cs
rename src/OpenFeature/Telemetry/{TelemetryAttributes.cs => TelemetryConstants.cs} (97%)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventFactory.cs b/src/OpenFeature/Telemetry/EvaluationEventFactory.cs
new file mode 100644
index 00000000..01965d12
--- /dev/null
+++ b/src/OpenFeature/Telemetry/EvaluationEventFactory.cs
@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+using OpenFeature.Constant;
+using OpenFeature.Model;
+
+namespace OpenFeature.Telemetry;
+
+public class EvaluationEvent
+{
+ public string Name { get; set; }
+ public Dictionary Attributes { get; set; }
+ public Dictionary Body { get; set; }
+}
+
+public static class EvaluationEventFactory
+{
+
+ private const string EventName = "feature_flag.evaluation";
+
+ public static EvaluationEvent CreateEvaluationEvent(HookContext hookContext, FlagEvaluationDetails details)
+ {
+ var attributes = new Dictionary
+ {
+ { TelemetryConstants.Key, hookContext.FlagKey },
+ { TelemetryConstants.Provider, hookContext.ProviderMetadata.Name }
+ };
+
+ attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : "unknown";
+
+ var body = new Dictionary();
+
+ if (!string.IsNullOrWhiteSpace(details.Variant))
+ {
+ attributes[TelemetryConstants.Variant] = details.Variant ?? details.Value.ToString();
+ }
+
+ attributes[TelemetryFlagMetadata.ContextId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.ContextId);
+ attributes[TelemetryFlagMetadata.FlagSetId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.FlagSetId);
+ attributes[TelemetryFlagMetadata.Version] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.Version);
+
+ if (details.ErrorType != ErrorType.None)
+ {
+ attributes[TelemetryConstants.ErrorCode] = details.ErrorType.ToString();
+
+ if (!string.IsNullOrWhiteSpace(details.ErrorMessage))
+ {
+ attributes[TelemetryConstants.ErrorMessage] = details.ErrorMessage ?? "N/A";
+ }
+ }
+
+ return new EvaluationEvent
+ {
+ Name = EventName,
+ Attributes = attributes,
+ Body = body
+ };
+ }
+}
diff --git a/src/OpenFeature/Telemetry/TelemetryAttributes.cs b/src/OpenFeature/Telemetry/TelemetryConstants.cs
similarity index 97%
rename from src/OpenFeature/Telemetry/TelemetryAttributes.cs
rename to src/OpenFeature/Telemetry/TelemetryConstants.cs
index fa60cf15..9cbd3db4 100644
--- a/src/OpenFeature/Telemetry/TelemetryAttributes.cs
+++ b/src/OpenFeature/Telemetry/TelemetryConstants.cs
@@ -4,7 +4,7 @@ namespace OpenFeature.Telemetry;
/// The attributes of an OpenTelemetry compliant event for flag evaluation.
///
///
-public static class TelemetryAttributes
+public static class TelemetryConstants
{
///
/// The lookup key of the feature flag.
From 783df4f652430bc369bf32a4bbde1547236252d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 6 Mar 2025 17:12:17 +0000
Subject: [PATCH 05/20] feat: Introduce EvaluationEvent class and refactor
EvaluationEventFactory for event creation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
src/OpenFeature/Telemetry/EvaluationEvent.cs | 37 +++++++++++++++++++
.../Telemetry/EvaluationEventFactory.cs | 26 ++++++-------
2 files changed, 48 insertions(+), 15 deletions(-)
create mode 100644 src/OpenFeature/Telemetry/EvaluationEvent.cs
diff --git a/src/OpenFeature/Telemetry/EvaluationEvent.cs b/src/OpenFeature/Telemetry/EvaluationEvent.cs
new file mode 100644
index 00000000..666f7b4c
--- /dev/null
+++ b/src/OpenFeature/Telemetry/EvaluationEvent.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+namespace OpenFeature.Telemetry;
+
+///
+/// Represents an evaluation event for feature flags.
+///
+public class EvaluationEvent
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the event.
+ /// The attributes of the event.
+ /// The body of the event.
+ public EvaluationEvent(string name, Dictionary attributes, Dictionary body)
+ {
+ this.Name = name;
+ this.Attributes = attributes;
+ this.Body = body;
+ }
+
+ ///
+ /// Gets or sets the name of the event.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Gets or sets the attributes of the event.
+ ///
+ public Dictionary Attributes { get; set; }
+
+ ///
+ /// Gets or sets the body of the event.
+ ///
+ public Dictionary Body { get; set; }
+}
diff --git a/src/OpenFeature/Telemetry/EvaluationEventFactory.cs b/src/OpenFeature/Telemetry/EvaluationEventFactory.cs
index 01965d12..e84928fd 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventFactory.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventFactory.cs
@@ -4,18 +4,19 @@
namespace OpenFeature.Telemetry;
-public class EvaluationEvent
-{
- public string Name { get; set; }
- public Dictionary Attributes { get; set; }
- public Dictionary Body { get; set; }
-}
-
+///
+/// Factory class for creating evaluation events for feature flags.
+///
public static class EvaluationEventFactory
{
-
private const string EventName = "feature_flag.evaluation";
+ ///
+ /// Creates an evaluation event based on the provided hook context and flag evaluation details.
+ ///
+ /// The context of the hook containing flag key and provider metadata.
+ /// The details of the flag evaluation including reason, variant, and metadata.
+ /// An instance of containing the event name, attributes, and body.
public static EvaluationEvent CreateEvaluationEvent(HookContext hookContext, FlagEvaluationDetails details)
{
var attributes = new Dictionary
@@ -24,7 +25,7 @@ public static EvaluationEvent CreateEvaluationEvent(HookContext hookConte
{ TelemetryConstants.Provider, hookContext.ProviderMetadata.Name }
};
- attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : "unknown";
+ attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : Reason.Unknown;
var body = new Dictionary();
@@ -47,11 +48,6 @@ public static EvaluationEvent CreateEvaluationEvent(HookContext hookConte
}
}
- return new EvaluationEvent
- {
- Name = EventName,
- Attributes = attributes,
- Body = body
- };
+ return new EvaluationEvent(EventName, attributes, body);
}
}
From f94d83aa120404d1e0c603b580609c5903f08c0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:38:53 +0000
Subject: [PATCH 06/20] feat: Add EvaluationEventBuilder for creating
evaluation events for feature flags
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
...{EvaluationEventFactory.cs => EvaluationEventBuilder.cs} | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
rename src/OpenFeature/Telemetry/{EvaluationEventFactory.cs => EvaluationEventBuilder.cs} (89%)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventFactory.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
similarity index 89%
rename from src/OpenFeature/Telemetry/EvaluationEventFactory.cs
rename to src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index e84928fd..e982587b 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventFactory.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -5,9 +5,9 @@
namespace OpenFeature.Telemetry;
///
-/// Factory class for creating evaluation events for feature flags.
+/// Class for creating evaluation events for feature flags.
///
-public static class EvaluationEventFactory
+public static class EvaluationEventBuilder
{
private const string EventName = "feature_flag.evaluation";
@@ -17,7 +17,7 @@ public static class EvaluationEventFactory
/// The context of the hook containing flag key and provider metadata.
/// The details of the flag evaluation including reason, variant, and metadata.
/// An instance of containing the event name, attributes, and body.
- public static EvaluationEvent CreateEvaluationEvent(HookContext hookContext, FlagEvaluationDetails details)
+ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluationDetails details)
{
var attributes = new Dictionary
{
From f6f3d0f53c9943ad5f3fc220cc5a46b5bb852ba0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:57:40 +0000
Subject: [PATCH 07/20] test: Add unit tests for EvaluationEventBuilder to
validate event creation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilderTests.cs | 88 +++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
new file mode 100644
index 00000000..1d65fc2d
--- /dev/null
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+using OpenFeature.Constant;
+using OpenFeature.Model;
+using OpenFeature.Telemetry;
+using Xunit;
+
+namespace OpenFeature.Tests.Telemetry
+{
+ public class EvaluationEventBuilderTests
+ {
+ [Fact]
+ public void Build_ShouldReturnEventWithCorrectAttributes()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value(), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var metadata = new Dictionary
+ {
+ { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
+ };
+ var flagMetadata = new ImmutableMetadata(metadata);
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: "reason", variant: "variant", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal("feature_flag.evaluation", evaluationEvent.Name);
+ Assert.Equal("flagKey", evaluationEvent.Attributes[TelemetryConstants.Key]);
+ Assert.Equal("provider", evaluationEvent.Attributes[TelemetryConstants.Provider]);
+ Assert.Equal("reason", evaluationEvent.Attributes[TelemetryConstants.Reason]);
+ Assert.Equal("variant", evaluationEvent.Attributes[TelemetryConstants.Variant]);
+ Assert.Equal("contextId", evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
+ Assert.Equal("flagSetId", evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
+ Assert.Equal("version", evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
+ }
+
+ [Fact]
+ public void Build_ShouldHandleErrorDetails()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value(), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var metadata = new Dictionary
+ {
+ { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
+ };
+ var flagMetadata = new ImmutableMetadata(metadata);
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.General,
+ errorMessage: "errorMessage", reason: "reason", variant: "variant", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal("General", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
+ Assert.Equal("errorMessage", evaluationEvent.Attributes[TelemetryConstants.ErrorMessage]);
+ }
+
+ [Fact]
+ public void Build_ShouldHandleMissingVariant()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var metadata = new Dictionary
+ {
+ { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
+ };
+ var flagMetadata = new ImmutableMetadata(metadata);
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: "reason", variant: "", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Throws(() => evaluationEvent.Attributes[TelemetryConstants.Variant]);
+ }
+ }
+}
From 53693481df6ff54b16e0878f6bda14d3e781bf6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Fri, 7 Mar 2025 14:30:32 +0000
Subject: [PATCH 08/20] fix: Simplify variant assignment in
EvaluationEventBuilder and enhance tests for missing flag metadata
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilder.cs | 6 +----
.../Telemetry/EvaluationEventBuilderTests.cs | 23 ++++++++++++++++++-
2 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index e982587b..6b67bc65 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -29,11 +29,7 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
var body = new Dictionary();
- if (!string.IsNullOrWhiteSpace(details.Variant))
- {
- attributes[TelemetryConstants.Variant] = details.Variant ?? details.Value.ToString();
- }
-
+ attributes[TelemetryConstants.Variant] = details.Variant;
attributes[TelemetryFlagMetadata.ContextId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.ContextId);
attributes[TelemetryFlagMetadata.FlagSetId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.FlagSetId);
attributes[TelemetryFlagMetadata.Version] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.Version);
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index 1d65fc2d..1965e415 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -75,6 +75,25 @@ public void Build_ShouldHandleMissingVariant()
{ "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
};
var flagMetadata = new ImmutableMetadata(metadata);
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: "reason", variant: null, flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Null(evaluationEvent.Attributes[TelemetryConstants.Variant]);
+ }
+
+ [Fact]
+ public void Build_ShouldHandleMissingFlagMetadata()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var flagMetadata = new ImmutableMetadata();
var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
reason: "reason", variant: "", flagMetadata: flagMetadata);
@@ -82,7 +101,9 @@ public void Build_ShouldHandleMissingVariant()
var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
// Assert
- Assert.Throws(() => evaluationEvent.Attributes[TelemetryConstants.Variant]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
}
}
}
From 2d81ada7a1b2e5c8058493e923e94ef63c36661a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Fri, 7 Mar 2025 14:32:18 +0000
Subject: [PATCH 09/20] fix: Ensure proper handling of missing reason in
EvaluationEventBuilder and add corresponding unit tests
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilder.cs | 2 +-
.../Telemetry/EvaluationEventBuilderTests.cs | 22 +++++++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index 6b67bc65..865045d3 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -25,10 +25,10 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
{ TelemetryConstants.Provider, hookContext.ProviderMetadata.Name }
};
- attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : Reason.Unknown;
var body = new Dictionary();
+ attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : Reason.Unknown;
attributes[TelemetryConstants.Variant] = details.Variant;
attributes[TelemetryFlagMetadata.ContextId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.ContextId);
attributes[TelemetryFlagMetadata.FlagSetId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.FlagSetId);
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index 1965e415..0d08cad1 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -105,5 +105,27 @@ public void Build_ShouldHandleMissingFlagMetadata()
Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
}
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public void Build_ShouldHandleMissingReason(string? reason)
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var flagMetadata = new ImmutableMetadata();
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: reason, variant: "", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal(Reason.Unknown, evaluationEvent.Attributes[TelemetryConstants.Reason]);
+ }
}
}
From 0d1a869424d98202f6f9ac5d76d0e7609e732967 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Tue, 11 Mar 2025 16:26:35 +0000
Subject: [PATCH 10/20] fix: Normalize error code to lowercase in
EvaluationEventBuilder
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
src/OpenFeature/Telemetry/EvaluationEventBuilder.cs | 2 +-
test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index 865045d3..4dc17aab 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -36,7 +36,7 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
if (details.ErrorType != ErrorType.None)
{
- attributes[TelemetryConstants.ErrorCode] = details.ErrorType.ToString();
+ attributes[TelemetryConstants.ErrorCode] = details.ErrorType.ToString()?.ToLowerInvariant();
if (!string.IsNullOrWhiteSpace(details.ErrorMessage))
{
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index 0d08cad1..2c56c360 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -58,7 +58,7 @@ public void Build_ShouldHandleErrorDetails()
var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
// Assert
- Assert.Equal("General", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
+ Assert.Equal("general", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
Assert.Equal("errorMessage", evaluationEvent.Attributes[TelemetryConstants.ErrorMessage]);
}
From 26d7dd5930c5d719bad250387240fbb44b9b8d71 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 17 Apr 2025 16:45:13 +0100
Subject: [PATCH 11/20] fix: Correct telemetry constant values for feature flag
attributes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
src/OpenFeature/Telemetry/TelemetryConstants.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/OpenFeature/Telemetry/TelemetryConstants.cs b/src/OpenFeature/Telemetry/TelemetryConstants.cs
index 9cbd3db4..3660c82d 100644
--- a/src/OpenFeature/Telemetry/TelemetryConstants.cs
+++ b/src/OpenFeature/Telemetry/TelemetryConstants.cs
@@ -19,7 +19,7 @@ public static class TelemetryConstants
///
/// A semantic identifier for an evaluated flag value.
///
- public const string Variant = "feature_flag.variant";
+ public const string Variant = "feature_flag.result.variant";
///
/// The unique identifier for the flag evaluation context. For example, the targeting key.
@@ -29,17 +29,17 @@ public static class TelemetryConstants
///
/// A message explaining the nature of an error occurring during flag evaluation.
///
- public const string ErrorMessage = "feature_flag.evaluation.error.message";
+ public const string ErrorMessage = "error.message";
///
/// The reason code which shows how a feature flag value was determined.
///
- public const string Reason = "feature_flag.evaluation.reason";
+ public const string Reason = "feature_flag.result.reason";
///
/// Describes a class of error the operation ended with.
///
- public const string Provider = "feature_flag.provider_name";
+ public const string Provider = "feature_flag.provider.name";
///
/// The identifier of the flag set to which the feature flag belongs.
From 6b484877a4807f7e6363f47e38f6caf69b53045f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Mon, 28 Apr 2025 16:34:53 +0100
Subject: [PATCH 12/20] refactor(tests): streamline EvaluationEventBuilderTests
for clarity and consistency
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilderTests.cs | 235 +++++++++---------
1 file changed, 117 insertions(+), 118 deletions(-)
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index 2c56c360..99805f2b 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -4,128 +4,127 @@
using OpenFeature.Telemetry;
using Xunit;
-namespace OpenFeature.Tests.Telemetry
+namespace OpenFeature.Tests.Telemetry;
+
+public class EvaluationEventBuilderTests
{
- public class EvaluationEventBuilderTests
+ [Fact]
+ public void Build_ShouldReturnEventWithCorrectAttributes()
{
- [Fact]
- public void Build_ShouldReturnEventWithCorrectAttributes()
- {
- // Arrange
- var clientMetadata = new ClientMetadata("client", "1.0.0");
- var providerMetadata = new Metadata("provider");
- var hookContext = new HookContext("flagKey", new Value(), FlagValueType.Object, clientMetadata,
- providerMetadata, EvaluationContext.Empty);
- var metadata = new Dictionary
- {
- { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
- };
- var flagMetadata = new ImmutableMetadata(metadata);
- var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
- reason: "reason", variant: "variant", flagMetadata: flagMetadata);
-
- // Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
-
- // Assert
- Assert.Equal("feature_flag.evaluation", evaluationEvent.Name);
- Assert.Equal("flagKey", evaluationEvent.Attributes[TelemetryConstants.Key]);
- Assert.Equal("provider", evaluationEvent.Attributes[TelemetryConstants.Provider]);
- Assert.Equal("reason", evaluationEvent.Attributes[TelemetryConstants.Reason]);
- Assert.Equal("variant", evaluationEvent.Attributes[TelemetryConstants.Variant]);
- Assert.Equal("contextId", evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
- Assert.Equal("flagSetId", evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
- Assert.Equal("version", evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
- }
-
- [Fact]
- public void Build_ShouldHandleErrorDetails()
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value(), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var metadata = new Dictionary
{
- // Arrange
- var clientMetadata = new ClientMetadata("client", "1.0.0");
- var providerMetadata = new Metadata("provider");
- var hookContext = new HookContext("flagKey", new Value(), FlagValueType.Object, clientMetadata,
- providerMetadata, EvaluationContext.Empty);
- var metadata = new Dictionary
- {
- { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
- };
- var flagMetadata = new ImmutableMetadata(metadata);
- var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.General,
- errorMessage: "errorMessage", reason: "reason", variant: "variant", flagMetadata: flagMetadata);
-
- // Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
-
- // Assert
- Assert.Equal("general", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
- Assert.Equal("errorMessage", evaluationEvent.Attributes[TelemetryConstants.ErrorMessage]);
- }
-
- [Fact]
- public void Build_ShouldHandleMissingVariant()
- {
- // Arrange
- var clientMetadata = new ClientMetadata("client", "1.0.0");
- var providerMetadata = new Metadata("provider");
- var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
- providerMetadata, EvaluationContext.Empty);
- var metadata = new Dictionary
- {
- { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
- };
- var flagMetadata = new ImmutableMetadata(metadata);
- var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
- reason: "reason", variant: null, flagMetadata: flagMetadata);
-
- // Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
-
- // Assert
- Assert.Null(evaluationEvent.Attributes[TelemetryConstants.Variant]);
- }
-
- [Fact]
- public void Build_ShouldHandleMissingFlagMetadata()
+ { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
+ };
+ var flagMetadata = new ImmutableMetadata(metadata);
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: "reason", variant: "variant", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal("feature_flag.evaluation", evaluationEvent.Name);
+ Assert.Equal("flagKey", evaluationEvent.Attributes[TelemetryConstants.Key]);
+ Assert.Equal("provider", evaluationEvent.Attributes[TelemetryConstants.Provider]);
+ Assert.Equal("reason", evaluationEvent.Attributes[TelemetryConstants.Reason]);
+ Assert.Equal("variant", evaluationEvent.Attributes[TelemetryConstants.Variant]);
+ Assert.Equal("contextId", evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
+ Assert.Equal("flagSetId", evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
+ Assert.Equal("version", evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
+ }
+
+ [Fact]
+ public void Build_ShouldHandleErrorDetails()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value(), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var metadata = new Dictionary
{
- // Arrange
- var clientMetadata = new ClientMetadata("client", "1.0.0");
- var providerMetadata = new Metadata("provider");
- var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
- providerMetadata, EvaluationContext.Empty);
- var flagMetadata = new ImmutableMetadata();
- var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
- reason: "reason", variant: "", flagMetadata: flagMetadata);
-
- // Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
-
- // Assert
- Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
- Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
- Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
- }
-
- [Theory]
- [InlineData(null)]
- [InlineData("")]
- [InlineData(" ")]
- public void Build_ShouldHandleMissingReason(string? reason)
+ { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
+ };
+ var flagMetadata = new ImmutableMetadata(metadata);
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.General,
+ errorMessage: "errorMessage", reason: "reason", variant: "variant", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal("general", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
+ Assert.Equal("errorMessage", evaluationEvent.Attributes[TelemetryConstants.ErrorMessage]);
+ }
+
+ [Fact]
+ public void Build_ShouldHandleMissingVariant()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var metadata = new Dictionary
{
- // Arrange
- var clientMetadata = new ClientMetadata("client", "1.0.0");
- var providerMetadata = new Metadata("provider");
- var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
- providerMetadata, EvaluationContext.Empty);
- var flagMetadata = new ImmutableMetadata();
- var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
- reason: reason, variant: "", flagMetadata: flagMetadata);
-
- // Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
-
- // Assert
- Assert.Equal(Reason.Unknown, evaluationEvent.Attributes[TelemetryConstants.Reason]);
- }
+ { "flagSetId", "flagSetId" }, { "contextId", "contextId" }, { "version", "version" }
+ };
+ var flagMetadata = new ImmutableMetadata(metadata);
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: "reason", variant: null, flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Null(evaluationEvent.Attributes[TelemetryConstants.Variant]);
+ }
+
+ [Fact]
+ public void Build_ShouldHandleMissingFlagMetadata()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var flagMetadata = new ImmutableMetadata();
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: "reason", variant: "", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public void Build_ShouldHandleMissingReason(string? reason)
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var flagMetadata = new ImmutableMetadata();
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.None,
+ reason: reason, variant: "", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal(Reason.Unknown, evaluationEvent.Attributes[TelemetryConstants.Reason]);
}
}
From 1b984c6ada975849d994628c587c2298cc1fb005 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Wed, 14 May 2025 16:16:11 +0100
Subject: [PATCH 13/20] Removed Body.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
src/OpenFeature/Telemetry/EvaluationEvent.cs | 9 +--------
src/OpenFeature/Telemetry/EvaluationEventBuilder.cs | 6 ++----
2 files changed, 3 insertions(+), 12 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEvent.cs b/src/OpenFeature/Telemetry/EvaluationEvent.cs
index 666f7b4c..c0cb1bbe 100644
--- a/src/OpenFeature/Telemetry/EvaluationEvent.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEvent.cs
@@ -12,12 +12,10 @@ public class EvaluationEvent
///
/// The name of the event.
/// The attributes of the event.
- /// The body of the event.
- public EvaluationEvent(string name, Dictionary attributes, Dictionary body)
+ public EvaluationEvent(string name, Dictionary attributes)
{
this.Name = name;
this.Attributes = attributes;
- this.Body = body;
}
///
@@ -29,9 +27,4 @@ public EvaluationEvent(string name, Dictionary attributes, Dict
/// Gets or sets the attributes of the event.
///
public Dictionary Attributes { get; set; }
-
- ///
- /// Gets or sets the body of the event.
- ///
- public Dictionary Body { get; set; }
}
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index 4dc17aab..72da1fa5 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -26,8 +26,6 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
};
- var body = new Dictionary();
-
attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : Reason.Unknown;
attributes[TelemetryConstants.Variant] = details.Variant;
attributes[TelemetryFlagMetadata.ContextId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.ContextId);
@@ -36,7 +34,7 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
if (details.ErrorType != ErrorType.None)
{
- attributes[TelemetryConstants.ErrorCode] = details.ErrorType.ToString()?.ToLowerInvariant();
+ attributes[TelemetryConstants.ErrorCode] = details.ErrorType.ToString().ToLowerInvariant();
if (!string.IsNullOrWhiteSpace(details.ErrorMessage))
{
@@ -44,6 +42,6 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
}
}
- return new EvaluationEvent(EventName, attributes, body);
+ return new EvaluationEvent(EventName, attributes);
}
}
From 5c9768786a0b57ce8d5dd144baae2e0f6bb14b6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Wed, 14 May 2025 16:33:33 +0100
Subject: [PATCH 14/20] docs: update XML documentation for
TelemetryFlagMetadata class
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/TelemetryEvaluationData.cs | 20 -----------
.../Telemetry/TelemetryFlagMetadata.cs | 35 +++++++++++--------
2 files changed, 20 insertions(+), 35 deletions(-)
delete mode 100644 src/OpenFeature/Telemetry/TelemetryEvaluationData.cs
diff --git a/src/OpenFeature/Telemetry/TelemetryEvaluationData.cs b/src/OpenFeature/Telemetry/TelemetryEvaluationData.cs
deleted file mode 100644
index f143dc02..00000000
--- a/src/OpenFeature/Telemetry/TelemetryEvaluationData.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace OpenFeature.Telemetry;
-
-/**
-* Event data, sometimes referred to as "body", is specific to a specific event.
-* In this case, the event is `feature_flag.evaluation`. That's why the prefix
-* is omitted from the values.
-* @see https://opentelemetry.io/docs/specs/semconv/feature-flags/feature-flags-logs/
-*/
-public static class TelemetryEvaluationData
-{
- /**
- * The evaluated value of the feature flag.
- *
- * - type: `undefined`
- * - requirement level: `conditionally required`
- * - condition: variant is not defined on the evaluation details
- * - example: `#ff0000`; `1`; `true`
- */
- public const string Value = "value";
-}
diff --git a/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs b/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
index 3bd0a258..eaa6d3f6 100644
--- a/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
+++ b/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
@@ -1,25 +1,30 @@
namespace OpenFeature.Telemetry;
-/**
- * Well-known flag metadata attributes for telemetry events.
- * @see https://openfeature.dev/specification/appendix-d#flag-metadata
- */
+///
+/// Well-known flag metadata attributes for telemetry events.
+/// See also: https://openfeature.dev/specification/appendix-d#flag-metadata
+///
public static class TelemetryFlagMetadata
{
- /**
- * The context identifier returned in the flag metadata uniquely identifies
- * the subject of the flag evaluation. If not available, the targeting key
- * should be used.
- */
+ ///
+ /// The context identifier returned in the flag metadata uniquely identifies
+ /// the subject of the flag evaluation. If not available, the targeting key
+ /// should be used.
+ ///
public const string ContextId = "contextId";
- /**
- * A logical identifier for the flag set.
- */
+ ///
+ /// ///A logical identifier for the flag set.
+ ///
public const string FlagSetId = "flagSetId";
- /**
- * A version string (format unspecified) for the flag or flag set.
- */
+ ///
+ /// A version string (format unspecified) for the flag or flag set.
+ ///
public const string Version = "version";
+
+ ///
+ /// The evaluated value of the feature flag.
+ ///
+ public const string Value = "value";
}
From ac2d968cfb0e5838df11b5ce9346f104f12a529e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 15 May 2025 16:35:16 +0100
Subject: [PATCH 15/20] feat: update telemetry attributes to include evaluated
value and refactor constants
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilder.cs | 7 ++++---
src/OpenFeature/Telemetry/TelemetryConstants.cs | 14 ++++++++++----
src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs | 5 -----
.../Telemetry/EvaluationEventBuilderTests.cs | 12 ++++++------
4 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index 72da1fa5..6572885a 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -28,9 +28,10 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : Reason.Unknown;
attributes[TelemetryConstants.Variant] = details.Variant;
- attributes[TelemetryFlagMetadata.ContextId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.ContextId);
- attributes[TelemetryFlagMetadata.FlagSetId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.FlagSetId);
- attributes[TelemetryFlagMetadata.Version] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.Version);
+ attributes[TelemetryConstants.Value] = details.Value;
+ attributes[TelemetryConstants.ContextId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.ContextId);
+ attributes[TelemetryConstants.FlagSetId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.FlagSetId);
+ attributes[TelemetryConstants.Version] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.Version);
if (details.ErrorType != ErrorType.None)
{
diff --git a/src/OpenFeature/Telemetry/TelemetryConstants.cs b/src/OpenFeature/Telemetry/TelemetryConstants.cs
index 3660c82d..62730666 100644
--- a/src/OpenFeature/Telemetry/TelemetryConstants.cs
+++ b/src/OpenFeature/Telemetry/TelemetryConstants.cs
@@ -16,20 +16,25 @@ public static class TelemetryConstants
///
public const string ErrorCode = "error.type";
+ ///
+ /// A message explaining the nature of an error occurring during flag evaluation.
+ ///
+ public const string ErrorMessage = "error.message";
+
///
/// A semantic identifier for an evaluated flag value.
///
public const string Variant = "feature_flag.result.variant";
///
- /// The unique identifier for the flag evaluation context. For example, the targeting key.
+ /// The evaluated value of the feature flag.
///
- public const string ContextId = "feature_flag.context.id";
+ public const string Value = "feature_flag.result.value";
///
- /// A message explaining the nature of an error occurring during flag evaluation.
+ /// The unique identifier for the flag evaluation context. For example, the targeting key.
///
- public const string ErrorMessage = "error.message";
+ public const string ContextId = "feature_flag.context.id";
///
/// The reason code which shows how a feature flag value was determined.
@@ -50,4 +55,5 @@ public static class TelemetryConstants
/// The version of the ruleset used during the evaluation. This may be any stable value which uniquely identifies the ruleset.
///
public const string Version = "feature_flag.version";
+
}
diff --git a/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs b/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
index eaa6d3f6..40b58f04 100644
--- a/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
+++ b/src/OpenFeature/Telemetry/TelemetryFlagMetadata.cs
@@ -22,9 +22,4 @@ public static class TelemetryFlagMetadata
/// A version string (format unspecified) for the flag or flag set.
///
public const string Version = "version";
-
- ///
- /// The evaluated value of the feature flag.
- ///
- public const string Value = "value";
}
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index 99805f2b..b5af1770 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -33,9 +33,9 @@ public void Build_ShouldReturnEventWithCorrectAttributes()
Assert.Equal("provider", evaluationEvent.Attributes[TelemetryConstants.Provider]);
Assert.Equal("reason", evaluationEvent.Attributes[TelemetryConstants.Reason]);
Assert.Equal("variant", evaluationEvent.Attributes[TelemetryConstants.Variant]);
- Assert.Equal("contextId", evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
- Assert.Equal("flagSetId", evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
- Assert.Equal("version", evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
+ Assert.Equal("contextId", evaluationEvent.Attributes[TelemetryConstants.ContextId]);
+ Assert.Equal("flagSetId", evaluationEvent.Attributes[TelemetryConstants.FlagSetId]);
+ Assert.Equal("version", evaluationEvent.Attributes[TelemetryConstants.Version]);
}
[Fact]
@@ -101,9 +101,9 @@ public void Build_ShouldHandleMissingFlagMetadata()
var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
// Assert
- Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.ContextId]);
- Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.FlagSetId]);
- Assert.Null(evaluationEvent.Attributes[TelemetryFlagMetadata.Version]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryConstants.ContextId]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryConstants.FlagSetId]);
+ Assert.Null(evaluationEvent.Attributes[TelemetryConstants.Version]);
}
[Theory]
From 789312e3117869406b38648a2d3d8d525bfe57fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 15 May 2025 16:47:13 +0100
Subject: [PATCH 16/20] feat: enhance EvaluationEventBuilder to handle empty
error messages and include value attribute
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilder.cs | 2 +-
.../Telemetry/EvaluationEventBuilderTests.cs | 42 +++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index 6572885a..e7253a29 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -39,7 +39,7 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
if (!string.IsNullOrWhiteSpace(details.ErrorMessage))
{
- attributes[TelemetryConstants.ErrorMessage] = details.ErrorMessage ?? "N/A";
+ attributes[TelemetryConstants.ErrorMessage] = details.ErrorMessage;
}
}
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index b5af1770..ecab3386 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -127,4 +127,46 @@ public void Build_ShouldHandleMissingReason(string? reason)
// Assert
Assert.Equal(Reason.Unknown, evaluationEvent.Attributes[TelemetryConstants.Reason]);
}
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public void Build_ShouldHandleErrorWithEmptyErrorMessage(string? errorMessage)
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value("value"), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var flagMetadata = new ImmutableMetadata();
+ var details = new FlagEvaluationDetails("flagKey", new Value("value"), ErrorType.General,
+ errorMessage: errorMessage, reason: "reason", variant: "", flagMetadata: flagMetadata);
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal("general", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
+ Assert.False(evaluationEvent.Attributes.ContainsKey(TelemetryConstants.ErrorMessage));
+ }
+
+ [Fact]
+ public void Build_ShouldIncludeValueAttributeInEvent()
+ {
+ // Arrange
+ var clientMetadata = new ClientMetadata("client", "1.0.0");
+ var providerMetadata = new Metadata("provider");
+ var hookContext = new HookContext("flagKey", new Value(), FlagValueType.Object, clientMetadata,
+ providerMetadata, EvaluationContext.Empty);
+ var testValue = new Value("test-value");
+ var details = new FlagEvaluationDetails("flagKey", testValue, ErrorType.None,
+ reason: "reason", variant: "variant", flagMetadata: new ImmutableMetadata());
+
+ // Act
+ var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+
+ // Assert
+ Assert.Equal(testValue, evaluationEvent.Attributes[TelemetryConstants.Value]);
+ }
}
From 62d8b5e61980590a2f7be37631d54002079fba11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 15 May 2025 16:50:46 +0100
Subject: [PATCH 17/20] feat: refactor EvaluationEventBuilder to improve
handling of flag metadata and streamline reason assignment
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilder.cs | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index e7253a29..39468aa5 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -25,13 +25,17 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
{ TelemetryConstants.Provider, hookContext.ProviderMetadata.Name }
};
-
- attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason) ? details.Reason?.ToLowerInvariant() : Reason.Unknown;
+ attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason)
+ ? details.Reason?.ToLowerInvariant()
+ : Reason.Unknown;
attributes[TelemetryConstants.Variant] = details.Variant;
attributes[TelemetryConstants.Value] = details.Value;
- attributes[TelemetryConstants.ContextId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.ContextId);
- attributes[TelemetryConstants.FlagSetId] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.FlagSetId);
- attributes[TelemetryConstants.Version] = details.FlagMetadata?.GetString(TelemetryFlagMetadata.Version);
+ if (details.FlagMetadata != null)
+ {
+ attributes[TelemetryConstants.ContextId] = details.FlagMetadata.GetString(TelemetryFlagMetadata.ContextId);
+ attributes[TelemetryConstants.FlagSetId] = details.FlagMetadata.GetString(TelemetryFlagMetadata.FlagSetId);
+ attributes[TelemetryConstants.Version] = details.FlagMetadata.GetString(TelemetryFlagMetadata.Version);
+ }
if (details.ErrorType != ErrorType.None)
{
From 0cf35e248f0691ed11d8c39eb45bff9773c6e92f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Thu, 15 May 2025 16:52:19 +0100
Subject: [PATCH 18/20] feat: normalize Reason attribute to lowercase in
EvaluationEventBuilder
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
src/OpenFeature/Telemetry/EvaluationEventBuilder.cs | 3 ++-
.../OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index 39468aa5..e79bbda4 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -27,9 +27,10 @@ public static EvaluationEvent Build(HookContext hookContext, FlagEvaluati
attributes[TelemetryConstants.Reason] = !string.IsNullOrWhiteSpace(details.Reason)
? details.Reason?.ToLowerInvariant()
- : Reason.Unknown;
+ : Reason.Unknown.ToLowerInvariant();
attributes[TelemetryConstants.Variant] = details.Variant;
attributes[TelemetryConstants.Value] = details.Value;
+
if (details.FlagMetadata != null)
{
attributes[TelemetryConstants.ContextId] = details.FlagMetadata.GetString(TelemetryFlagMetadata.ContextId);
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index ecab3386..88f79c1a 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -125,7 +125,7 @@ public void Build_ShouldHandleMissingReason(string? reason)
var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
// Assert
- Assert.Equal(Reason.Unknown, evaluationEvent.Attributes[TelemetryConstants.Reason]);
+ Assert.Equal(Reason.Unknown.ToLowerInvariant(), evaluationEvent.Attributes[TelemetryConstants.Reason]);
}
[Theory]
From f91851d1adc5313b5208a8246a4b6f5f5e7d37dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Mon, 19 May 2025 07:58:31 +0100
Subject: [PATCH 19/20] refactor: change attributes type to IDictionary and
make properties read-only in EvaluationEvent
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
src/OpenFeature/Telemetry/EvaluationEvent.cs | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEvent.cs b/src/OpenFeature/Telemetry/EvaluationEvent.cs
index c0cb1bbe..51506ad7 100644
--- a/src/OpenFeature/Telemetry/EvaluationEvent.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEvent.cs
@@ -12,19 +12,19 @@ public class EvaluationEvent
///
/// The name of the event.
/// The attributes of the event.
- public EvaluationEvent(string name, Dictionary attributes)
+ public EvaluationEvent(string name, IDictionary attributes)
{
- this.Name = name;
- this.Attributes = attributes;
+ Name = name;
+ Attributes = new Dictionary(attributes);
}
///
- /// Gets or sets the name of the event.
+ /// Gets the name of the event.
///
- public string Name { get; set; }
+ public string Name { get; }
///
- /// Gets or sets the attributes of the event.
+ /// Gets the attributes of the event.
///
- public Dictionary Attributes { get; set; }
+ public IReadOnlyDictionary Attributes { get; }
}
From b7d34fb01e5bd3149139960aea3a19a8ae0a3b6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Mon, 19 May 2025 08:03:25 +0100
Subject: [PATCH 20/20] refactor: change EvaluationEventBuilder to sealed and
update Build method calls in tests
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.../Telemetry/EvaluationEventBuilder.cs | 9 +++++++--
.../Telemetry/EvaluationEventBuilderTests.cs | 16 +++++++++-------
2 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
index e79bbda4..2f73224f 100644
--- a/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
+++ b/src/OpenFeature/Telemetry/EvaluationEventBuilder.cs
@@ -7,17 +7,22 @@ namespace OpenFeature.Telemetry;
///
/// Class for creating evaluation events for feature flags.
///
-public static class EvaluationEventBuilder
+public sealed class EvaluationEventBuilder
{
private const string EventName = "feature_flag.evaluation";
+ ///
+ /// Gets the default instance of the .
+ ///
+ public static EvaluationEventBuilder Default { get; } = new();
+
///
/// Creates an evaluation event based on the provided hook context and flag evaluation details.
///
/// The context of the hook containing flag key and provider metadata.
/// The details of the flag evaluation including reason, variant, and metadata.
/// An instance of containing the event name, attributes, and body.
- public static EvaluationEvent Build(HookContext hookContext, FlagEvaluationDetails details)
+ public EvaluationEvent Build(HookContext hookContext, FlagEvaluationDetails details)
{
var attributes = new Dictionary
{
diff --git a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
index 88f79c1a..3b02a8ee 100644
--- a/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
+++ b/test/OpenFeature.Tests/Telemetry/EvaluationEventBuilderTests.cs
@@ -8,6 +8,8 @@ namespace OpenFeature.Tests.Telemetry;
public class EvaluationEventBuilderTests
{
+ private readonly EvaluationEventBuilder _builder = EvaluationEventBuilder.Default;
+
[Fact]
public void Build_ShouldReturnEventWithCorrectAttributes()
{
@@ -25,7 +27,7 @@ public void Build_ShouldReturnEventWithCorrectAttributes()
reason: "reason", variant: "variant", flagMetadata: flagMetadata);
// Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+ var evaluationEvent = _builder.Build(hookContext, details);
// Assert
Assert.Equal("feature_flag.evaluation", evaluationEvent.Name);
@@ -55,7 +57,7 @@ public void Build_ShouldHandleErrorDetails()
errorMessage: "errorMessage", reason: "reason", variant: "variant", flagMetadata: flagMetadata);
// Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+ var evaluationEvent = _builder.Build(hookContext, details);
// Assert
Assert.Equal("general", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
@@ -79,7 +81,7 @@ public void Build_ShouldHandleMissingVariant()
reason: "reason", variant: null, flagMetadata: flagMetadata);
// Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+ var evaluationEvent = _builder.Build(hookContext, details);
// Assert
Assert.Null(evaluationEvent.Attributes[TelemetryConstants.Variant]);
@@ -98,7 +100,7 @@ public void Build_ShouldHandleMissingFlagMetadata()
reason: "reason", variant: "", flagMetadata: flagMetadata);
// Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+ var evaluationEvent = _builder.Build(hookContext, details);
// Assert
Assert.Null(evaluationEvent.Attributes[TelemetryConstants.ContextId]);
@@ -122,7 +124,7 @@ public void Build_ShouldHandleMissingReason(string? reason)
reason: reason, variant: "", flagMetadata: flagMetadata);
// Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+ var evaluationEvent = _builder.Build(hookContext, details);
// Assert
Assert.Equal(Reason.Unknown.ToLowerInvariant(), evaluationEvent.Attributes[TelemetryConstants.Reason]);
@@ -144,7 +146,7 @@ public void Build_ShouldHandleErrorWithEmptyErrorMessage(string? errorMessage)
errorMessage: errorMessage, reason: "reason", variant: "", flagMetadata: flagMetadata);
// Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+ var evaluationEvent = _builder.Build(hookContext, details);
// Assert
Assert.Equal("general", evaluationEvent.Attributes[TelemetryConstants.ErrorCode]);
@@ -164,7 +166,7 @@ public void Build_ShouldIncludeValueAttributeInEvent()
reason: "reason", variant: "variant", flagMetadata: new ImmutableMetadata());
// Act
- var evaluationEvent = EvaluationEventBuilder.Build(hookContext, details);
+ var evaluationEvent = _builder.Build(hookContext, details);
// Assert
Assert.Equal(testValue, evaluationEvent.Attributes[TelemetryConstants.Value]);