|
| 1 | +package dev.openfeature.sdk |
| 2 | + |
| 3 | +import dev.openfeature.sdk.exceptions.ErrorCode |
| 4 | + |
| 5 | +const val TELEMETRY_KEY = "feature_flag.key" |
| 6 | +const val TELEMETRY_ERROR_CODE = "error.type" |
| 7 | +const val TELEMETRY_VARIANT = "feature_flag.variant" |
| 8 | +const val TELEMETRY_CONTEXT_ID = "feature_flag.context.id" |
| 9 | +const val TELEMETRY_ERROR_MSG = "feature_flag.evaluation.error.message" |
| 10 | +const val TELEMETRY_REASON = "feature_flag.evaluation.reason" |
| 11 | +const val TELEMETRY_PROVIDER = "feature_flag.provider_name" |
| 12 | +const val TELEMETRY_FLAG_SET_ID = "feature_flag.set.id" |
| 13 | +const val TELEMETRY_VERSION = "feature_flag.version" |
| 14 | + |
| 15 | +// Well-known flag metadata attributes for telemetry events. |
| 16 | +// Specification: https://openfeature.dev/specification/appendix-d#flag-metadata |
| 17 | +const val TELEMETRY_FLAG_META_CONTEXT_ID = "contextId" |
| 18 | +const val TELEMETRY_FLAG_META_FLAG_SET_ID = "flagSetId" |
| 19 | +const val TELEMETRY_FLAG_META_VERSION = "version" |
| 20 | + |
| 21 | +// OpenTelemetry event body. |
| 22 | +// Specification: https://opentelemetry.io/docs/specs/semconv/feature-flags/feature-flags-logs/ |
| 23 | +const val TELEMETRY_BODY = "value" |
| 24 | + |
| 25 | +const val FLAG_EVALUATION_EVENT_NAME = "feature_flag.evaluation" |
| 26 | + |
| 27 | +/** |
| 28 | + * Creates an [EvaluationEvent] from a flag evaluation. To be used inside a finally hook to provide an OpenTelemetry |
| 29 | + * compatible telemetry signal. |
| 30 | + */ |
| 31 | +fun <T> createEvaluationEvent( |
| 32 | + hookContext: HookContext<T>, |
| 33 | + providerEvaluation: ProviderEvaluation<T> |
| 34 | +): EvaluationEvent { |
| 35 | + val attributes = mutableMapOf<String, Any?>() |
| 36 | + val body = mutableMapOf<String, Any?>() |
| 37 | + attributes[TELEMETRY_KEY] = hookContext.flagKey |
| 38 | + attributes[TELEMETRY_PROVIDER] = hookContext.providerMetadata.name ?: "" |
| 39 | + attributes[TELEMETRY_REASON] = providerEvaluation.reason?.lowercase() ?: Reason.UNKNOWN.name.lowercase() |
| 40 | + attributes[TELEMETRY_CONTEXT_ID] = |
| 41 | + providerEvaluation.metadata.getString(TELEMETRY_FLAG_META_CONTEXT_ID) ?: hookContext.ctx?.getTargetingKey() |
| 42 | + providerEvaluation.metadata.getString(TELEMETRY_FLAG_META_FLAG_SET_ID)?.let { attributes[TELEMETRY_FLAG_SET_ID] = it } |
| 43 | + providerEvaluation.metadata.getString(TELEMETRY_FLAG_META_VERSION)?.let { attributes[TELEMETRY_VERSION] = it } |
| 44 | + |
| 45 | + val variant = providerEvaluation.variant |
| 46 | + if (variant == null) { |
| 47 | + body[TELEMETRY_BODY] = providerEvaluation.value |
| 48 | + } else { |
| 49 | + attributes[TELEMETRY_VARIANT] = variant |
| 50 | + } |
| 51 | + |
| 52 | + if (providerEvaluation.reason == Reason.ERROR.name) { |
| 53 | + attributes[TELEMETRY_ERROR_CODE] = providerEvaluation.errorCode ?: ErrorCode.GENERAL |
| 54 | + providerEvaluation.errorMessage?.let { attributes[TELEMETRY_ERROR_MSG] = it } |
| 55 | + } |
| 56 | + |
| 57 | + return EvaluationEvent( |
| 58 | + FLAG_EVALUATION_EVENT_NAME, |
| 59 | + attributes, |
| 60 | + body |
| 61 | + ) |
| 62 | +} |
0 commit comments