From 1b075710a54ac4ffd99ac2caf5321469ec547c49 Mon Sep 17 00:00:00 2001 From: Nicklas Lundin Date: Wed, 20 Nov 2024 16:50:01 +0100 Subject: [PATCH] fix: wip telemetry singleton --- api/sdk.api.md | 3 +- packages/sdk/src/Confidence.ts | 8 +-- packages/sdk/src/FlagResolverClient.ts | 32 ++++-------- packages/sdk/src/Telemetry.ts | 68 ++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 packages/sdk/src/Telemetry.ts diff --git a/api/sdk.api.md b/api/sdk.api.md index a520a5e2..653cdfa5 100644 --- a/api/sdk.api.md +++ b/api/sdk.api.md @@ -25,7 +25,7 @@ export class Confidence implements EventSender, Trackable, FlagResolver { // // @internal readonly contextChanges: Subscribe; - static create({ clientSecret, region, timeout, environment, fetchImplementation, logger, resolveBaseUrl, }: ConfidenceOptions): Confidence; + static create({ clientSecret, region, timeout, environment, fetchImplementation, logger, resolveBaseUrl, disableTelemetry, }: ConfidenceOptions): Confidence; get environment(): string; evaluateFlag(path: string, defaultValue: string): FlagEvaluation; // (undocumented) @@ -55,6 +55,7 @@ export class Confidence implements EventSender, Trackable, FlagResolver { // @public export interface ConfidenceOptions { clientSecret: string; + disableTelemetry?: boolean; environment: 'client' | 'backend'; // Warning: (ae-forgotten-export) The symbol "SimpleFetch" needs to be exported by the entry point index.d.ts fetchImplementation?: SimpleFetch; diff --git a/packages/sdk/src/Confidence.ts b/packages/sdk/src/Confidence.ts index 9da20f24..8e17982e 100644 --- a/packages/sdk/src/Confidence.ts +++ b/packages/sdk/src/Confidence.ts @@ -37,8 +37,8 @@ export interface ConfidenceOptions { logger?: Logger; /** Sets an alternative resolve url */ resolveBaseUrl?: string; - /** Enable telemetry */ - enableTelemetry?: boolean; + /** Disable telemetry */ + disableTelemetry?: boolean; } /** @@ -335,7 +335,7 @@ export class Confidence implements EventSender, Trackable, FlagResolver { fetchImplementation = defaultFetchImplementation(), logger = defaultLogger(), resolveBaseUrl, - enableTelemetry = true, + disableTelemetry = false, }: ConfidenceOptions): Confidence { const sdk = { id: SdkId.SDK_ID_JS_CONFIDENCE, @@ -349,7 +349,7 @@ export class Confidence implements EventSender, Trackable, FlagResolver { resolveTimeout: timeout, region, resolveBaseUrl, - enableTelemetry, + disableTelemetry, }); if (environment === 'client') { flagResolverClient = new CachingFlagResolverClient(flagResolverClient, Number.POSITIVE_INFINITY); diff --git a/packages/sdk/src/FlagResolverClient.ts b/packages/sdk/src/FlagResolverClient.ts index b14cf885..25329f7f 100644 --- a/packages/sdk/src/FlagResolverClient.ts +++ b/packages/sdk/src/FlagResolverClient.ts @@ -1,6 +1,7 @@ import { FlagEvaluation } from '.'; import { AccessiblePromise } from './AccessiblePromise'; import { Applier, FlagResolution } from './FlagResolution'; +import Telemetry from './Telemetry'; import { Value } from './Value'; import { Context } from './context'; import { FetchBuilder, TimeUnit } from './fetch-util'; @@ -11,11 +12,7 @@ import { AppliedFlag, } from './generated/confidence/flags/resolver/v1/api'; import { Sdk } from './generated/confidence/flags/resolver/v1/types'; -import { - LibraryTraces_Library, - LibraryTraces_TraceId, - Monitoring, -} from './generated/confidence/telemetry/v1/telemetry'; +import { Monitoring } from './generated/confidence/telemetry/v1/telemetry'; import { SimpleFetch } from './types'; const FLAG_PREFIX = 'flags/'; @@ -93,7 +90,7 @@ export type FlagResolverClientOptions = { environment: 'client' | 'backend'; region?: 'eu' | 'us'; resolveBaseUrl?: string; - enableTelemetry: boolean; + disableTelemetry: boolean; }; export class FetchingFlagResolverClient implements FlagResolverClient { @@ -114,9 +111,9 @@ export class FetchingFlagResolverClient implements FlagResolverClient { environment, region, resolveBaseUrl, - enableTelemetry, + disableTelemetry, }: FlagResolverClientOptions) { - if (!enableTelemetry) { + if (disableTelemetry) { // TODO think about both resolve and apply request logic for backends this.fetchImplementation = environment === 'backend' ? fetchImplementation : withRequestLogic(fetchImplementation); @@ -152,6 +149,7 @@ export class FetchingFlagResolverClient implements FlagResolverClient { this.resolveTimeout, new ResolveError('TIMEOUT', 'Resolve timeout'), ); + const start = new Date().getTime(); return this.resolveFlagsJson(request, signalWithTimeout) .then(response => FlagResolution.ready(context, response, this.createApplier(response.resolveToken))) .catch(error => { @@ -159,6 +157,9 @@ export class FetchingFlagResolverClient implements FlagResolverClient { return FlagResolution.failed(context, error.code, error.message); } throw error; + }) + .finally(() => { + Telemetry.getInstance().markFlagResolved(new Date().getTime() - start); }); }); } @@ -297,20 +298,7 @@ export function withTelemetryData(fetchImplementation: (request: Request) => Pro return new FetchBuilder() .modifyRequest(async request => { // TODO pull actual telemetry data - const monitoring: Monitoring = { - libraryTraces: [ - { - library: LibraryTraces_Library.LIBRARY_CONFIDENCE, - libraryVersion: '0.1.0', - traces: [ - { - id: LibraryTraces_TraceId.TRACE_ID_RESOLVE_LATENCY, - millisecondDuration: 100, - }, - ], - }, - ], - }; + const monitoring: Monitoring = Telemetry.getInstance().getSnapshot(); const headers = new Headers(request.headers); const base64Message = btoa(String.fromCharCode(...Monitoring.encode(monitoring).finish())); diff --git a/packages/sdk/src/Telemetry.ts b/packages/sdk/src/Telemetry.ts new file mode 100644 index 00000000..f2582851 --- /dev/null +++ b/packages/sdk/src/Telemetry.ts @@ -0,0 +1,68 @@ +import { + LibraryTraces_Library, + LibraryTraces_TraceId, + Monitoring, +} from './generated/confidence/telemetry/v1/telemetry'; + +class Telemetry { + private static instance: Telemetry; + private monitoring: Monitoring = { + libraryTraces: [], + }; + + static getInstance(): Telemetry { + if (!Telemetry.instance) { + Telemetry.instance = new Telemetry(); + } + return Telemetry.instance; + } + + markStaleFlag(): void { + console.log('Marking stale flag'); + this.pushTrace(LibraryTraces_TraceId.TRACE_ID_STALE_FLAG); + } + + pushTrace(traceId: LibraryTraces_TraceId, latency: number | undefined = undefined): void { + const { library, version } = this.getLibraryAndVersion(); + const existing = this.monitoring.libraryTraces.find(trace => { + trace.library === library && trace.libraryVersion === version; + }); + if (existing) { + existing.traces.push({ + id: traceId, + millisecondDuration: latency, + }); + } else { + this.monitoring.libraryTraces.push({ + library, + libraryVersion: version, + traces: [ + { + id: traceId, + millisecondDuration: latency, + }, + ], + }); + } + } + + getLibraryAndVersion(): { library: LibraryTraces_Library; version: string } { + return { + library: LibraryTraces_Library.LIBRARY_CONFIDENCE, + version: '0.0.0', + }; + } + + markFlagResolved(latency: number): void { + this.pushTrace(LibraryTraces_TraceId.TRACE_ID_RESOLVE_LATENCY, latency); + } + + getSnapshot(): Monitoring { + const snapshot = { ...this.monitoring }; + this.monitoring = { libraryTraces: [] }; // Reset the state + console.log('Snapshot:', snapshot); + return snapshot; + } +} + +export default Telemetry;