@abbacchio/browser-transport 0.1.4 → 0.2.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.
- package/dist/auto.d.ts +4 -5
- package/dist/auto.d.ts.map +1 -1
- package/dist/auto.js +6 -9
- package/dist/auto.js.map +1 -1
- package/dist/client.d.ts +67 -47
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +290 -77
- package/dist/client.js.map +1 -1
- package/dist/console.d.ts +6 -19
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +27 -75
- package/dist/console.js.map +1 -1
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +106 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +9 -50
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +27 -34
- package/dist/logger.js.map +1 -1
- package/dist/react/AbbacchioProvider.d.ts +5 -42
- package/dist/react/AbbacchioProvider.d.ts.map +1 -1
- package/dist/react/AbbacchioProvider.js +9 -48
- package/dist/react/AbbacchioProvider.js.map +1 -1
- package/package.json +2 -2
package/dist/auto.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auto-initialize console interception
|
|
2
|
+
* Auto-initialize console interception (OTLP native)
|
|
3
3
|
*
|
|
4
4
|
* Simply import this module to start capturing console output:
|
|
5
5
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* ```typescript
|
|
8
8
|
* import '@abbacchio/browser-transport/auto'
|
|
9
9
|
*
|
|
10
|
-
* // All console.log calls now go to Abbacchio
|
|
10
|
+
* // All console.log calls now go to Abbacchio via OTLP
|
|
11
11
|
* console.log('This is captured!')
|
|
12
12
|
* ```
|
|
13
13
|
*
|
|
@@ -17,10 +17,9 @@
|
|
|
17
17
|
* ```html
|
|
18
18
|
* <script>
|
|
19
19
|
* window.__ABBACCHIO_CONFIG__ = {
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* endpoint: 'http://localhost:4002',
|
|
21
|
+
* serviceName: 'my-app',
|
|
22
22
|
* appName: 'my-web-app',
|
|
23
|
-
* secretKey: 'optional-encryption-key',
|
|
24
23
|
* }
|
|
25
24
|
* </script>
|
|
26
25
|
* <script type="module">
|
package/dist/auto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAoB,KAAK,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,oBAAoB,CAAC,EAAE,yBAAyB,CAAC;KAClD;CACF"}
|
package/dist/auto.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auto-initialize console interception
|
|
2
|
+
* Auto-initialize console interception (OTLP native)
|
|
3
3
|
*
|
|
4
4
|
* Simply import this module to start capturing console output:
|
|
5
5
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* ```typescript
|
|
8
8
|
* import '@abbacchio/browser-transport/auto'
|
|
9
9
|
*
|
|
10
|
-
* // All console.log calls now go to Abbacchio
|
|
10
|
+
* // All console.log calls now go to Abbacchio via OTLP
|
|
11
11
|
* console.log('This is captured!')
|
|
12
12
|
* ```
|
|
13
13
|
*
|
|
@@ -17,10 +17,9 @@
|
|
|
17
17
|
* ```html
|
|
18
18
|
* <script>
|
|
19
19
|
* window.__ABBACCHIO_CONFIG__ = {
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* endpoint: 'http://localhost:4002',
|
|
21
|
+
* serviceName: 'my-app',
|
|
22
22
|
* appName: 'my-web-app',
|
|
23
|
-
* secretKey: 'optional-encryption-key',
|
|
24
23
|
* }
|
|
25
24
|
* </script>
|
|
26
25
|
* <script type="module">
|
|
@@ -29,12 +28,10 @@
|
|
|
29
28
|
* ```
|
|
30
29
|
*/
|
|
31
30
|
import { interceptConsole } from './console.js';
|
|
32
|
-
// Get configuration from global variable or use defaults
|
|
33
31
|
const config = (typeof window !== 'undefined' && window.__ABBACCHIO_CONFIG__) || {};
|
|
34
|
-
// Start intercepting with merged config
|
|
35
32
|
interceptConsole({
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
endpoint: 'http://localhost:4002',
|
|
34
|
+
serviceName: 'browser',
|
|
38
35
|
appName: 'auto',
|
|
39
36
|
...config,
|
|
40
37
|
});
|
package/dist/auto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,gBAAgB,EAAkC,MAAM,cAAc,CAAC;AAQhF,MAAM,MAAM,GACV,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;AAEvE,gBAAgB,CAAC;IACf,QAAQ,EAAE,uBAAuB;IACjC,WAAW,EAAE,SAAS;IACtB,OAAO,EAAE,MAAM;IACf,GAAG,MAAM;CACV,CAAC,CAAC"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser HTTP client for Abbacchio
|
|
3
|
-
*
|
|
2
|
+
* Browser HTTP client for Abbacchio (OTLP native)
|
|
3
|
+
* Sends logs to POST /v1/logs, metrics to POST /v1/metrics, traces to POST /v1/traces
|
|
4
4
|
*/
|
|
5
5
|
export interface AbbacchioClientOptions {
|
|
6
|
-
/**
|
|
6
|
+
/** Base URL of the OTLP server (e.g. "http://localhost:4002"). Logs go to {endpoint}/v1/logs */
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
/** @deprecated Use `endpoint` instead */
|
|
7
9
|
url?: string;
|
|
8
|
-
/** Secret key for encryption. If provided,
|
|
10
|
+
/** Secret key for encryption. If provided, payloads will be encrypted before sending */
|
|
9
11
|
secretKey?: string;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
/** Number of
|
|
12
|
+
/** Service name — maps to OTLP resource attribute `service.name`. Defaults to 'default' */
|
|
13
|
+
serviceName?: string;
|
|
14
|
+
/** Additional OTLP resource attributes */
|
|
15
|
+
resourceAttributes?: Record<string, string>;
|
|
16
|
+
/** Number of log records to batch before sending. Defaults to 10 */
|
|
15
17
|
batchSize?: number;
|
|
16
18
|
/** Interval in ms between flushes. Defaults to 1000 */
|
|
17
19
|
flushInterval?: number;
|
|
@@ -28,65 +30,83 @@ export interface LogEntry {
|
|
|
28
30
|
[key: string]: unknown;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
+
* A metric data point to send via OTLP.
|
|
34
|
+
*/
|
|
35
|
+
export interface MetricRecord {
|
|
36
|
+
name: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
unit?: string;
|
|
39
|
+
type: 'sum' | 'gauge';
|
|
40
|
+
value: number;
|
|
41
|
+
attributes?: Record<string, unknown>;
|
|
42
|
+
isMonotonic?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A trace span to send via OTLP.
|
|
46
|
+
*/
|
|
47
|
+
export interface SpanRecord {
|
|
48
|
+
traceId: string;
|
|
49
|
+
spanId: string;
|
|
50
|
+
parentSpanId?: string;
|
|
51
|
+
name: string;
|
|
52
|
+
kind?: number;
|
|
53
|
+
startTimeUnixNano: string;
|
|
54
|
+
endTimeUnixNano: string;
|
|
55
|
+
attributes?: Record<string, unknown>;
|
|
56
|
+
status?: {
|
|
57
|
+
code: number;
|
|
58
|
+
message?: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Browser HTTP client for Abbacchio (OTLP native).
|
|
63
|
+
* Sends logs via POST /v1/logs in ExportLogsServiceRequest format.
|
|
33
64
|
*/
|
|
34
65
|
export declare class AbbacchioClient {
|
|
35
|
-
private
|
|
66
|
+
private endpoint;
|
|
36
67
|
private secretKey?;
|
|
37
|
-
private
|
|
38
|
-
private
|
|
68
|
+
private serviceName;
|
|
69
|
+
private resourceAttributes;
|
|
39
70
|
private batchSize;
|
|
40
71
|
private flushInterval;
|
|
41
72
|
private headers;
|
|
42
73
|
private enabled;
|
|
43
74
|
private buffer;
|
|
75
|
+
private metricBuffer;
|
|
76
|
+
private spanBuffer;
|
|
44
77
|
private timer;
|
|
78
|
+
private metricTimer;
|
|
79
|
+
private spanTimer;
|
|
45
80
|
private isClosed;
|
|
46
81
|
constructor(options?: AbbacchioClientOptions);
|
|
47
|
-
/**
|
|
48
|
-
* Process a log entry: inject default namespace, then encrypt if needed
|
|
49
|
-
*/
|
|
50
|
-
private processLog;
|
|
51
|
-
/**
|
|
52
|
-
* Enable sending logs to the server
|
|
53
|
-
*/
|
|
54
82
|
enable(): void;
|
|
55
|
-
/**
|
|
56
|
-
* Disable sending logs to the server. Logs will be silently dropped.
|
|
57
|
-
*/
|
|
58
83
|
disable(): void;
|
|
59
|
-
/**
|
|
60
|
-
* Check if the client is currently enabled
|
|
61
|
-
*/
|
|
62
84
|
isEnabled(): boolean;
|
|
63
|
-
/**
|
|
64
|
-
* Add a log to the buffer and trigger send if needed
|
|
65
|
-
*/
|
|
85
|
+
/** Add a log to the buffer and trigger send if needed */
|
|
66
86
|
add(log: LogEntry): void;
|
|
67
|
-
/**
|
|
68
|
-
* Schedule a send after the interval
|
|
69
|
-
*/
|
|
70
87
|
private scheduleSend;
|
|
71
|
-
/**
|
|
72
|
-
* Flush the buffer and send to server
|
|
73
|
-
*/
|
|
88
|
+
/** Flush all buffers (logs, metrics, traces) */
|
|
74
89
|
flush(): Promise<void>;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
flushLogs(): Promise<void>;
|
|
91
|
+
private sendLogs;
|
|
92
|
+
/** Add a metric data point to the buffer */
|
|
93
|
+
addMetric(metric: MetricRecord): void;
|
|
94
|
+
private scheduleMetricFlush;
|
|
95
|
+
flushMetrics(): Promise<void>;
|
|
96
|
+
private sendMetrics;
|
|
97
|
+
/** Add a trace span to the buffer */
|
|
98
|
+
addSpan(span: SpanRecord): void;
|
|
99
|
+
private scheduleSpanFlush;
|
|
100
|
+
flushSpans(): Promise<void>;
|
|
101
|
+
private sendSpans;
|
|
82
102
|
close(): Promise<void>;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
private buildResourceAttributes;
|
|
104
|
+
private post;
|
|
105
|
+
/** Update client configuration */
|
|
86
106
|
configure(options: Partial<AbbacchioClientOptions>): void;
|
|
87
107
|
}
|
|
88
108
|
/**
|
|
89
|
-
* Create a new Abbacchio client instance
|
|
109
|
+
* Create a new Abbacchio OTLP client instance
|
|
90
110
|
*/
|
|
91
111
|
export declare function createClient(options?: AbbacchioClientOptions): AbbacchioClient;
|
|
92
112
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoDH,MAAM,WAAW,sBAAsB;IACrC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,OAAO,CAAU;IAEzB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,GAAE,sBAA2B;IA0BhD,MAAM,IAAI,IAAI;IACd,OAAO,IAAI,IAAI;IACf,SAAS,IAAI,OAAO;IAEpB,yDAAyD;IACzD,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI;IAWxB,OAAO,CAAC,YAAY;IAQpB,gDAAgD;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;YAclB,QAAQ;IAqCtB,4CAA4C;IAC5C,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAWrC,OAAO,CAAC,mBAAmB;IAQrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAWrB,WAAW;IAuDzB,qCAAqC;IACrC,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAW/B,OAAO,CAAC,iBAAiB;IAQnB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAWnB,SAAS;IAgCjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB5B,OAAO,CAAC,uBAAuB;YAUjB,IAAI;IAmBlB,kCAAkC;IAClC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAAG,IAAI;CAkB1D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAE9E"}
|
package/dist/client.js
CHANGED
|
@@ -1,21 +1,74 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser HTTP client for Abbacchio
|
|
3
|
-
*
|
|
2
|
+
* Browser HTTP client for Abbacchio (OTLP native)
|
|
3
|
+
* Sends logs to POST /v1/logs, metrics to POST /v1/metrics, traces to POST /v1/traces
|
|
4
4
|
*/
|
|
5
5
|
import { encrypt } from './crypto.js';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
|
|
7
|
+
* OTLP severity numbers per OpenTelemetry spec.
|
|
8
|
+
*/
|
|
9
|
+
const PINO_TO_OTLP_SEVERITY = {
|
|
10
|
+
10: { number: 1, text: 'TRACE' },
|
|
11
|
+
20: { number: 5, text: 'DEBUG' },
|
|
12
|
+
30: { number: 9, text: 'INFO' },
|
|
13
|
+
40: { number: 13, text: 'WARN' },
|
|
14
|
+
50: { number: 17, text: 'ERROR' },
|
|
15
|
+
60: { number: 21, text: 'FATAL' },
|
|
16
|
+
};
|
|
17
|
+
function toOtlpAnyValue(val) {
|
|
18
|
+
if (typeof val === 'string')
|
|
19
|
+
return { stringValue: val };
|
|
20
|
+
if (typeof val === 'number') {
|
|
21
|
+
return Number.isInteger(val) ? { intValue: val } : { doubleValue: val };
|
|
22
|
+
}
|
|
23
|
+
if (typeof val === 'boolean')
|
|
24
|
+
return { boolValue: val };
|
|
25
|
+
if (val === null || val === undefined)
|
|
26
|
+
return { stringValue: '' };
|
|
27
|
+
// For objects/arrays, JSON.stringify
|
|
28
|
+
try {
|
|
29
|
+
return { stringValue: JSON.stringify(val) };
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { stringValue: String(val) };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function toOtlpAttributes(obj) {
|
|
36
|
+
const attrs = [];
|
|
37
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
38
|
+
if (val === undefined)
|
|
39
|
+
continue;
|
|
40
|
+
attrs.push({ key, value: toOtlpAnyValue(val) });
|
|
41
|
+
}
|
|
42
|
+
return attrs;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Browser HTTP client for Abbacchio (OTLP native).
|
|
46
|
+
* Sends logs via POST /v1/logs in ExportLogsServiceRequest format.
|
|
9
47
|
*/
|
|
10
48
|
export class AbbacchioClient {
|
|
11
49
|
constructor(options = {}) {
|
|
12
50
|
this.buffer = [];
|
|
51
|
+
this.metricBuffer = [];
|
|
52
|
+
this.spanBuffer = [];
|
|
13
53
|
this.timer = null;
|
|
54
|
+
this.metricTimer = null;
|
|
55
|
+
this.spanTimer = null;
|
|
14
56
|
this.isClosed = false;
|
|
15
|
-
|
|
57
|
+
if (options.url && !options.endpoint) {
|
|
58
|
+
try {
|
|
59
|
+
const u = new URL(options.url);
|
|
60
|
+
this.endpoint = `${u.protocol}//${u.host}`;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
this.endpoint = options.url;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.endpoint = options.endpoint || 'http://localhost:4002';
|
|
68
|
+
}
|
|
16
69
|
this.secretKey = options.secretKey;
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
70
|
+
this.serviceName = options.serviceName || 'default';
|
|
71
|
+
this.resourceAttributes = options.resourceAttributes || {};
|
|
19
72
|
this.batchSize = options.batchSize || 10;
|
|
20
73
|
this.flushInterval = options.flushInterval || 1000;
|
|
21
74
|
this.headers = options.headers || {};
|
|
@@ -26,40 +79,10 @@ export class AbbacchioClient {
|
|
|
26
79
|
window.addEventListener('pagehide', () => this.flush());
|
|
27
80
|
}
|
|
28
81
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
let processed = log;
|
|
34
|
-
if (this.namespace && !processed.namespace && !processed.name) {
|
|
35
|
-
processed = { ...processed, namespace: this.namespace };
|
|
36
|
-
}
|
|
37
|
-
if (this.secretKey) {
|
|
38
|
-
return { encrypted: await encrypt(JSON.stringify(processed), this.secretKey) };
|
|
39
|
-
}
|
|
40
|
-
return processed;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Enable sending logs to the server
|
|
44
|
-
*/
|
|
45
|
-
enable() {
|
|
46
|
-
this.enabled = true;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Disable sending logs to the server. Logs will be silently dropped.
|
|
50
|
-
*/
|
|
51
|
-
disable() {
|
|
52
|
-
this.enabled = false;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Check if the client is currently enabled
|
|
56
|
-
*/
|
|
57
|
-
isEnabled() {
|
|
58
|
-
return this.enabled;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Add a log to the buffer and trigger send if needed
|
|
62
|
-
*/
|
|
82
|
+
enable() { this.enabled = true; }
|
|
83
|
+
disable() { this.enabled = false; }
|
|
84
|
+
isEnabled() { return this.enabled; }
|
|
85
|
+
/** Add a log to the buffer and trigger send if needed */
|
|
63
86
|
add(log) {
|
|
64
87
|
if (!this.enabled || this.isClosed)
|
|
65
88
|
return;
|
|
@@ -71,9 +94,6 @@ export class AbbacchioClient {
|
|
|
71
94
|
this.scheduleSend();
|
|
72
95
|
}
|
|
73
96
|
}
|
|
74
|
-
/**
|
|
75
|
-
* Schedule a send after the interval
|
|
76
|
-
*/
|
|
77
97
|
scheduleSend() {
|
|
78
98
|
if (this.timer)
|
|
79
99
|
return;
|
|
@@ -82,10 +102,15 @@ export class AbbacchioClient {
|
|
|
82
102
|
this.flush();
|
|
83
103
|
}, this.flushInterval);
|
|
84
104
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Flush the buffer and send to server
|
|
87
|
-
*/
|
|
105
|
+
/** Flush all buffers (logs, metrics, traces) */
|
|
88
106
|
async flush() {
|
|
107
|
+
await Promise.all([
|
|
108
|
+
this.flushLogs(),
|
|
109
|
+
this.flushMetrics(),
|
|
110
|
+
this.flushSpans(),
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
async flushLogs() {
|
|
89
114
|
if (this.buffer.length === 0)
|
|
90
115
|
return;
|
|
91
116
|
const toSend = this.buffer;
|
|
@@ -94,53 +119,241 @@ export class AbbacchioClient {
|
|
|
94
119
|
clearTimeout(this.timer);
|
|
95
120
|
this.timer = null;
|
|
96
121
|
}
|
|
97
|
-
|
|
98
|
-
const processedLogs = await Promise.all(toSend.map(log => this.processLog(log)));
|
|
99
|
-
await this.sendToServer(processedLogs);
|
|
122
|
+
await this.sendLogs(toSend);
|
|
100
123
|
}
|
|
101
|
-
|
|
102
|
-
* Send logs to the Abbacchio server
|
|
103
|
-
*/
|
|
104
|
-
async sendToServer(logs) {
|
|
124
|
+
async sendLogs(logs) {
|
|
105
125
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
const logRecords = logs.map((log) => {
|
|
127
|
+
const { level, msg, message, time, name, ...extra } = log;
|
|
128
|
+
const pinoLevel = typeof level === 'number' ? level : 30;
|
|
129
|
+
const severity = PINO_TO_OTLP_SEVERITY[pinoLevel] || PINO_TO_OTLP_SEVERITY[30];
|
|
130
|
+
const body = msg || message || '';
|
|
131
|
+
const timeNano = String((time || Date.now()) * 1000000);
|
|
132
|
+
return {
|
|
133
|
+
timeUnixNano: timeNano,
|
|
134
|
+
observedTimeUnixNano: timeNano,
|
|
135
|
+
severityNumber: severity.number,
|
|
136
|
+
severityText: severity.text,
|
|
137
|
+
body: { stringValue: String(body) },
|
|
138
|
+
attributes: toOtlpAttributes(extra),
|
|
139
|
+
};
|
|
115
140
|
});
|
|
141
|
+
const payload = {
|
|
142
|
+
resourceLogs: [{
|
|
143
|
+
resource: { attributes: this.buildResourceAttributes() },
|
|
144
|
+
scopeLogs: [{
|
|
145
|
+
scope: { name: '@abbacchio/browser-transport' },
|
|
146
|
+
logRecords,
|
|
147
|
+
}],
|
|
148
|
+
}],
|
|
149
|
+
};
|
|
150
|
+
await this.post('/v1/logs', payload);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Silently fail
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// ─── Metrics ───────────────────────────────────────
|
|
157
|
+
/** Add a metric data point to the buffer */
|
|
158
|
+
addMetric(metric) {
|
|
159
|
+
if (!this.enabled || this.isClosed)
|
|
160
|
+
return;
|
|
161
|
+
this.metricBuffer.push(metric);
|
|
162
|
+
if (this.metricBuffer.length >= this.batchSize) {
|
|
163
|
+
this.flushMetrics();
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.scheduleMetricFlush();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
scheduleMetricFlush() {
|
|
170
|
+
if (this.metricTimer)
|
|
171
|
+
return;
|
|
172
|
+
this.metricTimer = setTimeout(() => {
|
|
173
|
+
this.metricTimer = null;
|
|
174
|
+
this.flushMetrics();
|
|
175
|
+
}, this.flushInterval);
|
|
176
|
+
}
|
|
177
|
+
async flushMetrics() {
|
|
178
|
+
if (this.metricBuffer.length === 0)
|
|
179
|
+
return;
|
|
180
|
+
const batch = this.metricBuffer;
|
|
181
|
+
this.metricBuffer = [];
|
|
182
|
+
if (this.metricTimer) {
|
|
183
|
+
clearTimeout(this.metricTimer);
|
|
184
|
+
this.metricTimer = null;
|
|
185
|
+
}
|
|
186
|
+
await this.sendMetrics(batch);
|
|
187
|
+
}
|
|
188
|
+
async sendMetrics(metrics) {
|
|
189
|
+
try {
|
|
190
|
+
const byName = new Map();
|
|
191
|
+
for (const m of metrics) {
|
|
192
|
+
const arr = byName.get(m.name) || [];
|
|
193
|
+
arr.push(m);
|
|
194
|
+
byName.set(m.name, arr);
|
|
195
|
+
}
|
|
196
|
+
const otlpMetrics = [];
|
|
197
|
+
for (const [name, records] of byName) {
|
|
198
|
+
const first = records[0];
|
|
199
|
+
const dataPoints = records.map((r) => ({
|
|
200
|
+
timeUnixNano: String(Date.now() * 1000000),
|
|
201
|
+
asDouble: r.value,
|
|
202
|
+
attributes: r.attributes ? toOtlpAttributes(r.attributes) : [],
|
|
203
|
+
}));
|
|
204
|
+
const metric = {
|
|
205
|
+
name,
|
|
206
|
+
description: first.description || '',
|
|
207
|
+
unit: first.unit || '',
|
|
208
|
+
};
|
|
209
|
+
if (first.type === 'sum') {
|
|
210
|
+
metric.sum = {
|
|
211
|
+
dataPoints,
|
|
212
|
+
aggregationTemporality: 2,
|
|
213
|
+
isMonotonic: first.isMonotonic ?? true,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
metric.gauge = { dataPoints };
|
|
218
|
+
}
|
|
219
|
+
otlpMetrics.push(metric);
|
|
220
|
+
}
|
|
221
|
+
const payload = {
|
|
222
|
+
resourceMetrics: [{
|
|
223
|
+
resource: { attributes: this.buildResourceAttributes() },
|
|
224
|
+
scopeMetrics: [{
|
|
225
|
+
scope: { name: '@abbacchio/browser-transport' },
|
|
226
|
+
metrics: otlpMetrics,
|
|
227
|
+
}],
|
|
228
|
+
}],
|
|
229
|
+
};
|
|
230
|
+
await this.post('/v1/metrics', payload);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// Silently fail
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// ─── Traces ────────────────────────────────────────
|
|
237
|
+
/** Add a trace span to the buffer */
|
|
238
|
+
addSpan(span) {
|
|
239
|
+
if (!this.enabled || this.isClosed)
|
|
240
|
+
return;
|
|
241
|
+
this.spanBuffer.push(span);
|
|
242
|
+
if (this.spanBuffer.length >= this.batchSize) {
|
|
243
|
+
this.flushSpans();
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
this.scheduleSpanFlush();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
scheduleSpanFlush() {
|
|
250
|
+
if (this.spanTimer)
|
|
251
|
+
return;
|
|
252
|
+
this.spanTimer = setTimeout(() => {
|
|
253
|
+
this.spanTimer = null;
|
|
254
|
+
this.flushSpans();
|
|
255
|
+
}, this.flushInterval);
|
|
256
|
+
}
|
|
257
|
+
async flushSpans() {
|
|
258
|
+
if (this.spanBuffer.length === 0)
|
|
259
|
+
return;
|
|
260
|
+
const batch = this.spanBuffer;
|
|
261
|
+
this.spanBuffer = [];
|
|
262
|
+
if (this.spanTimer) {
|
|
263
|
+
clearTimeout(this.spanTimer);
|
|
264
|
+
this.spanTimer = null;
|
|
265
|
+
}
|
|
266
|
+
await this.sendSpans(batch);
|
|
267
|
+
}
|
|
268
|
+
async sendSpans(spans) {
|
|
269
|
+
try {
|
|
270
|
+
const otlpSpans = spans.map((s) => ({
|
|
271
|
+
traceId: s.traceId,
|
|
272
|
+
spanId: s.spanId,
|
|
273
|
+
parentSpanId: s.parentSpanId,
|
|
274
|
+
name: s.name,
|
|
275
|
+
kind: s.kind ?? 1,
|
|
276
|
+
startTimeUnixNano: s.startTimeUnixNano,
|
|
277
|
+
endTimeUnixNano: s.endTimeUnixNano,
|
|
278
|
+
attributes: s.attributes ? toOtlpAttributes(s.attributes) : [],
|
|
279
|
+
status: s.status,
|
|
280
|
+
}));
|
|
281
|
+
const payload = {
|
|
282
|
+
resourceSpans: [{
|
|
283
|
+
resource: { attributes: this.buildResourceAttributes() },
|
|
284
|
+
scopeSpans: [{
|
|
285
|
+
scope: { name: '@abbacchio/browser-transport' },
|
|
286
|
+
spans: otlpSpans,
|
|
287
|
+
}],
|
|
288
|
+
}],
|
|
289
|
+
};
|
|
290
|
+
await this.post('/v1/traces', payload);
|
|
116
291
|
}
|
|
117
292
|
catch {
|
|
118
|
-
// Silently fail
|
|
293
|
+
// Silently fail
|
|
119
294
|
}
|
|
120
295
|
}
|
|
121
|
-
|
|
122
|
-
* Close the client and flush any remaining logs
|
|
123
|
-
*/
|
|
296
|
+
// ─── Shared ────────────────────────────────────────
|
|
124
297
|
async close() {
|
|
125
298
|
this.isClosed = true;
|
|
126
299
|
if (this.timer) {
|
|
127
300
|
clearTimeout(this.timer);
|
|
128
301
|
this.timer = null;
|
|
129
302
|
}
|
|
303
|
+
if (this.metricTimer) {
|
|
304
|
+
clearTimeout(this.metricTimer);
|
|
305
|
+
this.metricTimer = null;
|
|
306
|
+
}
|
|
307
|
+
if (this.spanTimer) {
|
|
308
|
+
clearTimeout(this.spanTimer);
|
|
309
|
+
this.spanTimer = null;
|
|
310
|
+
}
|
|
130
311
|
await this.flush();
|
|
131
312
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
313
|
+
buildResourceAttributes() {
|
|
314
|
+
const attrs = [
|
|
315
|
+
{ key: 'service.name', value: { stringValue: this.serviceName } },
|
|
316
|
+
];
|
|
317
|
+
for (const [key, val] of Object.entries(this.resourceAttributes)) {
|
|
318
|
+
attrs.push({ key, value: { stringValue: val } });
|
|
319
|
+
}
|
|
320
|
+
return attrs;
|
|
321
|
+
}
|
|
322
|
+
async post(path, payload) {
|
|
323
|
+
let body = JSON.stringify(payload);
|
|
324
|
+
const headers = {
|
|
325
|
+
'Content-Type': 'application/json',
|
|
326
|
+
...this.headers,
|
|
327
|
+
};
|
|
328
|
+
if (this.secretKey) {
|
|
329
|
+
body = await encrypt(JSON.stringify(payload), this.secretKey);
|
|
330
|
+
headers['X-Encrypted'] = 'true';
|
|
331
|
+
}
|
|
332
|
+
await fetch(`${this.endpoint}${path}`, {
|
|
333
|
+
method: 'POST',
|
|
334
|
+
headers,
|
|
335
|
+
body,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
/** Update client configuration */
|
|
135
339
|
configure(options) {
|
|
136
|
-
if (options.
|
|
137
|
-
this.
|
|
340
|
+
if (options.endpoint !== undefined)
|
|
341
|
+
this.endpoint = options.endpoint;
|
|
342
|
+
if (options.url !== undefined && !options.endpoint) {
|
|
343
|
+
try {
|
|
344
|
+
const u = new URL(options.url);
|
|
345
|
+
this.endpoint = `${u.protocol}//${u.host}`;
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
this.endpoint = options.url;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
138
351
|
if (options.secretKey !== undefined)
|
|
139
352
|
this.secretKey = options.secretKey;
|
|
140
|
-
if (options.
|
|
141
|
-
this.
|
|
142
|
-
if (options.
|
|
143
|
-
this.
|
|
353
|
+
if (options.serviceName !== undefined)
|
|
354
|
+
this.serviceName = options.serviceName;
|
|
355
|
+
if (options.resourceAttributes !== undefined)
|
|
356
|
+
this.resourceAttributes = options.resourceAttributes;
|
|
144
357
|
if (options.enabled !== undefined)
|
|
145
358
|
this.enabled = options.enabled;
|
|
146
359
|
if (options.batchSize !== undefined)
|
|
@@ -152,7 +365,7 @@ export class AbbacchioClient {
|
|
|
152
365
|
}
|
|
153
366
|
}
|
|
154
367
|
/**
|
|
155
|
-
* Create a new Abbacchio client instance
|
|
368
|
+
* Create a new Abbacchio OTLP client instance
|
|
156
369
|
*/
|
|
157
370
|
export function createClient(options) {
|
|
158
371
|
return new AbbacchioClient(options);
|