diff --git a/e2e/nextjs/src/app/components/highlight-buttons.tsx b/e2e/nextjs/src/app/components/highlight-buttons.tsx index 03fc42616..33d7eba7b 100644 --- a/e2e/nextjs/src/app/components/highlight-buttons.tsx +++ b/e2e/nextjs/src/app/components/highlight-buttons.tsx @@ -51,6 +51,27 @@ export function HighlightButtons() { > Track + + + ) } diff --git a/e2e/react-router/src/ldclientLazy.tsx b/e2e/react-router/src/ldclientLazy.tsx index 16c349358..00e687a63 100644 --- a/e2e/react-router/src/ldclientLazy.tsx +++ b/e2e/react-router/src/ldclientLazy.tsx @@ -16,10 +16,18 @@ const observabilitySettings: ConstructorParameters[0] = { } const sessionReplaySettings: ConstructorParameters[0] = { debug: { clientInteractions: true, domRecording: true }, + environment: 'production', + inlineImages: true, + inlineStylesheet: true, privacySetting: 'none', serviceName: 'ryan-test', backendUrl: 'http://localhost:8082/public', manualStart: true, + enableCanvasRecording: true, + samplingStrategy: { + canvas: 2, // 2 fps + canvasMaxSnapshotDimension: 720, // 720p quality + }, } export const client = init( diff --git a/sdk/highlight-run/src/client/index.tsx b/sdk/highlight-run/src/client/index.tsx index 0574ae3dc..fe6ccd9df 100644 --- a/sdk/highlight-run/src/client/index.tsx +++ b/sdk/highlight-run/src/client/index.tsx @@ -71,7 +71,7 @@ import { NetworkPerformancePayload, } from './listeners/network-listener/performance-listener' import { Logger } from './logger' -import { BROWSER_METER_NAME, getTracer, setupBrowserTracing } from './otel' +import { getMeter, getTracer, setupBrowserTracing, shutdown } from './otel' import { HighlightIframeMessage, HighlightIframeReponse, @@ -115,12 +115,10 @@ import { Counter, Gauge, Histogram, - metrics, UpDownCounter, } from '@opentelemetry/api' import { IntegrationClient } from '../integrations' -import { LaunchDarklyIntegration } from '../integrations/launchdarkly' -import { LDClient } from '../integrations/launchdarkly' +import { LaunchDarklyIntegration, LDClient } from '../integrations/launchdarkly' import { createLog, defaultLogOptions } from './listeners/console-listener' import { CustomSampler } from './otel/sampling/CustomSampler' import randomUuidV4 from './utils/randomUuidV4' @@ -635,6 +633,11 @@ export class Highlight { }, sampler, ) + // reset metrics to connect them to the new meter + this._gauges.clear() + this._counters.clear() + this._histograms.clear() + this._up_down_counters.clear() this.logger.log( `Initializing...`, @@ -1280,10 +1283,10 @@ SessionSecureID: ${this.sessionData.sessionSecureID}`, } recordGauge(metric: RecordMetric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let gauge = this._gauges.get(metric.name) if (!gauge) { - gauge = meter.createGauge(metric.name) + gauge = getMeter()?.createGauge(metric.name) + if (!gauge) return this._gauges.set(metric.name, gauge) } gauge.record(metric.value, { @@ -1298,10 +1301,10 @@ SessionSecureID: ${this.sessionData.sessionSecureID}`, } recordCount(metric: RecordMetric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let counter = this._counters.get(metric.name) if (!counter) { - counter = meter.createCounter(metric.name) + counter = getMeter()?.createCounter(metric.name) + if (!counter) return this._counters.set(metric.name, counter) } counter.add(metric.value, { @@ -1317,10 +1320,10 @@ SessionSecureID: ${this.sessionData.sessionSecureID}`, } recordHistogram(metric: RecordMetric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let histogram = this._histograms.get(metric.name) if (!histogram) { - histogram = meter.createHistogram(metric.name) + histogram = getMeter()?.createHistogram(metric.name) + if (!histogram) return this._histograms.set(metric.name, histogram) } histogram.record(metric.value, { @@ -1332,10 +1335,10 @@ SessionSecureID: ${this.sessionData.sessionSecureID}`, } recordUpDownCounter(metric: RecordMetric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let up_down_counter = this._up_down_counters.get(metric.name) if (!up_down_counter) { - up_down_counter = meter.createUpDownCounter(metric.name) + up_down_counter = getMeter()?.createUpDownCounter(metric.name) + if (!up_down_counter) return this._up_down_counters.set(metric.name, up_down_counter) } up_down_counter.add(metric.value, { @@ -1367,6 +1370,7 @@ SessionSecureID: ${this.sessionData.sessionSecureID}`, // stop all other event listeners, to be restarted on initialize() this.listeners.forEach((stop) => stop()) this.listeners = [] + void shutdown() } getCurrentSessionTimestamp() { diff --git a/sdk/highlight-run/src/client/otel/index.ts b/sdk/highlight-run/src/client/otel/index.ts index de1197788..27f090681 100644 --- a/sdk/highlight-run/src/client/otel/index.ts +++ b/sdk/highlight-run/src/client/otel/index.ts @@ -58,6 +58,7 @@ import version from '../../version' import { ExportSampler } from './sampling/ExportSampler' import { getPersistentSessionSecureID } from '../utils/sessionStorage/highlightSession' import type { EventName } from '@opentelemetry/instrumentation-user-interaction' + export type Callback = (span?: Span) => any export type BrowserTracingConfig = { @@ -94,7 +95,7 @@ export const setupBrowserTracing = ( config: BrowserTracingConfig, sampler: ExportSampler, ) => { - if (providers.tracerProvider !== undefined) { + if (providers.tracerProvider || providers.meterProvider) { console.warn('OTEL already initialized. Skipping...') return } @@ -417,18 +418,28 @@ export const getActiveSpanContext = () => { } export const shutdown = async () => { - if (providers.tracerProvider) { - await providers.tracerProvider.forceFlush() - await providers.tracerProvider.shutdown() - } else { - console.warn('OTEL shutdown called without initialized tracerProvider.') - } - if (providers.meterProvider) { - await providers.meterProvider.forceFlush() - await providers.meterProvider.shutdown() - } else { - console.warn('OTEL shutdown called without initialized meterProvider.') - } + await Promise.allSettled([ + (async () => { + if (providers.tracerProvider) { + await providers.tracerProvider.shutdown() + providers.tracerProvider = undefined + } else { + console.warn( + 'OTEL shutdown called without initialized tracerProvider.', + ) + } + })(), + (async () => { + if (providers.meterProvider) { + await providers.meterProvider.shutdown() + providers.meterProvider = undefined + } else { + console.warn( + 'OTEL shutdown called without initialized meterProvider.', + ) + } + })(), + ]) } const enhanceSpanWithHttpRequestAttributes = ( diff --git a/sdk/highlight-run/src/sdk/observe.ts b/sdk/highlight-run/src/sdk/observe.ts index 11ba9d29e..01a9c3bd0 100644 --- a/sdk/highlight-run/src/sdk/observe.ts +++ b/sdk/highlight-run/src/sdk/observe.ts @@ -4,21 +4,21 @@ import { Counter, Gauge, Histogram, - metrics, Span, SpanOptions, SpanStatusCode, UpDownCounter, } from '@opentelemetry/api' import { - BROWSER_METER_NAME, - Callback, - getTracer, + ATTR_EXCEPTION_ID, ATTR_LOG_MESSAGE, ATTR_LOG_SEVERITY, + Callback, + getMeter, + getTracer, LOG_SPAN_NAME, setupBrowserTracing, - ATTR_EXCEPTION_ID, + shutdown, } from '../client/otel' import type { Observe } from '../api/observe' import { getNoopSpan } from '../client/otel/utils' @@ -77,8 +77,6 @@ import { import randomUuidV4 from '../client/utils/randomUuidV4' import { recordException } from '../client/otel/recordException' import { ObserveOptions } from '../client/types/observe' -import { WebTracerProvider } from '@opentelemetry/sdk-trace-web' -import { MeterProvider } from '@opentelemetry/sdk-metrics' import { isMetricSafeNumber } from '../client/utils/utils' export class ObserveSDK implements Observe { @@ -104,12 +102,6 @@ export class ObserveSDK implements Observe { >() private readonly sampler: ExportSampler = new CustomSampler() private _started = false - private providers!: - | { - tracerProvider?: WebTracerProvider - meterProvider?: MeterProvider - } - | undefined private graphqlSDK!: Sdk constructor( options: ObserveOptions & { @@ -126,7 +118,7 @@ export class ObserveSDK implements Observe { return } this._started = true - this.providers = setupBrowserTracing( + setupBrowserTracing( { ...{ backendUrl: @@ -151,6 +143,11 @@ export class ObserveSDK implements Observe { }, this.sampler, ) + // reset metrics to connect them to the new meter + this._gauges.clear() + this._counters.clear() + this._histograms.clear() + this._up_down_counters.clear() const client = new GraphQLClient( this._options.backendUrl ?? 'https://pub.observability.app.launchdarkly.com', @@ -168,10 +165,7 @@ export class ObserveSDK implements Observe { return } this._started = false - await Promise.all([ - this.providers?.tracerProvider?.shutdown(), - this.providers?.meterProvider?.shutdown(), - ]) + await shutdown() } private async configureSampling() { @@ -282,10 +276,10 @@ export class ObserveSDK implements Observe { } recordCount(metric: Metric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let counter = this._counters.get(metric.name) if (!counter) { - counter = meter.createCounter(metric.name) + counter = getMeter()?.createCounter(metric.name) + if (!counter) return this._counters.set(metric.name, counter) } counter.add(metric.value, { @@ -295,10 +289,10 @@ export class ObserveSDK implements Observe { } recordGauge(metric: Metric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let gauge = this._gauges.get(metric.name) if (!gauge) { - gauge = meter.createGauge(metric.name) + gauge = getMeter()?.createGauge(metric.name) + if (!gauge) return this._gauges.set(metric.name, gauge) } gauge.record(metric.value, { @@ -330,10 +324,10 @@ export class ObserveSDK implements Observe { } recordHistogram(metric: Metric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let histogram = this._histograms.get(metric.name) if (!histogram) { - histogram = meter.createHistogram(metric.name) + histogram = getMeter()?.createHistogram(metric.name) + if (!histogram) return this._histograms.set(metric.name, histogram) } histogram.record(metric.value, { @@ -343,10 +337,10 @@ export class ObserveSDK implements Observe { } recordUpDownCounter(metric: Metric) { - const meter = metrics.getMeter(BROWSER_METER_NAME) let up_down_counter = this._up_down_counters.get(metric.name) if (!up_down_counter) { - up_down_counter = meter.createUpDownCounter(metric.name) + up_down_counter = getMeter()?.createUpDownCounter(metric.name) + if (!up_down_counter) return this._up_down_counters.set(metric.name, up_down_counter) } up_down_counter.add(metric.value, {