@prilog/monitoring-browser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/index.js +67 -0
  4. package/package.json +30 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prilog
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @prilog/monitoring-browser
2
+
3
+ Prilog monitoring for browser and React applications, built on OpenTelemetry. Capture application logs, page and network traces, and exceptions in your Prilog workspace.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @prilog/monitoring-browser
9
+ ```
10
+
11
+ ## Initialize
12
+
13
+ Select **Add free Prilog monitoring** for your service in Prilog and copy its publishable ingest DSN. Initialize the SDK once in your browser entry point, before rendering your application:
14
+
15
+ ```javascript
16
+ import { init } from '@prilog/monitoring-browser';
17
+
18
+ const monitoring = init({
19
+ dsn: 'YOUR_PRILOG_DSN',
20
+ serviceName: 'checkout-web',
21
+ environment: 'production',
22
+ });
23
+
24
+ // Render your existing React application or start your browser application here.
25
+ ```
26
+
27
+ Replace `YOUR_PRILOG_DSN` with the DSN shown in Prilog. It grants telemetry ingestion for that service. Initialization sends a startup log and span; Prilog verifies the connection when it receives them.
28
+
29
+ The package is an ES module for browser bundlers. `init` returns a disabled monitor when called without a DSN or outside a browser.
30
+
31
+ ## Logs, traces, and exceptions
32
+
33
+ The SDK instruments fetch, XMLHttpRequest, and document loading, and captures global errors and unhandled promise rejections.
34
+
35
+ ```javascript
36
+ import { withSpan, log, captureException } from '@prilog/monitoring-browser';
37
+
38
+ withSpan('checkout.validate', () => {
39
+ log('info', 'Order validated', { 'order.id': 'example-order' });
40
+ });
41
+
42
+ // In an existing error boundary or handled-error path:
43
+ captureException(new Error('Checkout failed'));
44
+ ```
45
+
46
+ `withSpan(name, callback, attributes?)` returns the callback's result and records and rethrows errors. `log(level, message, attributes?)` supports `debug`, `info`, `warn`, and `error`. `captureException(error)` records the exception and its stack trace.
47
+
48
+ ## Configuration
49
+
50
+ | Option | Description | Default |
51
+ | --- | --- | --- |
52
+ | `dsn` | Your service's publishable ingest DSN | Required to enable monitoring |
53
+ | `serviceName` | Frontend service identity | `frontend` |
54
+ | `environment` | Deployment environment | `production` |
55
+ | `release` | Application release/version | `unknown` |
56
+ | `tracePropagationTargets` | URLs or regular expressions for API requests that receive trace headers | Current origin |
57
+
58
+ For distributed tracing across your frontend and backend, set `tracePropagationTargets` to your API origins and configure the API's CORS policy to accept `traceparent`, `tracestate`, and `baggage` headers. If your application uses Content Security Policy, include the Prilog ingest origin in `connect-src`.
59
+
60
+ The SDK attempts to flush pending telemetry on `pagehide`. Await `monitoring.shutdown()` when explicitly tearing down the monitored application.
61
+
62
+ ## License
63
+
64
+ MIT. See [LICENSE](./LICENSE).
65
+
66
+ Source and issue tracker: [Prilog-ai/prilog-monitoring](https://github.com/Prilog-ai/prilog-monitoring).
package/index.js ADDED
@@ -0,0 +1,67 @@
1
+ import { context, trace, SpanStatusCode } from '@opentelemetry/api';
2
+ import { logs } from '@opentelemetry/api-logs';
3
+ import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
4
+ import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
5
+ import { LoggerProvider, BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
6
+ import { resourceFromAttributes } from '@opentelemetry/resources';
7
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
8
+ import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
9
+ import { registerInstrumentations } from '@opentelemetry/instrumentation';
10
+ import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
11
+ import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
12
+ import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
13
+
14
+ let state;
15
+
16
+ export function init(options = {}) {
17
+ if (state) return state;
18
+ if (typeof window === 'undefined' || !options.dsn) return { enabled: false, shutdown: async () => {} };
19
+ const url = new URL(options.dsn);
20
+ if (!['https:', 'http:'].includes(url.protocol) || !url.username.startsWith('pk_prilog_') || url.password || url.search || url.hash) throw new Error('Invalid publishable Prilog DSN');
21
+ const headers = { 'X-Prilog-OTLP-Token': decodeURIComponent(url.username) };
22
+ url.username = '';
23
+ const endpoint = url.toString().replace(/\/$/, '');
24
+ const resource = resourceFromAttributes({ 'service.name': options.serviceName || 'frontend', 'deployment.environment.name': options.environment || 'production', 'service.version': options.release || 'unknown', 'prilog.sdk.version': '0.1.0' });
25
+ const traces = new WebTracerProvider({ resource, spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter({ url: `${endpoint}/v1/traces`, headers }))] });
26
+ traces.register();
27
+ const loggerProvider = new LoggerProvider({ resource });
28
+ loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(new OTLPLogExporter({ url: `${endpoint}/v1/logs`, headers })));
29
+ logs.setGlobalLoggerProvider(loggerProvider);
30
+ const ignoreUrls = [new RegExp(`^${endpoint.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`)];
31
+ const propagateTraceHeaderCorsUrls = options.tracePropagationTargets || [window.location.origin];
32
+ const unregister = registerInstrumentations({ instrumentations: [new FetchInstrumentation({ ignoreUrls, propagateTraceHeaderCorsUrls }), new XMLHttpRequestInstrumentation({ ignoreUrls, propagateTraceHeaderCorsUrls }), new DocumentLoadInstrumentation()] });
33
+ const onError = event => captureException(event.error || new Error(event.message));
34
+ const onRejection = event => captureException(event.reason);
35
+ const flush = () => { void Promise.all([traces.forceFlush(), loggerProvider.forceFlush()]).catch(() => {}); };
36
+ window.addEventListener('error', onError);
37
+ window.addEventListener('unhandledrejection', onRejection);
38
+ window.addEventListener('pagehide', flush);
39
+ state = { enabled: true, async shutdown() { window.removeEventListener('error', onError); window.removeEventListener('unhandledrejection', onRejection); window.removeEventListener('pagehide', flush); unregister(); await Promise.all([traces.shutdown(), loggerProvider.shutdown()]); } };
40
+ withSpan('prilog.startup', () => log('info', 'Prilog monitoring initialized', { 'prilog.startup': true }));
41
+ return state;
42
+ }
43
+
44
+ export function log(level, message, attributes = {}) {
45
+ logs.getLogger('prilog.monitoring').emit({ severityNumber: { debug: 5, info: 9, warn: 13, error: 17 }[level] || 9, severityText: level.toUpperCase(), body: String(message), attributes, context: context.active() });
46
+ }
47
+
48
+ export function captureException(value) {
49
+ const error = value instanceof Error ? value : new Error(String(value));
50
+ const active = trace.getActiveSpan();
51
+ const span = active || trace.getTracer('prilog.monitoring').startSpan('exception');
52
+ context.with(trace.setSpan(context.active(), span), () => {
53
+ span.recordException(error); span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
54
+ log('error', error.message, { 'exception.type': error.name, 'exception.message': error.message, 'exception.stacktrace': error.stack || '' });
55
+ });
56
+ if (!active) span.end();
57
+ }
58
+
59
+ export function withSpan(name, fn, attributes = {}) {
60
+ return trace.getTracer('prilog.monitoring').startActiveSpan(name, { attributes }, span => {
61
+ try {
62
+ const result = fn(span);
63
+ if (result && typeof result.then === 'function') return Promise.resolve(result).catch(error => { context.with(trace.setSpan(context.active(), span), () => captureException(error)); throw error; }).finally(() => span.end());
64
+ span.end(); return result;
65
+ } catch (error) { captureException(error); span.end(); throw error; }
66
+ });
67
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@prilog/monitoring-browser",
3
+ "version": "0.1.0",
4
+ "description": "OpenTelemetry browser monitoring for Prilog",
5
+ "repository": { "type": "git", "url": "git+https://github.com/Prilog-ai/prilog-monitoring.git" },
6
+ "homepage": "https://github.com/Prilog-ai/prilog-monitoring#readme",
7
+ "bugs": { "url": "https://github.com/Prilog-ai/prilog-monitoring/issues" },
8
+ "type": "module",
9
+ "main": "index.js",
10
+ "exports": { ".": "./index.js" },
11
+ "license": "MIT",
12
+ "files": ["index.js", "LICENSE"],
13
+ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
14
+ "scripts": { "test": "node --test test.mjs", "prepublishOnly": "npm test" },
15
+ "devDependencies": { "playwright": "^1.58.2", "vite": "^8.0.10" },
16
+ "dependencies": {
17
+ "@opentelemetry/api": "^1.9.0",
18
+ "@opentelemetry/api-logs": "^0.200.0",
19
+ "@opentelemetry/sdk-trace-web": "^2.0.0",
20
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
21
+ "@opentelemetry/sdk-logs": "^0.200.0",
22
+ "@opentelemetry/exporter-logs-otlp-http": "^0.200.0",
23
+ "@opentelemetry/exporter-trace-otlp-http": "^0.200.0",
24
+ "@opentelemetry/resources": "^2.0.0",
25
+ "@opentelemetry/instrumentation": "^0.200.0",
26
+ "@opentelemetry/instrumentation-fetch": "^0.200.0",
27
+ "@opentelemetry/instrumentation-xml-http-request": "^0.200.0",
28
+ "@opentelemetry/instrumentation-document-load": "^0.48.0"
29
+ }
30
+ }