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, {