Skip to content

Commit 06226ca

Browse files
authored
feat: Add semantic conventions for feature flags. (#231)
## Summary Adds semantic convention for feature flags. ## How did you test this change? Unit testing.
1 parent ddd6d4b commit 06226ca

File tree

4 files changed

+562
-37
lines changed

4 files changed

+562
-37
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import 'dart:convert';
2+
3+
import 'package:launchdarkly_flutter_client_sdk/launchdarkly_flutter_client_sdk.dart';
4+
import 'package:launchdarkly_flutter_observability/src/otel/stringable_attribute.dart';
5+
6+
const _featureFlagEventName = 'feature_flag';
7+
const _featureFlagKeyAttr = '$_featureFlagEventName.key';
8+
9+
// Feature flag set.
10+
const _featureFlagSetAttr = '$_featureFlagEventName.set';
11+
const _featureFlagSetIdAttr = '$_featureFlagSetAttr.id';
12+
13+
// Feature flag provider.
14+
const _featureFlagProviderAttr = '$_featureFlagEventName.provider';
15+
const _featureFlagProviderNameAttr = '$_featureFlagProviderAttr.name';
16+
17+
// Feature flag context.
18+
const _featureFlagContextAttr = '$_featureFlagEventName.context';
19+
const _featureFlagContextIdAttr = '$_featureFlagContextAttr.id';
20+
21+
// Feature flag result.
22+
const _featureFlagResultAttr = '$_featureFlagEventName.result';
23+
const _featureFlagResultValueAttr = '$_featureFlagResultAttr.value';
24+
const _featureFlagResultVariationIndex =
25+
'$_featureFlagResultAttr.variationIndex';
26+
const _featureFlagResultReasonAttr = '$_featureFlagResultAttr.reason';
27+
const _featureFlagResultReasonInExperimentAttr =
28+
'$_featureFlagResultReasonAttr.inExperiment';
29+
30+
final providerNameAttribute = StringableAttribute.fromString(
31+
_featureFlagProviderNameAttr,
32+
'LaunchDarkly',
33+
);
34+
35+
String? _toJsonValue(LDValue value) {
36+
try {
37+
return jsonEncode(value.toDynamic());
38+
} catch (_) {
39+
// All LDValues must be JSON presentable, but this is for safety.
40+
return null;
41+
}
42+
}
43+
44+
List<StringableAttribute> _getValueAttributes(LDEvaluationDetail<LDValue> detail) {
45+
final attributes = <StringableAttribute>[];
46+
final jsonValue = _toJsonValue(detail.value);
47+
if (jsonValue != null) {
48+
attributes.add(
49+
StringableAttribute.fromString(_featureFlagResultValueAttr, jsonValue),
50+
);
51+
}
52+
if (detail.variationIndex != null) {
53+
attributes.add(
54+
StringableAttribute.fromInt(
55+
_featureFlagResultVariationIndex,
56+
detail.variationIndex!,
57+
),
58+
);
59+
}
60+
if (detail.reason != null && detail.reason!.inExperiment) {
61+
attributes.add(
62+
StringableAttribute.fromBoolean(
63+
_featureFlagResultReasonInExperimentAttr,
64+
detail.reason!.inExperiment,
65+
),
66+
);
67+
}
68+
return attributes;
69+
}
70+
71+
/// Class representing the feature flagging semantic convention with
72+
/// LaunchDarkly additions.
73+
class FeatureFlagConvention {
74+
/// Get feature_flag event attributes.
75+
static List<StringableAttribute> getEventAttributes({
76+
required String key,
77+
required LDEvaluationDetail<LDValue> detail,
78+
LDContext? context,
79+
String? environmentId,
80+
}) {
81+
List<StringableAttribute> attributes = [
82+
providerNameAttribute,
83+
StringableAttribute.fromString(_featureFlagKeyAttr, key),
84+
..._getValueAttributes(detail),
85+
];
86+
87+
if (context != null && context.valid) {
88+
attributes.add(
89+
StringableAttribute.fromString(_featureFlagContextIdAttr, context.canonicalKey),
90+
);
91+
}
92+
93+
if (environmentId != null) {
94+
attributes.add(
95+
StringableAttribute.fromString(_featureFlagSetIdAttr, environmentId!),
96+
);
97+
}
98+
return attributes;
99+
}
100+
101+
/// The name to use for the event.
102+
static const eventName = _featureFlagEventName;
103+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import 'package:opentelemetry/api.dart';
2+
3+
/// Extended attribute class to add toString methods to attributes for
4+
/// error handling, testing, and debugging purposes.
5+
///
6+
/// This is an internal type not to be exposed to end users.
7+
/// I an attribute type were to be exposed, then it shouldn't indirectly expose
8+
/// the implementation Attribute type.
9+
final class StringableAttribute extends Attribute {
10+
/// Create an Attribute from a String value.
11+
StringableAttribute.fromString(super.key, super.value) : super.fromString();
12+
13+
/// Create an Attribute from a boolean value.
14+
// ignore: avoid_positional_boolean_parameters
15+
StringableAttribute.fromBoolean(super.key, super.value) : super.fromBoolean();
16+
17+
/// Create an Attribute from a double-precision floating-point value.
18+
StringableAttribute.fromDouble(super.key, super.value) : super.fromDouble();
19+
20+
/// Create an Attribute from an integer value.
21+
StringableAttribute.fromInt(super.key, super.value) : super.fromInt();
22+
23+
/// Create an Attribute from a list of String values.
24+
StringableAttribute.fromStringList(super.key, super.value) : super.fromStringList();
25+
26+
/// Create an Attribute from a list of boolean values.
27+
StringableAttribute.fromBooleanList(super.key, super.value) : super.fromBooleanList();
28+
29+
/// Create an Attribute from a list of double-precision floating-point values.
30+
StringableAttribute.fromDoubleList(super.key, super.value) : super.fromDoubleList();
31+
32+
/// Create an Attribute from a list of integer values.
33+
StringableAttribute.fromIntList(super.key, super.value) : super.fromIntList();
34+
35+
@override
36+
String toString() {
37+
return 'Attribute{key: $key, value: $value}';
38+
}
39+
}

sdk/@launchdarkly/launchdarkly_flutter_observability/pubspec.yaml

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,11 @@ dependencies:
1212
flutter:
1313
sdk: flutter
1414

15+
opentelemetry: 0.18.10
16+
17+
launchdarkly_flutter_client_sdk: ^4.12.0
18+
1519
dev_dependencies:
1620
flutter_test:
1721
sdk: flutter
1822
flutter_lints: ^5.0.0
19-
20-
# For information on the generic Dart part of this file, see the
21-
# following page: https://dart.dev/tools/pub/pubspec
22-
23-
# The following section is specific to Flutter packages.
24-
flutter:
25-
26-
# To add assets to your package, add an assets section, like this:
27-
# assets:
28-
# - images/a_dot_burr.jpeg
29-
# - images/a_dot_ham.jpeg
30-
#
31-
# For details regarding assets in packages, see
32-
# https://flutter.dev/to/asset-from-package
33-
#
34-
# An image asset can refer to one or more resolution-specific "variants", see
35-
# https://flutter.dev/to/resolution-aware-images
36-
37-
# To add custom fonts to your package, add a fonts section here,
38-
# in this "flutter" section. Each entry in this list should have a
39-
# "family" key with the font family name, and a "fonts" key with a
40-
# list giving the asset and other descriptors for the font. For
41-
# example:
42-
# fonts:
43-
# - family: Schyler
44-
# fonts:
45-
# - asset: fonts/Schyler-Regular.ttf
46-
# - asset: fonts/Schyler-Italic.ttf
47-
# style: italic
48-
# - family: Trajan Pro
49-
# fonts:
50-
# - asset: fonts/TrajanPro.ttf
51-
# - asset: fonts/TrajanPro_Bold.ttf
52-
# weight: 700
53-
#
54-
# For details regarding fonts in packages, see
55-
# https://flutter.dev/to/font-from-package

0 commit comments

Comments
 (0)