@sigx/lynx-observability 0.5.2
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.
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/error-capture.d.ts +13 -0
- package/dist/error-capture.d.ts.map +1 -0
- package/dist/error-capture.js +117 -0
- package/dist/error-capture.js.map +1 -0
- package/dist/http-sink.d.ts +23 -0
- package/dist/http-sink.d.ts.map +1 -0
- package/dist/http-sink.js +75 -0
- package/dist/http-sink.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +30 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +27 -0
- package/dist/init.js.map +1 -0
- package/dist/install.d.ts +13 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +25 -0
- package/dist/install.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Andreas Ekdahl
|
|
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,103 @@
|
|
|
1
|
+
# @sigx/lynx-observability
|
|
2
|
+
|
|
3
|
+
Opt-in **production error capture** and **provider-agnostic log/error sinks** for sigx-lynx. Builds on the logger in [`@sigx/lynx-core`](https://github.com/signalxjs/lynx/tree/main/packages/lynx-core#logging): uncaught errors are funneled in as `error`-level records, and a "sink" is just a `LogTransport`. No hard dependency on any vendor SDK.
|
|
4
|
+
|
|
5
|
+
> Logging itself ships in the framework (`import { createLogger } from '@sigx/lynx'`). This package adds the *production* pieces — catching crashes and shipping records off-device — and is installed only when you want them.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @sigx/lynx-observability
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start — declarative (recommended)
|
|
14
|
+
|
|
15
|
+
Declare it in `signalx.config.ts` and it auto-wires in **release** builds — no code in your app entry:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// signalx.config.ts
|
|
19
|
+
export default defineLynxConfig({
|
|
20
|
+
name: 'my-app',
|
|
21
|
+
logging: {
|
|
22
|
+
level: 'warn', // logger level (also dev: 'debug' / release: 'warn' default)
|
|
23
|
+
namespaces: { disabled: ['http'] },// silence namespaces at startup
|
|
24
|
+
production: {
|
|
25
|
+
sink: { url: 'https://logs.example.com/ingest', headers: { 'x-api-key': KEY }, sampleRate: 0.25 },
|
|
26
|
+
captureErrors: true, // default
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Just install the package (`pnpm add @sigx/lynx-observability`) — `@sigx/lynx-plugin` prepends the init for you in release builds. (Dev uses the console streamer; observability auto-wiring is release-only.)
|
|
33
|
+
|
|
34
|
+
## Quick start — manual
|
|
35
|
+
|
|
36
|
+
Or wire it yourself, `Sentry.init()`-style (call once in your app entry):
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { initObservability } from '@sigx/lynx-observability';
|
|
40
|
+
|
|
41
|
+
initObservability({
|
|
42
|
+
level: 'warn', // optional: override the default level
|
|
43
|
+
captureErrors: true, // default — catch uncaught errors / rejections
|
|
44
|
+
sink: { // optional remote sink
|
|
45
|
+
url: 'https://logs.example.com/ingest',
|
|
46
|
+
headers: { 'x-api-key': API_KEY },
|
|
47
|
+
sampleRate: 0.25, // keep 25% of non-error records; errors always kept
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
That's it: uncaught errors now flow to your logs (and the `sigx dev` terminal in development), and records at/above the level are batched and POSTed to your endpoint.
|
|
53
|
+
|
|
54
|
+
## Pieces (compose them yourself)
|
|
55
|
+
|
|
56
|
+
- **`installErrorCapture(opts?)`** — registers Lynx's `lynx.onError` (background thread) plus `globalThis` `error`/`unhandledrejection` handlers, normalizes whatever was thrown into an `Error`, and logs it at `error` level under the `uncaught` namespace. The `Error` rides in the record's `fields`, so transports can treat it as an exception (with a stack). Idempotent; returns an uninstall function.
|
|
57
|
+
- **`createHttpSink(opts)`** — a batching `LogTransport` that POSTs `{ records: [...] }` as JSON to `opts.url`. Options: `batchSize`, `flushIntervalMs`, `sampleRate`, `minLevel`, `headers`, `excludeNamespaces`. `Error` fields are serialized to `{ name, message, stack }`. It excludes the `http` namespace by default (its own POSTs log there) and swallows its own send failures, so it can't feed back into itself. Has a `.flush()` for graceful shutdown.
|
|
58
|
+
- **`toError(value)`** — the normalization helper, exported for reuse.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { addTransport } from '@sigx/lynx';
|
|
62
|
+
import { createHttpSink, installErrorCapture } from '@sigx/lynx-observability';
|
|
63
|
+
|
|
64
|
+
addTransport(createHttpSink({ url, minLevel: 'info' }));
|
|
65
|
+
const uninstall = installErrorCapture({ onError: (e) => myAnalytics.track('crash', e.message) });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Wire format
|
|
69
|
+
|
|
70
|
+
The sink POSTs:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{ "records": [ { "level": "error", "namespace": "uncaught", "msg": "[lynx] …", "fields": [ { "name": "TypeError", "message": "…", "stack": "…" } ], "ts": 1733740000000 } ] }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Provider adapters
|
|
77
|
+
|
|
78
|
+
There's no built-in vendor coupling — any provider is a `LogTransport`. Errors arrive as `error`-level records with the `Error` in `fields[0]`, so an adapter can split exceptions from breadcrumbs. Example **Sentry** adapter (Sentry is an optional peer in *your* app, not a dependency of this package):
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import * as Sentry from '@sentry/browser'; // your app's dep
|
|
82
|
+
import { addTransport, installErrorCapture, type LogRecord } from '@sigx/lynx';
|
|
83
|
+
|
|
84
|
+
Sentry.init({ dsn: SENTRY_DSN });
|
|
85
|
+
|
|
86
|
+
addTransport((r: LogRecord) => {
|
|
87
|
+
const err = r.fields.find((f) => f instanceof Error) as Error | undefined;
|
|
88
|
+
if (r.level.name === 'error' && err) {
|
|
89
|
+
Sentry.captureException(err);
|
|
90
|
+
} else {
|
|
91
|
+
Sentry.addBreadcrumb({ category: r.namespace, message: r.msg, level: r.level.name });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
installErrorCapture();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The same shape works for Datadog, a custom backend, etc.
|
|
98
|
+
|
|
99
|
+
## Notes
|
|
100
|
+
|
|
101
|
+
- `lynx.onError` is **background-thread only** upstream; main-thread error capture may need a separate path in the future.
|
|
102
|
+
- For readable stack traces in release builds, upload your source maps to your provider (out of scope here).
|
|
103
|
+
- Declarative `signalx.config.ts` `logging.production` config auto-wires this package in release builds (see Quick start above); `initObservability()` remains for manual/dev setup.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ErrorCaptureOptions {
|
|
2
|
+
/** Extra callback invoked with the normalized Error for each captured error. */
|
|
3
|
+
onError?: (error: Error) => void;
|
|
4
|
+
}
|
|
5
|
+
/** Normalize anything thrown (Error, ErrorEvent, PromiseRejection, string, …) into an Error. */
|
|
6
|
+
export declare function toError(input: unknown): Error;
|
|
7
|
+
/**
|
|
8
|
+
* Install global error capture. Returns an uninstall function. Idempotent —
|
|
9
|
+
* calling it again while already installed is a no-op and returns a no-op
|
|
10
|
+
* uninstall.
|
|
11
|
+
*/
|
|
12
|
+
export declare function installErrorCapture(opts?: ErrorCaptureOptions): () => void;
|
|
13
|
+
//# sourceMappingURL=error-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-capture.d.ts","sourceRoot":"","sources":["../src/error-capture.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,mBAAmB;IAChC,gFAAgF;IAChF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACpC;AAUD,gGAAgG;AAChG,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAa7C;AAMD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,mBAAwB,GAAG,MAAM,IAAI,CAmE9E"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production error capture. Registers Lynx's background-thread error hook
|
|
3
|
+
* (`lynx.onError`) plus the `globalThis` `error` / `unhandledrejection`
|
|
4
|
+
* handlers where present, normalizes whatever was thrown into an `Error`, and
|
|
5
|
+
* funnels it into the core logger as an `error`-level record under the
|
|
6
|
+
* `uncaught` namespace.
|
|
7
|
+
*
|
|
8
|
+
* Because it goes through the core logger, captured errors show up in the
|
|
9
|
+
* `sigx dev` terminal AND reach every registered transport (e.g. a remote
|
|
10
|
+
* sink from {@link createHttpSink}). The original `Error` rides in the record
|
|
11
|
+
* `fields`, so transports can treat it as an exception (with a stack) rather
|
|
12
|
+
* than a plain log line.
|
|
13
|
+
*/
|
|
14
|
+
import { createLogger } from '@sigx/lynx-core';
|
|
15
|
+
const log = createLogger('uncaught');
|
|
16
|
+
function lynxObj() {
|
|
17
|
+
return typeof lynx !== 'undefined' ? lynx : undefined;
|
|
18
|
+
}
|
|
19
|
+
function safeStringify(value) {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.stringify(value) ?? String(value);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return String(value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Normalize anything thrown (Error, ErrorEvent, PromiseRejection, string, …) into an Error. */
|
|
28
|
+
export function toError(input) {
|
|
29
|
+
if (input instanceof Error)
|
|
30
|
+
return input;
|
|
31
|
+
if (input && typeof input === 'object') {
|
|
32
|
+
const o = input;
|
|
33
|
+
// ErrorEvent.error / PromiseRejectionEvent.reason may hold the real Error.
|
|
34
|
+
const inner = o['error'] ?? o['reason'];
|
|
35
|
+
if (inner instanceof Error)
|
|
36
|
+
return inner;
|
|
37
|
+
const msg = o['message'] ?? o['reason'] ?? o['error'];
|
|
38
|
+
const err = new Error(typeof msg === 'string' && msg ? msg : safeStringify(input));
|
|
39
|
+
if (typeof o['stack'] === 'string')
|
|
40
|
+
err.stack = o['stack'];
|
|
41
|
+
return err;
|
|
42
|
+
}
|
|
43
|
+
return new Error(typeof input === 'string' ? input : safeStringify(input));
|
|
44
|
+
}
|
|
45
|
+
// Idempotent across module re-evaluation (HMR) / multiple bundle copies.
|
|
46
|
+
const G = globalThis;
|
|
47
|
+
const INSTALLED = '__sigxObservabilityErrorCaptureInstalled';
|
|
48
|
+
/**
|
|
49
|
+
* Install global error capture. Returns an uninstall function. Idempotent —
|
|
50
|
+
* calling it again while already installed is a no-op and returns a no-op
|
|
51
|
+
* uninstall.
|
|
52
|
+
*/
|
|
53
|
+
export function installErrorCapture(opts = {}) {
|
|
54
|
+
if (G[INSTALLED])
|
|
55
|
+
return () => { };
|
|
56
|
+
G[INSTALLED] = true;
|
|
57
|
+
const report = (input, source) => {
|
|
58
|
+
const err = toError(input);
|
|
59
|
+
// Error object in fields → transports can treat it as an exception.
|
|
60
|
+
log.error(`[${source}] ${err.message}`, err);
|
|
61
|
+
try {
|
|
62
|
+
opts.onError?.(err);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* never let a user hook break capture */
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const undo = [];
|
|
69
|
+
// 1. Lynx background-thread hook (no documented removal — nothing to undo).
|
|
70
|
+
const lx = lynxObj();
|
|
71
|
+
if (typeof lx?.onError === 'function') {
|
|
72
|
+
lx.onError((e) => report(e, 'lynx'));
|
|
73
|
+
}
|
|
74
|
+
// 2. globalThis handlers (web/dev and any host exposing them).
|
|
75
|
+
const g = globalThis;
|
|
76
|
+
if (typeof g.addEventListener === 'function') {
|
|
77
|
+
const onErr = (e) => report(e?.error ?? e, 'error');
|
|
78
|
+
const onRej = (e) => report(e?.reason ?? e, 'unhandledrejection');
|
|
79
|
+
g.addEventListener('error', onErr);
|
|
80
|
+
g.addEventListener('unhandledrejection', onRej);
|
|
81
|
+
undo.push(() => {
|
|
82
|
+
g.removeEventListener?.('error', onErr);
|
|
83
|
+
g.removeEventListener?.('unhandledrejection', onRej);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const prevErr = g.onerror;
|
|
88
|
+
const prevRej = g.onunhandledrejection;
|
|
89
|
+
g.onerror = (...args) => {
|
|
90
|
+
// (message, source, lineno, colno, error)
|
|
91
|
+
report(args[4] ?? args[0], 'error');
|
|
92
|
+
// Chain to any pre-existing handler so we don't clobber host/app
|
|
93
|
+
// error reporting; preserve its return value (truthy = handled).
|
|
94
|
+
if (typeof prevErr === 'function') {
|
|
95
|
+
return prevErr(...args);
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
};
|
|
99
|
+
g.onunhandledrejection = (e) => {
|
|
100
|
+
report(e?.reason ?? e, 'unhandledrejection');
|
|
101
|
+
if (typeof prevRej === 'function')
|
|
102
|
+
prevRej(e);
|
|
103
|
+
};
|
|
104
|
+
undo.push(() => {
|
|
105
|
+
g.onerror = prevErr;
|
|
106
|
+
g.onunhandledrejection = prevRej;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return () => {
|
|
110
|
+
if (!G[INSTALLED])
|
|
111
|
+
return;
|
|
112
|
+
G[INSTALLED] = false;
|
|
113
|
+
for (const u of undo)
|
|
114
|
+
u();
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=error-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-capture.js","sourceRoot":"","sources":["../src/error-capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LogLevelName, LogTransport } from '@sigx/lynx-core';
|
|
2
|
+
export interface HttpSinkOptions {
|
|
3
|
+
/** Endpoint that receives `POST` `{ records: WireRecord[] }`. */
|
|
4
|
+
url: string;
|
|
5
|
+
/** Extra headers (e.g. auth). `content-type: application/json` is set for you. */
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
/** Flush once the buffer reaches this many records. Default 20. */
|
|
8
|
+
batchSize?: number;
|
|
9
|
+
/** Flush at most this often (ms) while records trickle in. Default 5000. */
|
|
10
|
+
flushIntervalMs?: number;
|
|
11
|
+
/** Keep this fraction (0–1) of non-error records; errors are always kept. Default 1. */
|
|
12
|
+
sampleRate?: number;
|
|
13
|
+
/** Only send records at or above this level. Default `'info'`. */
|
|
14
|
+
minLevel?: LogLevelName;
|
|
15
|
+
/** Namespaces to drop. Default `['http']` (prevents the sink's own POSTs feeding back). */
|
|
16
|
+
excludeNamespaces?: string[];
|
|
17
|
+
}
|
|
18
|
+
/** A LogTransport with an extra `flush()` for tests / graceful shutdown. */
|
|
19
|
+
export type HttpSink = LogTransport & {
|
|
20
|
+
flush(): void;
|
|
21
|
+
};
|
|
22
|
+
export declare function createHttpSink(opts: HttpSinkOptions): HttpSink;
|
|
23
|
+
//# sourceMappingURL=http-sink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-sink.d.ts","sourceRoot":"","sources":["../src/http-sink.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAa,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAM7E,MAAM,WAAW,eAAe;IAC5B,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAUD,4EAA4E;AAC5E,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,CAAC;AAOxD,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,QAAQ,CAoD9D"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-agnostic batching HTTP sink — a {@link LogTransport} that buffers
|
|
3
|
+
* log records and POSTs them as JSON to any endpoint (your own backend, an
|
|
4
|
+
* OTLP-style collector, a serverless function, …). Register it with the core
|
|
5
|
+
* logger's `addTransport`.
|
|
6
|
+
*
|
|
7
|
+
* Loop safety: the sink's own POST goes through `@sigx/lynx-http`, which logs
|
|
8
|
+
* under the `http` namespace — so that namespace is excluded by default to
|
|
9
|
+
* stop the sink from feeding its own traffic back into itself. The sink also
|
|
10
|
+
* swallows its own send failures (it never logs them).
|
|
11
|
+
*/
|
|
12
|
+
import { fetch } from '@sigx/lynx-http';
|
|
13
|
+
const SEVERITY = {
|
|
14
|
+
trace: 10, debug: 20, info: 30, warn: 40, error: 50, silent: 100,
|
|
15
|
+
};
|
|
16
|
+
function serializeField(f) {
|
|
17
|
+
if (f instanceof Error)
|
|
18
|
+
return { name: f.name, message: f.message, stack: f.stack };
|
|
19
|
+
return f;
|
|
20
|
+
}
|
|
21
|
+
export function createHttpSink(opts) {
|
|
22
|
+
const batchSize = opts.batchSize ?? 20;
|
|
23
|
+
const flushIntervalMs = opts.flushIntervalMs ?? 5000;
|
|
24
|
+
const sampleRate = opts.sampleRate ?? 1;
|
|
25
|
+
const minSeverity = SEVERITY[opts.minLevel ?? 'info'];
|
|
26
|
+
const exclude = new Set(opts.excludeNamespaces ?? ['http']);
|
|
27
|
+
let buffer = [];
|
|
28
|
+
let timer = null;
|
|
29
|
+
const flush = () => {
|
|
30
|
+
if (timer !== null) {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
timer = null;
|
|
33
|
+
}
|
|
34
|
+
if (buffer.length === 0)
|
|
35
|
+
return;
|
|
36
|
+
const batch = buffer;
|
|
37
|
+
buffer = [];
|
|
38
|
+
// Fire-and-forget; swallow failures so the sink never feeds its own
|
|
39
|
+
// errors back into the logger (which would loop into this transport).
|
|
40
|
+
void fetch(opts.url, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
// content-type is set for us — caller headers can't override it.
|
|
43
|
+
headers: { ...opts.headers, 'content-type': 'application/json' },
|
|
44
|
+
body: JSON.stringify({ records: batch }),
|
|
45
|
+
}).catch(() => { });
|
|
46
|
+
};
|
|
47
|
+
const schedule = () => {
|
|
48
|
+
if (timer === null)
|
|
49
|
+
timer = setTimeout(flush, flushIntervalMs);
|
|
50
|
+
};
|
|
51
|
+
const sink = ((record) => {
|
|
52
|
+
if (record.level.severity < minSeverity)
|
|
53
|
+
return;
|
|
54
|
+
if (exclude.has(record.namespace))
|
|
55
|
+
return;
|
|
56
|
+
// Sample non-error records; always keep errors.
|
|
57
|
+
if (sampleRate < 1 && record.level.severity < SEVERITY.error && Math.random() >= sampleRate) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
buffer.push({
|
|
61
|
+
level: record.level.name,
|
|
62
|
+
namespace: record.namespace,
|
|
63
|
+
msg: record.msg,
|
|
64
|
+
fields: record.fields.map(serializeField),
|
|
65
|
+
ts: record.ts,
|
|
66
|
+
});
|
|
67
|
+
if (buffer.length >= batchSize)
|
|
68
|
+
flush();
|
|
69
|
+
else
|
|
70
|
+
schedule();
|
|
71
|
+
});
|
|
72
|
+
sink.flush = flush;
|
|
73
|
+
return sink;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=http-sink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-sink.js","sourceRoot":"","sources":["../src/http-sink.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sigx/lynx-observability — opt-in production error capture + provider-agnostic
|
|
3
|
+
* log/error sinks for sigx-lynx.
|
|
4
|
+
*
|
|
5
|
+
* Builds on the `@sigx/lynx-core` logger: uncaught errors are funneled in as
|
|
6
|
+
* `error`-level records, and sinks are just `LogTransport`s. Call
|
|
7
|
+
* {@link initObservability} once in your app entry, or compose the pieces
|
|
8
|
+
* ({@link installErrorCapture}, {@link createHttpSink}) yourself.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
export { initObservability } from './init.js';
|
|
13
|
+
export type { ObservabilityOptions } from './init.js';
|
|
14
|
+
export { installErrorCapture, toError } from './error-capture.js';
|
|
15
|
+
export type { ErrorCaptureOptions } from './error-capture.js';
|
|
16
|
+
export { createHttpSink } from './http-sink.js';
|
|
17
|
+
export type { HttpSink, HttpSinkOptions } from './http-sink.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sigx/lynx-observability — opt-in production error capture + provider-agnostic
|
|
3
|
+
* log/error sinks for sigx-lynx.
|
|
4
|
+
*
|
|
5
|
+
* Builds on the `@sigx/lynx-core` logger: uncaught errors are funneled in as
|
|
6
|
+
* `error`-level records, and sinks are just `LogTransport`s. Call
|
|
7
|
+
* {@link initObservability} once in your app entry, or compose the pieces
|
|
8
|
+
* ({@link installErrorCapture}, {@link createHttpSink}) yourself.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
export { initObservability } from './init.js';
|
|
13
|
+
export { installErrorCapture, toError } from './error-capture.js';
|
|
14
|
+
export { createHttpSink } from './http-sink.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-call setup, à la `Sentry.init()`. Call once in your app entry.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { initObservability } from '@sigx/lynx-observability';
|
|
7
|
+
*
|
|
8
|
+
* initObservability({
|
|
9
|
+
* level: 'warn', // optional: override the default level
|
|
10
|
+
* captureErrors: true, // default: capture uncaught errors
|
|
11
|
+
* sink: { url: 'https://logs.example.com/ingest', headers: { 'x-api-key': KEY } },
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { type LogLevelName } from '@sigx/lynx-core';
|
|
16
|
+
import { type HttpSinkOptions } from './http-sink.js';
|
|
17
|
+
import { type ErrorCaptureOptions } from './error-capture.js';
|
|
18
|
+
export interface ObservabilityOptions {
|
|
19
|
+
/** Override the global log level (e.g. `'warn'` in production). */
|
|
20
|
+
level?: LogLevelName;
|
|
21
|
+
/** Remote sink to forward records to. Omit for error-capture only. */
|
|
22
|
+
sink?: HttpSinkOptions;
|
|
23
|
+
/** Capture uncaught errors / unhandled rejections. Default `true`. */
|
|
24
|
+
captureErrors?: boolean;
|
|
25
|
+
/** Options for {@link installErrorCapture} (e.g. an extra `onError` hook). */
|
|
26
|
+
errorCapture?: ErrorCaptureOptions;
|
|
27
|
+
}
|
|
28
|
+
/** Wire up logging level, an optional remote sink, and error capture in one call. */
|
|
29
|
+
export declare function initObservability(opts?: ObservabilityOptions): void;
|
|
30
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAA6B,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEnF,MAAM,WAAW,oBAAoB;IACjC,mEAAmE;IACnE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,sEAAsE;IACtE,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,sEAAsE;IACtE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAIvE"}
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-call setup, à la `Sentry.init()`. Call once in your app entry.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { initObservability } from '@sigx/lynx-observability';
|
|
7
|
+
*
|
|
8
|
+
* initObservability({
|
|
9
|
+
* level: 'warn', // optional: override the default level
|
|
10
|
+
* captureErrors: true, // default: capture uncaught errors
|
|
11
|
+
* sink: { url: 'https://logs.example.com/ingest', headers: { 'x-api-key': KEY } },
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { addTransport, setLogLevel } from '@sigx/lynx-core';
|
|
16
|
+
import { createHttpSink } from './http-sink.js';
|
|
17
|
+
import { installErrorCapture } from './error-capture.js';
|
|
18
|
+
/** Wire up logging level, an optional remote sink, and error capture in one call. */
|
|
19
|
+
export function initObservability(opts = {}) {
|
|
20
|
+
if (opts.level)
|
|
21
|
+
setLogLevel(opts.level);
|
|
22
|
+
if (opts.sink)
|
|
23
|
+
addTransport(createHttpSink(opts.sink));
|
|
24
|
+
if (opts.captureErrors !== false)
|
|
25
|
+
installErrorCapture(opts.errorCapture);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAqB,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAwB,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAA4B,MAAM,oBAAoB,CAAC;AAanF,qFAAqF;AACrF,MAAM,UAAU,iBAAiB,CAAC,IAAI,GAAyB,EAAE;IAC7D,IAAI,IAAI,CAAC,KAAK;QAAE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,IAAI,CAAC,IAAI;QAAE,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,IAAI,IAAI,CAAC,aAAa,KAAK,KAAK;QAAE,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HttpSinkOptions } from './http-sink.js';
|
|
2
|
+
/** Config injected into {@link install} (the `logging.production` shape). */
|
|
3
|
+
export interface InjectedObservabilityConfig {
|
|
4
|
+
sink?: HttpSinkOptions;
|
|
5
|
+
captureErrors?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Apply the injected config: no-op when `null`/`undefined`, otherwise wire up
|
|
9
|
+
* observability. Never throws — observability must not crash the host app.
|
|
10
|
+
* Exported (not just the side effect) so it's unit-testable.
|
|
11
|
+
*/
|
|
12
|
+
export declare function install(cfg: InjectedObservabilityConfig | null | undefined): void;
|
|
13
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,6EAA6E;AAC7E,MAAM,WAAW,2BAA2B;IACxC,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,2BAA2B,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAMjF"}
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-installed by `@sigx/lynx-plugin` in **release** builds when the app's
|
|
3
|
+
* `signalx.config.ts` declares `logging.production`. Prepended to the BG entry
|
|
4
|
+
* so error capture + the remote sink are wired before app code runs.
|
|
5
|
+
*
|
|
6
|
+
* Never import this directly from app code — it has unconditional side effects.
|
|
7
|
+
* To set up observability manually instead, call `initObservability(...)`.
|
|
8
|
+
*/
|
|
9
|
+
import { initObservability } from './init.js';
|
|
10
|
+
/**
|
|
11
|
+
* Apply the injected config: no-op when `null`/`undefined`, otherwise wire up
|
|
12
|
+
* observability. Never throws — observability must not crash the host app.
|
|
13
|
+
* Exported (not just the side effect) so it's unit-testable.
|
|
14
|
+
*/
|
|
15
|
+
export function install(cfg) {
|
|
16
|
+
try {
|
|
17
|
+
if (cfg)
|
|
18
|
+
initObservability({ sink: cfg.sink, captureErrors: cfg.captureErrors });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
/* never let observability wiring crash the host app */
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
install(typeof __SIGX_OBSERVABILITY_CONFIG__ !== 'undefined' ? __SIGX_OBSERVABILITY_CONFIG__ : null);
|
|
25
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAS9C;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,GAAmD;IACvE,IAAI,CAAC;QACD,IAAI,GAAG;YAAE,iBAAiB,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACL,uDAAuD;IAC3D,CAAC;AACL,CAAC;AAMD,OAAO,CAAC,OAAO,6BAA6B,KAAK,WAAW,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sigx/lynx-observability",
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "Opt-in production error capture + provider-agnostic log/error sinks for sigx-lynx",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./install": {
|
|
14
|
+
"import": "./dist/install.js",
|
|
15
|
+
"types": "./dist/install.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"sigx",
|
|
24
|
+
"lynx",
|
|
25
|
+
"logging",
|
|
26
|
+
"observability",
|
|
27
|
+
"error-reporting"
|
|
28
|
+
],
|
|
29
|
+
"author": "Andreas Ekdahl",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/signalxjs/lynx.git",
|
|
34
|
+
"directory": "packages/lynx-observability"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/signalxjs/lynx/tree/main/packages/lynx-observability",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/signalxjs/lynx/issues"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@sigx/lynx-core": "^0.5.2",
|
|
42
|
+
"@sigx/lynx-http": "^0.5.2"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@typescript/native-preview": "7.0.0-dev.20260521.1",
|
|
46
|
+
"typescript": "^6.0.3"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "node ../../scripts/clean.mjs dist && tsgo",
|
|
53
|
+
"dev": "tsgo --watch",
|
|
54
|
+
"clean": "node ../../scripts/clean.mjs dist .turbo"
|
|
55
|
+
}
|
|
56
|
+
}
|