UNPKG

highlight.run

Version:

Open source, fullstack monitoring. Capture frontend errors, record server side logs, and visualize what broke with session replay.

767 lines (733 loc) 30.9 kB
import { Attributes } from '@opentelemetry/api'; import { Context } from '@opentelemetry/api'; import { EvaluationSeriesContext } from '@launchdarkly/js-client-sdk'; import { EvaluationSeriesData } from '@launchdarkly/js-client-sdk'; import { Hook as Hook_2 } from '@launchdarkly/js-client-sdk'; import { LDContext } from '@launchdarkly/js-client-sdk'; import { LDEvaluationReason } from '@launchdarkly/js-client-sdk'; import { LDFlagValue } from '@launchdarkly/js-client-sdk'; import { LDPluginEnvironmentMetadata } from '@launchdarkly/js-client-sdk'; import { LDPluginMetadata } from '@launchdarkly/js-client-sdk'; import { Span } from '@opentelemetry/api'; import { SpanOptions } from '@opentelemetry/api'; declare const ALL_CONSOLE_METHODS: readonly ["assert", "count", "countReset", "debug", "dir", "dirxml", "error", "group", "groupCollapsed", "groupEnd", "info", "log", "table", "time", "timeEnd", "timeLog", "trace", "warn"]; declare interface AmplitudeIntegrationOptions extends DefaultIntegrationOptions { apiKey?: string; } declare type CommonOptions = { /** * Do not use this. * @private */ debug?: boolean | DebugOptions; /** * Specifies the name of the app. */ serviceName?: string; /** * Specifies the version of your application. * This is commonly a Git hash or a semantic version. */ version?: string; /** * Specifies where to send Highlight session data. * You should not have to set this unless you are running an on-premise instance. */ backendUrl?: string; /** * Set to `sessionStorage` to bypass all `window.localStorage` usage. * This can help with compliance for cookie-consent regulation. * Using `sessionStorage` will cause app close+reopens to start a new highlight session, * as the session ID will not persist. */ storageMode?: 'sessionStorage' | 'localStorage'; /** * By default, session data is stored in the `sessionStorage` of the browser. * Set to `true` to store session data in a cookie instead. * This can help with compliance for cookie-consent regulation. */ sessionCookie?: boolean; /** * Specifies if Highlight should not automatically initialize when the class is created. * This should be used with `.start()` and `.stop()` if you want to control when Highlight records. * @default false */ manualStart?: boolean; /** * Key to use to determine the sessionSecureID in order to override session generation logic. * If a sessionKey is provided, the session will be combined with the previous session with the same key. * Otherwise, the current session logic will be used to generate a sessionSecureID. */ sessionKey?: string; /** * A function that returns a friendly name for a given context. * This name will be used to identify the session in the observability UI. * ```ts * contextFriendlyName: (context: LDContext) => { * if(context.kind === 'multi' && context.user?.email) { * return context.user.email; * } else if(context.kind === 'user') { * return context.key; * } * // Use the default identifier for contexts which don't contain a user. * return undefined; * } * ``` * @param context The context to get a friendly name for. * @returns The friendly name for the context, or undefined to use the * default identifier. */ contextFriendlyName?: (context: LDContext) => string | undefined; }; declare type ConsoleMethods = ConsoleMethodsTuple[number]; declare type ConsoleMethodsTuple = typeof ALL_CONSOLE_METHODS; declare type DebugOptions = { clientInteractions?: boolean; domRecording?: boolean; }; declare type DefaultIntegrationOptions = { disabled?: boolean; }; declare type ErrorMessageType = 'console.error' | 'window.onerror' | 'window.onunhandledrejection' | 'custom' | 'React.ErrorBoundary'; declare interface Headers_2 { [key: string]: any; } /** * Interface for extending SDK functionality via hooks. */ declare type Hook = Omit<Hook_2, 'afterEvaluation'> & { /** * This method is called during the execution of the variation method * after the flag value has been determined. The method is executed synchronously. * * @param hookContext Contains read-only information about the evaluation * being performed. * @param data A record associated with each stage of hook invocations. Each * stage is called with the data of the previous stage for a series. * @param detail The result of the evaluation. This value should not be * modified. * @returns Data to use when executing the next state of the hook in the evaluation series. It is * recommended to expand the previous input into the return. This helps ensure your stage remains * compatible moving forward as more stages are added. * ```js * return {...data, "my-new-field": /*my data/*} * ``` */ afterEvaluation?(hookContext: EvaluationSeriesContext, data: EvaluationSeriesData, detail: { /** * The result of the flag evaluation. This will be either one of the flag's variations or * the default value that was passed to `LDClient.variationDetail`. */ value: LDFlagValue; /** * The index of the returned value within the flag's list of variations, e.g. 0 for the * first variation-- or `null` if the default value was returned. */ variationIndex?: number | null; /** * An object describing the main factor that influenced the flag evaluation value. */ reason?: LDEvaluationReason | null; }): EvaluationSeriesData; }; declare type IntegrationOptions = { amplitude?: AmplitudeIntegrationOptions; intercom?: IntercomIntegrationOptions; mixpanel?: MixpanelIntegrationOptions; }; declare interface IntercomIntegrationOptions extends DefaultIntegrationOptions { } declare interface LDClientMin { track(key: string, data?: any, metricValue?: number): void; identify(ctx: any): void; addHook(hook: Hook): void; } declare interface LDPlugin { getMetadata(): LDPluginMetadata; register(client: LDClientMin, environmentMetadata: LDPluginEnvironmentMetadata): void; getHooks?(metadata: LDPluginEnvironmentMetadata): Hook[]; } declare type MaskInputOptions = Partial<{ color: boolean; date: boolean; 'datetime-local': boolean; email: boolean; month: boolean; number: boolean; range: boolean; search: boolean; tel: boolean; text: boolean; time: boolean; url: boolean; week: boolean; textarea: boolean; select: boolean; password: boolean; }>; declare interface MixpanelIntegrationOptions extends DefaultIntegrationOptions { projectToken?: string; } declare type NetworkRecordingOptions = { /** * Enables recording of network requests. * The data includes the URLs, the size of the request, and how long the request took. * @default true */ enabled?: boolean; /** * This enables recording XMLHttpRequest and Fetch headers and bodies. * @default false */ recordHeadersAndBody?: boolean; /** * This disables recording WebSocket events. * WebSocket events are recorded by default if recordHeadersAndBody is set. * @default false */ disableWebSocketEventRecordings?: boolean; /** * Request and response headers where the value is not recorded. * The header value is replaced with '[REDACTED]'. * These headers are case-insensitive. * `recordHeadersAndBody` needs to be enabled. * This option will be ignored if `headerKeysToRecord` is set. * @example * networkHeadersToRedact: ['Secret-Header', 'Plain-Text-Password'] */ networkHeadersToRedact?: string[]; /** * Specifies the keys for request/response JSON body that should not be recorded. * The body value is replaced with '[REDACTED]'. * These keys are case-insensitive. * `enabled` and `recordHeadersAndBody` need to be `true`. Otherwise this option will be ignored. * @example bodyKeysToRedact: ['secret-token', 'plain-text-password'] * // Only `body.id` and `body.pageNumber` will be recorded. * body = { * 'id': '123', * 'pageNumber': '1', * 'secret-token': 'super-sensitive-value', * 'plain-text-password': 'password123', * } */ networkBodyKeysToRedact?: string[]; /** * URLs to not record headers and bodies for, and to not propagate trace * headers (e.g. `traceparent`) to. Each entry is matched as a * case-insensitive substring of the full request URL; a match suppresses * both recording and trace-header propagation for that request. * To disable recording headers and bodies for all URLs, set `recordHeadersAndBody` to `false`. * @default ['https://www.googleapis.com/identitytoolkit', 'https://securetoken.googleapis.com'] */ urlBlocklist?: string[]; /** * Specifies the keys for request/response headers to record. * This option will override `networkHeadersToRedact` if specified. * `enabled` and `recordHeadersAndBody` need to be `true`. Otherwise this option will be ignored. * @example headerKeysToRecord: ['id', 'pageNumber'] * // Only `headers.id` and `headers.pageNumber` will be recorded. * headers = { * 'id': '123', * 'pageNumber': '1', * 'secret-token': 'super-sensitive-value', * 'plain-text-password': 'password123', * } */ headerKeysToRecord?: string[]; /** * Specifies the keys for request/response JSON body to record. * This option will override `networkBodyKeysToRedact` if specified. * `enabled` and `recordHeadersAndBody` need to be `true`. Otherwise this option will be ignored. * @example bodyKeysToRecord: ['id', 'pageNumber'] * // Only `body.id` and `body.pageNumber` will be recorded. * body = { * 'id': '123', * 'pageNumber': '1', * 'secret-token': 'super-sensitive-value', * 'plain-text-password': 'password123', * } */ bodyKeysToRecord?: string[]; /** * Record frontend network request metrics that are sent to * the following list of domains. A domain substring match is used to * determine if a network request matches one of the following values. * @example destinationDomains: ['backend.example.com'] * // if your frontend makes requests to `backend.example.com` that you would like to record */ destinationDomains?: string[]; /** * Function to edit/remove data in request/response pairs or ignore the pair entirely. * @example * ``` * requestResponseSanitizer: pair => { * if (pair.request.url.toLowerCase().indexOf('ignore') !== -1) { * // ignore the entire request/response pair (no network logs) * return null * } * * if (pair.response.body.indexOf('secret') !== -1) { * // remove the body in the response * delete pair.response.body; * } * * return pair * } * ``` * */ requestResponseSanitizer?: (pair: RequestResponsePair) => RequestResponsePair | null; }; export declare class Observe extends Plugin_2<ObserveOptions> implements LDPlugin { observe: Observe_2 | undefined; options: ObserveOptions | undefined; constructor(options?: ObserveOptions); private initialize; getMetadata(): { name: string; }; register(client: LDClientMin, environmentMetadata: LDPluginEnvironmentMetadata): void; getHooks?(metadata: LDPluginEnvironmentMetadata): Hook[]; } declare interface Observe_2 { /** * Start the observability data capture when running in `manualStart` mode. */ start: () => Promise<void>; /** * Stop the observability data capture. */ stop: () => Promise<void>; /** * Record arbitrary metric values via as a Gauge. * A Gauge records any point-in-time measurement, such as the current CPU utilization %. * Values with the same metric name and attributes are aggregated via the OTel SDK. * See https://opentelemetry.io/docs/specs/otel/metrics/data-model/ for more details. */ recordGauge: (metric: OTelMetric) => void; /** * Record arbitrary metric values via as a Counter. * A Counter efficiently records an increment in a metric, such as number of cache hits. * Values with the same metric name and attributes are aggregated via the OTel SDK. * See https://opentelemetry.io/docs/specs/otel/metrics/data-model/ for more details. */ recordCount: (metric: OTelMetric) => void; /** * Record arbitrary metric values via as a Counter. * A Counter efficiently records an increment in a metric, such as number of cache hits. * Values with the same metric name and attributes are aggregated via the OTel SDK. * See https://opentelemetry.io/docs/specs/otel/metrics/data-model/ for more details. */ recordIncr: (metric: Omit<OTelMetric, 'value'>) => void; /** * Record arbitrary metric values via as a Histogram. * A Histogram efficiently records near-by point-in-time measurement into a bucketed aggregate. * Values with the same metric name and attributes are aggregated via the OTel SDK. * See https://opentelemetry.io/docs/specs/otel/metrics/data-model/ for more details. */ recordHistogram: (metric: OTelMetric) => void; /** * Record arbitrary metric values via as a UpDownCounter. * A UpDownCounter efficiently records an increment or decrement in a metric, such as number of paying customers. * Values with the same metric name and attributes are aggregated via the OTel SDK. * See https://opentelemetry.io/docs/specs/otel/metrics/data-model/ for more details. */ recordUpDownCounter: (metric: OTelMetric) => void; /** * Starts a new span for tracing in Highlight. The span will be ended when the * callback function returns. * * @example * ```typescript * H.startSpan('span-name', callbackFn) * ``` * @example * ```typescript * H.startSpan('span-name', options, callbackFn) * ``` * @example * ```typescript * H.startSpan('span-name', options, context, callbackFn) * ``` * @example * ```typescript * H.startSpan('span-name', async (span) => { * span.setAttribute('key', 'value') * await someAsyncFunction() * }) * ``` * * @param name The name of the span. * @param options Options for the span. * @param context The context for the span. * @param callbackFn The function to run in the span. */ startSpan: { <F extends (span?: Span) => ReturnType<F>>(name: string, fn: F): ReturnType<F>; <F extends (span?: Span) => ReturnType<F>>(name: string, options: SpanOptions, fn: F): ReturnType<F>; <F extends (span?: Span) => ReturnType<F>>(name: string, options: SpanOptions, context: Context, fn: F): ReturnType<F>; }; /** * Starts a new span for tracing in Highlight. The span will be ended when the * `end()` is called on the span. It returns whatever is returned from the * callback function. * * @example * ```typescript * H.startManualSpan('span-name', options, (span) => { * span.addEvent('event-name', { key: 'value' }) * span.setAttribute('key', 'value') * await someAsyncFunction() * span.end() * }) * ``` * * @example * ```typescript * const span = H.startManualSpan('span-name', (s) => s) * span.addEvent('event-name', { key: 'value' }) * await someAsyncFunction() * span.end() * ``` * * @param name The name of the span. * @param options Options for the span. * @param context The context for the span. * @param fn The function to run in the span. */ startManualSpan: { <F extends (span: Span) => ReturnType<F>>(name: string, fn: F): ReturnType<F>; <F extends (span: Span) => ReturnType<F>>(name: string, options: SpanOptions, fn: F): ReturnType<F>; <F extends (span: Span) => ReturnType<F>>(name: string, options: SpanOptions, context: Context, fn: F): ReturnType<F>; }; /** * Calling this method will report an error in Highlight and map it to the current session being recorded. * A common use case for `H.error` is calling it right outside of an error boundary. * @see {@link https://docs.highlight.run/grouping-errors} for more information. */ recordError: (error: any, message?: string, payload?: { [key: string]: string; }, source?: string, type?: ErrorMessageType) => void; /** * Record arbitrary logs from your own integrations or manual. * Useful when you don't want to emit a console log to the browser dev tools but still want to report a custom log. */ recordLog: (message: any, level: ConsoleMethods, metadata?: Attributes) => void; register(client: LDClientMin, environmentMetadata: LDPluginEnvironmentMetadata): void; getHooks?(metadata: LDPluginEnvironmentMetadata): Hook[]; setLDContextKeyAttributes(contextKeys: Attributes): void; getLDContextKeyAttributes(): Attributes | undefined; } declare type ObserveOptions = CommonOptions & { /** * Specifies where the backend of the app lives. If specified, the SDK will attach the * traceparent header to outgoing requests whose destination URLs match a substring * or regexp from this list, so that backend errors can be linked back to the session. * If 'true' is specified, all requests to the current domain will be matched. * @example tracingOrigins: ['localhost', /^\//, 'backend.myapp.com'] */ tracingOrigins?: boolean | (string | RegExp)[]; /** * Specifies how and what the SDK records from network requests and responses. */ networkRecording?: boolean | NetworkRecordingOptions; /** * Specifies whether the SDK will record console messages. * @default false */ disableConsoleRecording?: boolean; /** * Specifies whether the SDK will report `console.error` invocations as Errors. * @default false */ reportConsoleErrors?: boolean; /** * Specifies which console methods to record. * The value here will be ignored if `disabledConsoleRecording` is `true`. * @default All console methods. * @example consoleMethodsToRecord: ['log', 'info', 'error'] */ consoleMethodsToRecord?: ConsoleMethods[]; /** * Specifies whether to record performance metrics (e.g. FPS, device memory). * @default true */ enablePerformanceRecording?: boolean; /** * Specifies whether to record main-thread `longtask` entries (>50ms) as * `long_task.duration` histogram samples. * @default true */ enableLongtaskRecording?: boolean; /** * Specifies whether to subscribe to the browser Reporting API and emit * CSP/intervention reports as errors and deprecation reports as warn logs. * @default true */ enableReportingObserver?: boolean; /** * Specifies the environment your application is running in. * This is useful to distinguish whether your session was recorded on localhost or in production. * @default 'production' */ environment?: 'development' | 'staging' | 'production' | string; /** * Specifies whether window.Promise should be patched * to record the stack trace of promise rejections. * @default true */ enablePromisePatch?: boolean; /** * OTLP options for OpenTelemetry tracing. Instrumentations are enabled by default. */ otel?: OtelOptions & { /** * OTLP HTTP endpoint for OpenTelemetry tracing. */ otlpEndpoint?: string; }; /** * Specifies whether to record product analytics events. * @default true */ productAnalytics?: boolean | ProductAnalyticsEvents; }; declare type OtelInstrumentatonOptions = { '@opentelemetry/instrumentation-document-load'?: false; '@opentelemetry/instrumentation-fetch'?: false; '@opentelemetry/instrumentation-xml-http-request'?: false; '@opentelemetry/instrumentation-user-interaction'?: false; }; declare interface OTelMetric { name: string; value: number; attributes?: Attributes; } declare type OtelOptions = { instrumentations?: OtelInstrumentatonOptions; }; declare class Plugin_2<T extends RecordOptions | ObserveOptions> { protected sessionSecureID: string; constructor(options?: T); configureSession(ldCredential: string, options?: T): void; } declare type PrivacySettingOption = 'strict' | 'default' | 'none'; declare type ProductAnalyticsEvents = { /** * Specifies whether to record product analytics for clicks. * Requires the use of the @opentelemetry/instrumentation-user-interaction instrumentation. * @default true */ clicks?: boolean; /** * Specifies whether to record product analytics for page views. * Requires the use of the @opentelemetry/instrumentation-document-load instrumentation. * @default true */ pageViews?: boolean; /** * Specifies whether to record product analytics for custom events. * @default true */ trackEvents?: boolean; }; declare type RecordOptions = CommonOptions & { /** * Specifies where the backend of the app lives. If specified, the SDK will attach the * traceparent header to outgoing requests whose destination URLs match a substring * or regexp from this list, so that backend errors can be linked back to the session. * If 'true' is specified, all requests to the current domain will be matched. * @example tracingOrigins: ['localhost', /^\//, 'backend.myapp.com'] */ tracingOrigins?: boolean | (string | RegExp)[]; /** * If set, the SDK will not record when your app is not visible (in the background). * By default, the SDK will record in the background. * @default false */ disableBackgroundRecording?: boolean; enableSegmentIntegration?: boolean; /** * Specifies the environment your application is running in. * This is useful to distinguish whether your session was recorded on localhost or in production. * @default 'production' */ environment?: 'development' | 'staging' | 'production' | string; /** * Specifies how much data the SDK should redact during recording. * strict - the SDK will redact all text data, input fields, images and videos on the page. * default - the SDK will redact text data on the page that resembles PII (based on regex patterns). * none - the SDK will not redact any text data on the page. * // Redacted text will be randomized. Instead of seeing "Hello World" in a recording, you will see "1fds1 j59a0". * @see {@link https://launchdarkly.com/docs/sdk/features/session-replay-config#privacy} for more information. */ privacySetting?: PrivacySettingOption; /** * Enable masking all <input/> elements. Only applies if privacySetting is `none`. */ maskAllInputs?: boolean; /** * Customize the input element types that are masked. Only applies if privacySetting is `none`. */ maskInputOptions?: MaskInputOptions; /** * Customize which elements' text should be masked by specifying a CSS class name or RegExp. * Default class is 'highlight-mask'. */ maskTextClass?: string | RegExp; /** * Customize which elements' text should be masked via a CSS selector that will match the element * and its descendants. */ maskTextSelector?: string; /** * Customize which elements should be blocked (not recorded) by specifying a class name or RegExp. * Default is 'highlight-block'. */ blockClass?: string | RegExp; /** * Customize which elements should be blocked via a CSS selector. */ blockSelector?: string; /** * Customize which elements and their descendants should be ignored from DOM events by class. * Default is 'highlight-ignore'. */ ignoreClass?: string; /** * Customize which elements should be ignored from DOM events via a CSS selector. */ ignoreSelector?: string; /** * Specifies whether to record canvas elements or not. * @default false */ enableCanvasRecording?: boolean; /** * Configure the recording sampling options, eg. how frequently we record canvas updates. */ samplingStrategy?: SamplingStrategy; /** * Specifies whether to inline images into the recording. * This means that images that are local to the client (eg. client-generated blob: urls) * will be serialized into the recording and will be valid on replay. * This will also use canvas snapshotting to inline <video> elements * that use `src="blob:..."` data or webcam feeds (blank src) as <canvas> elements * Only enable this if you are running into issues with client-local images. * Will negatively affect performance. * @default false */ inlineImages?: boolean; /** * Specifies whether to inline <video> elements into the recording. * This means that video that are not accessible at a later time * (eg., a signed URL that is short lived) * will be serialized into the recording and will be valid on replay. * Only enable this if you are running into issues with the normal serialization. * Will negatively affect performance. * @default false */ inlineVideos?: boolean; /** * Specifies whether to inline stylesheets into the recording. * This means that stylesheets that are local to the client (eg. client-generated blob: urls) * will be serialized into the recording and will be valid on replay. * Only enable this if you are running into issues with client-local stylesheets. * May negatively affect performance. * @default true */ inlineStylesheet?: boolean; /** * Enables recording of cross-origin iframes. Should be set in both the parent window and * in the cross-origin iframe. * @default false */ recordCrossOriginIframe?: boolean; integrations?: IntegrationOptions; /** * Specifies the keyboard shortcut to open the current session replay. * @see {@link https://launchdarkly.com/docs/sdk/features/session-replay-config#retrieve-session-urls-on-the-client} for more information. */ sessionShortcut?: SessionShortcutOptions; /** * By default, data is serialized and send by the Web Worker. Set to `local` to force * sending from the main js thread. Only use `local` for custom environments where Web Workers * are not available (ie. Figma plugins). */ sendMode?: 'webworker' | 'local'; }; declare interface Request_2 { sessionSecureID: string; id: string; url: string; verb: string; headers?: Headers_2; body: any; } declare interface RequestResponsePair { request: Request_2; response: Response_2; /** Whether this URL matched a `urlToBlock` so the contents should not be recorded. */ urlBlocked: boolean; } declare interface Response_2 { status: number; headers?: { [k: string]: string; }; body: any; /** Number of Bytes transferred over the network. */ size?: number; } declare type SamplingStrategy = { /** * 'all' will record every single canvas call. * a number will record an image snapshots in a web-worker a (maximum) number of times per second. * Number is only supported where [`OffscreenCanvas`](http://mdn.io/offscreencanvas) is supported. */ canvas?: 'all' | number; /** * For manual usage of `H.snapshot(element) from your canvas rendering function. * See https://www.highlight.io/docs/getting-started/client-sdk/replay-configuration/canvas for setup.` * a number will record an image snapshots in a web-worker a (maximum) number of times per second. */ canvasManualSnapshot?: number; /** * A quality at which to take canvas snapshots. See https://developer.mozilla.org/en-US/docs/Web/API/createImageBitmap * @deprecated This value is no longer used. */ canvasQuality?: 'pixelated' | 'low' | 'medium' | 'high'; /** * A multiplier resolution at which to take canvas snapshots. */ canvasFactor?: number; /** * The maximum dimension to take canvas snapshots at. * This setting takes precedence over resizeFactor if the resulting image size * from the resizeFactor calculation is larger than this value. * Eg: set to 600 to ensure that the canvas is saved with images no larger than 600px * in either dimension (while preserving the original canvas aspect ratio). */ canvasMaxSnapshotDimension?: number; /** * Default behavior for WebGL canvas elements with `preserveDrawingBuffer: false` is to clear the buffer to * load the canvas into memory to avoid getting a transparent bitmap. * Set to false to disable the clearing (in case there are visual glitches in the canvas). */ canvasClearWebGLBuffer?: boolean; /** * Time (in milliseconds) to wait before the initial snapshot of canvas/video elements. */ canvasInitialSnapshotDelay?: number; /** * Settings for canvas data serialization. Defaults to {"image/webp", 0.9} for browsers * that support WebP and {"image/jpeg", 0.6} for others. Can be overridden to any type * or quality value supported by [`toDataURL`](http://mdn.io/toDataURL). */ dataUrlOptions?: Partial<{ type: string; quality: number; }>; }; declare type SessionShortcutOptions = false | string; export { }