Apache Flagon UserALE.js provides a comprehensive JavaScript and TypeScript API for runtime configuration, custom log enrichment/filtering, header injection, and pluggable wire exporters.
options(newConfig?: Partial<Settings.Config>): Settings.ConfigReads or shallow-merges runtime configuration options. Calling options() with no arguments returns the current configuration object.
import * as userale from 'flagon-userale'; userale.options({ url: 'https://collector.example.com/v1/logs', userId: 'user-42', toolName: 'checkout-ui', toolVersion: '2.5.0', exporterFormat: 'otlp-json', });
start(): void & stop(): voidStarts or halts event capturing and log transmission. Particularly useful when autostart: false (data-autostart="false") is configured until user consent or authentication completes.
userale.options({ autostart: false }); // Later, after user consent: userale.start();
version: stringExports the current flagon-userale package version string.
addCallbacks(callbacks: Record<string, CallableFunction>): booleanRegisters one or more named callback functions executed sequentially on every packaged log ((log, event) => log | false). Returning false (or any falsy value) drops the log from the queue; returning a modified log object replaces it.
userale.addCallbacks({ dropIntervalLogs(log) { if (log.logType === 'interval') { return false; } return log; }, attachExperimentTag(log, event) { return { ...log, experimentVariant: 'checkout-v2', }; }, });
removeCallbacks(targetKeys: string[]): voidRemoves registered callbacks by name:
userale.removeCallbacks(['dropIntervalLogs']);
log(customLog: Logging.CustomLog): booleanAppends a custom log object directly onto the UserALE queue:
userale.log({ target: 'checkout-funnel', type: 'step_complete', logType: 'custom', userAction: false, details: { step: 2, cartTotal: 149.0 }, });
packageLog(e: Event, detailFcn?: Logging.DynamicDetailFunction): boolean & packageCustomLog(customLogs: Logging.CustomLog, detailFcn: Logging.DynamicDetailFunction, userAction: boolean): booleanPackages a standard DOM Event or custom metadata object with full UserALE page/session/browser context and enqueues it.
details(config: Configuration, type: Events.AllowedEvents): ((e: Event) => Record<string, unknown>) | nullReturns the default detail-extractor function for a given DOM event type (click, keydown, input, change, wheel, etc.). Note that <input type="password"> values are automatically masked ("********").
getSelector(ele: EventTarget): string & buildPath(e: Event): string[]Utility helpers that compute a CSS-like element selector (tag#id.class) and full DOM ancestry path array from a DOM Event.
getTargetRect(el: EventTarget | null): TargetRect | null & detectBrowser(): { browser: string; version: string }getTargetRect extracts a rounded bounding box ({ x, y, width, height, top, right, bottom, left }) for a DOM element (attached automatically as targetRect on click, dblclick, change, and submit events).detectBrowser detects the browser name and version using navigator.userAgentData (User-Agent Client Hints) with a regex fallback and zero external dependencies.All HTTP-based exporters (HttpJsonExporter, ProtoJsonExporter, OtlpJsonExporter) construct headers through buildHeaders(config), applying them to both interval fetch() requests and pagehide (fetch(..., { keepalive: true })) flushes.
registerAuthCallback(callback: () => string): boolean & resetAuthCallback(): voidRegisters (or resets) a dynamic provider returning the Authorization header value on each batch export:
userale.registerAuthCallback(() => `Bearer ${getCurrentJwtToken()}`);
registerHeadersCallback(callback: () => Record<string, string>): boolean & resetHeadersCallback(): voidRegisters (or resets) a dynamic provider returning arbitrary HTTP headers on each batch export:
userale.registerHeadersCallback(() => ({ 'x-tenant-id': 'acme-corp', 'x-request-source': 'userale-web', }));
Static headers can also be configured via userale.options({ authHeader: 'Bearer ...', apiKey: '...', headers: { ... } }).
LogExporter)UserALE decouples in-memory event packaging from wire serialization and transport via the LogExporter interface:
export interface LogExporter { export(logs: Array<Logging.Log>, options?: ExportOptions): Promise<ExportResult>; flush?(): Promise<void>; shutdown?(): Promise<void>; }
| Exported Symbol | Description |
|---|---|
registerExporter(exporter: LogExporter) | Registers a custom LogExporter instance and switches the active sender to use it immediately. |
createExporter(config) | Factory returning the active LogExporter for a Configuration instance. |
HttpJsonExporter | Default exporter sending flat UserALE JSON arrays over fetch (with 48 KiB chunked keepalive: true on pagehide). |
WebSocketExporter | Streams log batches over a persistent WebSocket (ws:// / wss://). |
ProtoJsonExporter / toProtoLogBatch(logs) / toProtoUserALELog(log) | Serializes logs into canonical Protobuf v1 JSON (apache.flagon.userale.v1.LogBatch) matching proto/apache/flagon/userale/v1/log.proto. |
OtlpJsonExporter / toOtlpExportLogsServiceRequest(logs, config) | Serializes logs into OpenTelemetry ExportLogsServiceRequest JSON with Resource hoisting, BigInt nanosecond timestamps, eventName, structured kvlistValue body, and W3C traceId/spanId correlation. |
OtelSdkExporter | Bridges UserALE logs directly into an existing OpenTelemetry JS @opentelemetry/api-logs Logger via logger.emit(...). |
resolveOtlpLogsEndpoint(endpoint) | Normalizes an OTLP base URL by appending /v1/logs when the URL path is empty or /. |
toUnixNanoString(clientTimeMs, microTime) | Converts millisecond + sub-millisecond timestamps to a 64-bit nanosecond string using BigInt. |
import * as userale from 'flagon-userale'; userale.options({ exporterType: 'otlp', otlpEndpoint: 'http://localhost:4318/v1/logs', toolName: 'storefront-web', traceContext: () => ({ traceId: currentActiveSpan.traceId, // 32-hex W3C trace-id spanId: currentActiveSpan.spanId, // 16-hex W3C span-id }), });
#104)UserALE provides a two-layer session replay architecture that keeps the core bundle dependency-free while supporting both structural DOM telemetry and rrweb-compatible visual playback:
| Exported Symbol | Description |
|---|---|
startSessionReplay(options?: SessionReplayOptions, config?: Configuration): () => void | Starts Layer 1 (MutationObserver + RageClickDetector) and optional Layer 2 (rrweb ring-buffer recorder). Returns a stop/cleanup function. |
flushSessionReplay(trigger?: string): Promise<ReplayEnvelope | null> | Immediately flushes the rolling rrweb ring buffer as a logType: 'replay', type: 'session_replay' envelope tagged with sessionId and active OpenTelemetry traceId / spanId. |
stopSessionReplay(): void | Stops active MutationObserver and rrweb recording and clears the ring buffer. |
getActiveReplayRingBuffer(): ReplayRingBuffer | null | Returns the active ReplayRingBuffer instance when session replay is running. |
ReplayRingBuffer | Rolling in-memory ring buffer (maxDurationMs, maxFrames) that preserves rrweb Meta (type: 4) and latest FullSnapshot (type: 2) anchor frames. |
RageClickDetector | Sliding-window detector that triggers when threshold (default 3) clicks occur on the same target selector within windowMs (1000ms) and maxRadiusPx (30px). |
import * as userale from 'flagon-userale'; import * as rrweb from 'rrweb'; userale.startSessionReplay({ recorder: rrweb.record, maxDurationMs: 30_000, observeMutations: true, detectRageClicks: true, flushOnError: true, });
Contributions are welcome! See our [Contributing Guide]({{ ‘/docs/contributing’ | prepend: site.baseurl }}) or open an issue on GitHub.