@foam-ai/node 0.1.0-alpha.1 → 0.1.0-alpha.11
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/README.md +656 -0
- package/dist/before-send.d.ts +27 -0
- package/dist/before-send.js +46 -0
- package/dist/constants.d.ts +44 -0
- package/dist/constants.js +67 -0
- package/dist/endpoint.d.ts +2 -0
- package/dist/endpoint.js +11 -0
- package/dist/exporters.d.ts +34 -0
- package/dist/exporters.js +208 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +28 -0
- package/dist/ingest.d.ts +12 -0
- package/dist/ingest.js +70 -0
- package/dist/init.d.ts +26 -0
- package/dist/init.js +171 -0
- package/dist/instrumentations.d.ts +4 -0
- package/dist/instrumentations.js +120 -0
- package/dist/library-versions.d.ts +1 -0
- package/dist/library-versions.js +59 -0
- package/dist/logs.d.ts +10 -0
- package/dist/logs.js +61 -0
- package/dist/metrics.d.ts +5 -0
- package/dist/metrics.js +29 -0
- package/dist/network-capture/collector.d.ts +27 -0
- package/dist/network-capture/collector.js +411 -0
- package/dist/network-capture/http.d.ts +5 -0
- package/dist/network-capture/http.js +221 -0
- package/dist/network-capture/index.d.ts +3 -0
- package/dist/network-capture/index.js +20 -0
- package/dist/network-capture/redact.d.ts +9 -0
- package/dist/network-capture/redact.js +401 -0
- package/dist/network-capture/support.d.ts +2 -0
- package/dist/network-capture/support.js +38 -0
- package/dist/network-capture/undici.d.ts +5 -0
- package/dist/network-capture/undici.js +177 -0
- package/dist/otlp.d.ts +8 -0
- package/dist/otlp.js +46 -0
- package/dist/propagation.d.ts +5 -0
- package/dist/propagation.js +47 -0
- package/dist/redaction-keys.d.ts +4 -0
- package/dist/redaction-keys.js +480 -0
- package/dist/redaction.d.ts +24 -0
- package/dist/redaction.js +281 -0
- package/dist/report.d.ts +9 -0
- package/dist/report.js +56 -0
- package/dist/resource.d.ts +3 -0
- package/dist/resource.js +24 -0
- package/dist/state.d.ts +26 -0
- package/dist/state.js +61 -0
- package/dist/traces.d.ts +1 -0
- package/dist/traces.js +21 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +20 -0
- package/package.json +47 -19
- package/dist/node/src/capture-exception.d.ts +0 -9
- package/dist/node/src/capture-exception.js +0 -31
- package/dist/node/src/index.d.ts +0 -9
- package/dist/node/src/index.js +0 -12
- package/dist/node/src/init.d.ts +0 -27
- package/dist/node/src/init.js +0 -203
- package/dist/shared/constants.d.ts +0 -1
- package/dist/shared/constants.js +0 -4
- package/dist/shared/util.d.ts +0 -9
- package/dist/shared/util.js +0 -21
package/dist/init.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.init = init;
|
|
4
|
+
const api_1 = require("@opentelemetry/api");
|
|
5
|
+
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
6
|
+
const context_async_hooks_1 = require("@opentelemetry/context-async-hooks");
|
|
7
|
+
const core_1 = require("@opentelemetry/core");
|
|
8
|
+
const instrumentation_1 = require("@opentelemetry/instrumentation");
|
|
9
|
+
const resource_js_1 = require("./resource.js");
|
|
10
|
+
const sdk_logs_1 = require("@opentelemetry/sdk-logs");
|
|
11
|
+
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
|
12
|
+
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
|
|
13
|
+
const report_js_1 = require("./report.js");
|
|
14
|
+
const exporters_js_1 = require("./exporters.js");
|
|
15
|
+
const instrumentations_js_1 = require("./instrumentations.js");
|
|
16
|
+
const before_send_js_1 = require("./before-send.js");
|
|
17
|
+
const logs_js_1 = require("./logs.js");
|
|
18
|
+
const redaction_js_1 = require("./redaction.js");
|
|
19
|
+
const state_js_1 = require("./state.js");
|
|
20
|
+
// TODO(pcga11): conflicts handling pending.
|
|
21
|
+
function init(options) {
|
|
22
|
+
const { sampleRate = 1, disableLogSending = false, networkCapture = "basic", enabled = true, } = options;
|
|
23
|
+
const redactionConfig = (0, redaction_js_1.resolveRedactionConfig)(options.redact);
|
|
24
|
+
const beforeSend = (0, before_send_js_1.resolveBeforeSend)(options.beforeSend);
|
|
25
|
+
// pcga11: Set params before any early return so every state is reported, including failed or disabled attempts.
|
|
26
|
+
(0, state_js_1.setParams)({ name: options.name, environment: options.environment });
|
|
27
|
+
// pcga11: We keep this option because companies often want to turn the integration off in specific envs
|
|
28
|
+
if (!enabled) {
|
|
29
|
+
(0, report_js_1.report)({
|
|
30
|
+
token: options.token,
|
|
31
|
+
severity: api_logs_1.SeverityNumber.INFO,
|
|
32
|
+
message: 'SDK disabled',
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!options.token) {
|
|
37
|
+
// pcga11: Nothing can be sent without a token; report() still logs locally so this is not silent.
|
|
38
|
+
(0, report_js_1.report)({
|
|
39
|
+
severity: api_logs_1.SeverityNumber.ERROR,
|
|
40
|
+
message: 'Parameter token not provided',
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// pcga11: Re-initializing could register duplicate providers/instrumentations, so we track the initialization state and bail out on repeat calls
|
|
45
|
+
if ((0, state_js_1.getInitialized)()) {
|
|
46
|
+
(0, report_js_1.report)({
|
|
47
|
+
token: options.token,
|
|
48
|
+
severity: api_logs_1.SeverityNumber.WARN,
|
|
49
|
+
message: 'SDK already initialized',
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!options.name?.trim() || !options.environment?.trim()) {
|
|
54
|
+
(0, report_js_1.report)({
|
|
55
|
+
token: options.token,
|
|
56
|
+
severity: api_logs_1.SeverityNumber.ERROR,
|
|
57
|
+
message: 'Parameters name and environment not provided',
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (!Number.isFinite(sampleRate) || sampleRate < 0 || sampleRate > 1) {
|
|
62
|
+
(0, report_js_1.report)({
|
|
63
|
+
token: options.token,
|
|
64
|
+
severity: api_logs_1.SeverityNumber.ERROR,
|
|
65
|
+
message: 'Parameter sampleRate must be a finite number between 0 and 1',
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// pcga11: Hoisted so the catch block can shut down providers whose batch/periodic
|
|
70
|
+
// export timers started in their constructors, even when init fails midway.
|
|
71
|
+
let tracerProvider;
|
|
72
|
+
let meterProvider;
|
|
73
|
+
try {
|
|
74
|
+
(0, redaction_js_1.setActiveRedactionConfig)(redactionConfig);
|
|
75
|
+
const resource = (0, resource_js_1.createFoamResource)(options.name, options.environment, options.version, options.additionalResourceAttributes);
|
|
76
|
+
// pcga11: The context manager is what makes the "current" context (active span, baggage)
|
|
77
|
+
// follow execution across await/callback boundaries, via Node's AsyncLocalStorage. Without
|
|
78
|
+
// one registered, spans would not nest and trace/log correlation would break. Registration
|
|
79
|
+
// is first-wins: if another SDK already installed one, we keep theirs and disable ours.
|
|
80
|
+
const contextManager = new context_async_hooks_1.AsyncLocalStorageContextManager();
|
|
81
|
+
contextManager.enable();
|
|
82
|
+
if (!api_1.context.setGlobalContextManager(contextManager)) {
|
|
83
|
+
contextManager.disable();
|
|
84
|
+
}
|
|
85
|
+
(0, state_js_1.setSignal)(state_js_1.Signals.baggage, api_1.propagation.setGlobalPropagator(new core_1.CompositePropagator({
|
|
86
|
+
propagators: [
|
|
87
|
+
new core_1.W3CTraceContextPropagator(),
|
|
88
|
+
new core_1.W3CBaggagePropagator(),
|
|
89
|
+
],
|
|
90
|
+
})) ? state_js_1.SignalSources.global : state_js_1.SignalSources.none);
|
|
91
|
+
// pcga11: The @opentelemetry/api setters return false when another SDK already owns a slot (registration is first-wins). So we try to register our own and if it fails or already taken, nothing happens.
|
|
92
|
+
// TODO(pcga11): Implement a way to handle piggy backing the context and propagation from another SDK.
|
|
93
|
+
tracerProvider = new sdk_trace_base_1.BasicTracerProvider({
|
|
94
|
+
resource,
|
|
95
|
+
sampler: new sdk_trace_base_1.ParentBasedSampler({
|
|
96
|
+
root: new sdk_trace_base_1.TraceIdRatioBasedSampler(sampleRate),
|
|
97
|
+
}),
|
|
98
|
+
spanProcessors: [
|
|
99
|
+
new sdk_trace_base_1.BatchSpanProcessor(new exporters_js_1.FoamTraceExporter(options.token, undefined, {
|
|
100
|
+
redaction: redactionConfig,
|
|
101
|
+
beforeSend,
|
|
102
|
+
})),
|
|
103
|
+
...(options.additionalSpanProcessors ?? []),
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
meterProvider = new sdk_metrics_1.MeterProvider({
|
|
107
|
+
resource,
|
|
108
|
+
readers: [
|
|
109
|
+
new sdk_metrics_1.PeriodicExportingMetricReader({
|
|
110
|
+
exporter: new exporters_js_1.FoamMetricExporter(options.token, undefined, {
|
|
111
|
+
redaction: redactionConfig,
|
|
112
|
+
}),
|
|
113
|
+
}),
|
|
114
|
+
...(options.additionalMetricReaders ?? []),
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
const loggerProvider = new sdk_logs_1.LoggerProvider({
|
|
118
|
+
resource,
|
|
119
|
+
processors: [
|
|
120
|
+
new sdk_logs_1.BatchLogRecordProcessor({
|
|
121
|
+
exporter: new exporters_js_1.FoamLogExporter(options.token, undefined, {
|
|
122
|
+
redaction: redactionConfig,
|
|
123
|
+
beforeSend,
|
|
124
|
+
}),
|
|
125
|
+
}),
|
|
126
|
+
...(options.additionalLogRecordProcessors ?? []),
|
|
127
|
+
],
|
|
128
|
+
});
|
|
129
|
+
(0, logs_js_1.setFoamLoggerProvider)(loggerProvider);
|
|
130
|
+
// pcga11: trace/metrics/propagation setters return false when the slot is taken by another SDK. The logs setter returns whichever provider ended up registered. We use that to record the source: "global" when Foam owns the slot, "none" when another SDK does. Logs fall back to "local" because the Foam-only provider behind log() exists either way.
|
|
131
|
+
(0, state_js_1.setSignal)(state_js_1.Signals.traces, api_1.trace.setGlobalTracerProvider(tracerProvider) ? state_js_1.SignalSources.global : state_js_1.SignalSources.none);
|
|
132
|
+
(0, state_js_1.setSignal)(state_js_1.Signals.metrics, api_1.metrics.setGlobalMeterProvider(meterProvider) ? state_js_1.SignalSources.global : state_js_1.SignalSources.none);
|
|
133
|
+
(0, state_js_1.setSignal)(state_js_1.Signals.logs, api_logs_1.logs.setGlobalLoggerProvider(loggerProvider) === loggerProvider ? state_js_1.SignalSources.global : state_js_1.SignalSources.local);
|
|
134
|
+
const instrumentations = (0, instrumentations_js_1.buildInstrumentations)(networkCapture, disableLogSending, options.ignoredOutboundHosts, options.additionalInstrumentations ?? []);
|
|
135
|
+
// pcga11: registerInstrumentations turns on instrumentation "hooks". OpenTelemetry wraps supported libraries when the app imports them.
|
|
136
|
+
(0, instrumentation_1.registerInstrumentations)({ instrumentations });
|
|
137
|
+
(0, state_js_1.setInstrumentations)(instrumentations.map(instrumentations_js_1.instrumentationLabel));
|
|
138
|
+
process.once("beforeExit", () => {
|
|
139
|
+
void Promise.all([
|
|
140
|
+
(0, state_js_1.getSignal)(state_js_1.Signals.traces) === state_js_1.SignalSources.global ? tracerProvider?.shutdown() : undefined,
|
|
141
|
+
(0, state_js_1.getSignal)(state_js_1.Signals.metrics) === state_js_1.SignalSources.global ? meterProvider?.shutdown() : undefined,
|
|
142
|
+
loggerProvider.shutdown(),
|
|
143
|
+
]).catch(() => undefined);
|
|
144
|
+
});
|
|
145
|
+
(0, state_js_1.setInitialized)(true);
|
|
146
|
+
(0, report_js_1.report)({
|
|
147
|
+
token: options.token,
|
|
148
|
+
severity: api_logs_1.SeverityNumber.INFO,
|
|
149
|
+
message: 'SDK initialized',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
154
|
+
for (const signal of [state_js_1.Signals.traces, state_js_1.Signals.metrics, state_js_1.Signals.logs, state_js_1.Signals.baggage]) {
|
|
155
|
+
(0, state_js_1.setSignal)(signal, state_js_1.SignalSources.none);
|
|
156
|
+
}
|
|
157
|
+
void Promise.all([
|
|
158
|
+
tracerProvider?.shutdown(),
|
|
159
|
+
meterProvider?.shutdown(),
|
|
160
|
+
(0, logs_js_1.getFoamLoggerProvider)()?.shutdown(),
|
|
161
|
+
]).catch(() => undefined);
|
|
162
|
+
(0, logs_js_1.clearFoamLoggerProvider)();
|
|
163
|
+
(0, state_js_1.setInitialized)(false);
|
|
164
|
+
(0, report_js_1.report)({
|
|
165
|
+
token: options.token,
|
|
166
|
+
severity: api_logs_1.SeverityNumber.ERROR,
|
|
167
|
+
message: `SDK initialization failed: ${errorMessage}`,
|
|
168
|
+
error: errorMessage,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Instrumentation } from "@opentelemetry/instrumentation";
|
|
2
|
+
export declare function buildInstrumentations(networkCapture?: "off" | "basic" | "advanced", disableLogSending?: boolean, ignoredOutboundHosts?: readonly string[], additionalInstrumentations?: readonly Instrumentation[]): Instrumentation[];
|
|
3
|
+
export declare const instrumentationName: (instrumentation: Instrumentation) => string;
|
|
4
|
+
export declare const instrumentationLabel: (instrumentation: Instrumentation) => string;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instrumentationLabel = exports.instrumentationName = void 0;
|
|
4
|
+
exports.buildInstrumentations = buildInstrumentations;
|
|
5
|
+
const auto_instrumentations_node_1 = require("@opentelemetry/auto-instrumentations-node");
|
|
6
|
+
const instrumentation_console_1 = require("@opentelemetry/instrumentation-console");
|
|
7
|
+
const index_js_1 = require("./network-capture/index.js");
|
|
8
|
+
const endpoint_js_1 = require("./endpoint.js");
|
|
9
|
+
const library_versions_js_1 = require("./library-versions.js");
|
|
10
|
+
const state_js_1 = require("./state.js");
|
|
11
|
+
const constants_js_1 = require("./constants.js");
|
|
12
|
+
function buildInstrumentations(networkCapture = "basic", disableLogSending = false, ignoredOutboundHosts, additionalInstrumentations) {
|
|
13
|
+
// pcga11: Captured bodies are emitted through the global logs pipeline. Only enable
|
|
14
|
+
// capture when that pipeline delivers to Foam ("global"/"ingest").
|
|
15
|
+
// If another SDK owns the slot (aka we have "local" for logs), capturing would be done for other vendors instead of Foam which might be non-compliant.
|
|
16
|
+
// If it's "none", capturing does not work at all.
|
|
17
|
+
const logsSource = (0, state_js_1.getSignal)(state_js_1.Signals.logs);
|
|
18
|
+
const bodyCaptureEnabled = logsSource === state_js_1.SignalSources.global || logsSource === state_js_1.SignalSources.ingest;
|
|
19
|
+
const capturedHeaders = networkCapture === "off" ? undefined : [...constants_js_1.SAFE_NETWORK_HEADERS];
|
|
20
|
+
// pcga11: Skip Foam's OTLP export HTTP calls so they do not become client spans
|
|
21
|
+
// TODO(pcga11): This could get real interesting: could we use these calls to verify coverage? IDK maybe tarpid
|
|
22
|
+
const ignoredHosts = new Set([
|
|
23
|
+
new URL(endpoint_js_1.endpoint).hostname,
|
|
24
|
+
...(ignoredOutboundHosts ?? []),
|
|
25
|
+
]);
|
|
26
|
+
const ignoreHttpOutgoingRequest = ((request) => typeof request.hostname === "string" &&
|
|
27
|
+
ignoredHosts.has(request.hostname));
|
|
28
|
+
const ignoreUndiciRequest = ((request) => {
|
|
29
|
+
try {
|
|
30
|
+
return ignoredHosts.has(new URL(request.origin).hostname);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// When the runtime's http/undici falls outside what the OTel instrumentation
|
|
37
|
+
// supports, the instrumentation is dropped entirely (enabled: false below
|
|
38
|
+
// makes getNodeAutoInstrumentations skip it), so the state reports only
|
|
39
|
+
// instrumentations that can actually work. Foam's capture hooks are also
|
|
40
|
+
// skipped so nothing patches objects or subscribes to diagnostics channels
|
|
41
|
+
// that would never fire.
|
|
42
|
+
const httpCaptureSupported = (0, index_js_1.isHttpCaptureSupported)();
|
|
43
|
+
const undiciCaptureSupported = (0, index_js_1.isUndiciCaptureSupported)();
|
|
44
|
+
const bodyHooks = networkCapture === "advanced" && httpCaptureSupported
|
|
45
|
+
? (0, index_js_1.createNetworkCaptureHooks)(bodyCaptureEnabled)
|
|
46
|
+
: undefined;
|
|
47
|
+
const advancedUndiciHooks = networkCapture === "advanced" && undiciCaptureSupported
|
|
48
|
+
? (0, index_js_1.createUndiciNetworkCaptureHooks)(bodyCaptureEnabled)
|
|
49
|
+
: undefined;
|
|
50
|
+
const automatic = (0, auto_instrumentations_node_1.getNodeAutoInstrumentations)({
|
|
51
|
+
"@opentelemetry/instrumentation-bunyan": {
|
|
52
|
+
disableLogSending: disableLogSending === true,
|
|
53
|
+
},
|
|
54
|
+
"@opentelemetry/instrumentation-http": {
|
|
55
|
+
enabled: httpCaptureSupported,
|
|
56
|
+
...(capturedHeaders
|
|
57
|
+
? {
|
|
58
|
+
headersToSpanAttributes: {
|
|
59
|
+
client: {
|
|
60
|
+
requestHeaders: capturedHeaders,
|
|
61
|
+
responseHeaders: capturedHeaders,
|
|
62
|
+
},
|
|
63
|
+
server: {
|
|
64
|
+
requestHeaders: capturedHeaders,
|
|
65
|
+
responseHeaders: capturedHeaders,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
: {}),
|
|
70
|
+
...(bodyHooks ?? {}),
|
|
71
|
+
ignoreOutgoingRequestHook: ignoreHttpOutgoingRequest,
|
|
72
|
+
},
|
|
73
|
+
"@opentelemetry/instrumentation-openai": {
|
|
74
|
+
captureMessageContent: networkCapture === "advanced",
|
|
75
|
+
},
|
|
76
|
+
"@opentelemetry/instrumentation-pino": {
|
|
77
|
+
disableLogSending: disableLogSending === true,
|
|
78
|
+
},
|
|
79
|
+
"@opentelemetry/instrumentation-undici": {
|
|
80
|
+
enabled: undiciCaptureSupported,
|
|
81
|
+
...(capturedHeaders
|
|
82
|
+
? {
|
|
83
|
+
headersToSpanAttributes: {
|
|
84
|
+
requestHeaders: capturedHeaders,
|
|
85
|
+
responseHeaders: capturedHeaders,
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
: {}),
|
|
89
|
+
...(advancedUndiciHooks ?? {}),
|
|
90
|
+
ignoreRequestHook: ignoreUndiciRequest,
|
|
91
|
+
},
|
|
92
|
+
"@opentelemetry/instrumentation-winston": {
|
|
93
|
+
disableLogSending: disableLogSending === true,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const instrumentations = [
|
|
97
|
+
...automatic,
|
|
98
|
+
// pcga11: Ignores disableLogSending: console has no in-process transports to double-ship through.
|
|
99
|
+
new instrumentation_console_1.ConsoleInstrumentation(),
|
|
100
|
+
...(additionalInstrumentations ?? []),
|
|
101
|
+
];
|
|
102
|
+
// pcga11: Last entry per name wins: user entries win over our defaults, so on a name
|
|
103
|
+
// collision the map holds our instance as they come first in the list and we disable it.
|
|
104
|
+
const unique = new Map();
|
|
105
|
+
for (const entry of instrumentations) {
|
|
106
|
+
unique.get(entry.instrumentationName)?.disable();
|
|
107
|
+
unique.set(entry.instrumentationName, entry);
|
|
108
|
+
}
|
|
109
|
+
return [...unique.values()];
|
|
110
|
+
}
|
|
111
|
+
const instrumentationName = (instrumentation) => instrumentation.instrumentationName.replace(/^@opentelemetry\/instrumentation-/, "");
|
|
112
|
+
exports.instrumentationName = instrumentationName;
|
|
113
|
+
// State label: "pg@8.11.3" when the target library's version is detectable,
|
|
114
|
+
// otherwise just the short name.
|
|
115
|
+
const instrumentationLabel = (instrumentation) => {
|
|
116
|
+
const name = (0, exports.instrumentationName)(instrumentation);
|
|
117
|
+
const version = (0, library_versions_js_1.instrumentedLibraryVersion)(name);
|
|
118
|
+
return version === undefined ? name : `${name}@${version}`;
|
|
119
|
+
};
|
|
120
|
+
exports.instrumentationLabel = instrumentationLabel;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function instrumentedLibraryVersion(shortName: string): string | undefined;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instrumentedLibraryVersion = instrumentedLibraryVersion;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_module_1 = require("node:module");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const utils_js_1 = require("./utils.js");
|
|
8
|
+
// Resolves the version of the library an instrumentation targets, so the
|
|
9
|
+
// reported state shows what is actually installed (e.g. "pg@8.11.3"), not
|
|
10
|
+
// just that an instrumentation was registered.
|
|
11
|
+
// Instrumentations of Node built-ins or the runtime itself: the version that
|
|
12
|
+
// matters is the runtime's.
|
|
13
|
+
const RUNTIME_TARGETS = {
|
|
14
|
+
http: process.versions.node,
|
|
15
|
+
dns: process.versions.node,
|
|
16
|
+
net: process.versions.node,
|
|
17
|
+
console: process.versions.node,
|
|
18
|
+
"runtime-node": process.versions.node,
|
|
19
|
+
undici: process.versions.undici,
|
|
20
|
+
"aws-lambda": undefined, // targets the Lambda environment, not a package
|
|
21
|
+
};
|
|
22
|
+
// Instrumentation short names whose target package is named differently.
|
|
23
|
+
const PACKAGE_NAMES = {
|
|
24
|
+
cucumber: "@cucumber/cucumber",
|
|
25
|
+
grpc: "@grpc/grpc-js",
|
|
26
|
+
hapi: "@hapi/hapi",
|
|
27
|
+
"nestjs-core": "@nestjs/core",
|
|
28
|
+
};
|
|
29
|
+
// Resolves the package entry point and walks up to its manifest; resolving
|
|
30
|
+
// "<name>/package.json" directly fails for packages whose "exports" field
|
|
31
|
+
// does not expose it (undici, for example).
|
|
32
|
+
function installedPackageVersion(name) {
|
|
33
|
+
return (0, utils_js_1.safely)(() => {
|
|
34
|
+
const require = (0, node_module_1.createRequire)(__filename);
|
|
35
|
+
let directory = (0, node_path_1.dirname)(require.resolve(name));
|
|
36
|
+
for (;;) {
|
|
37
|
+
const manifest = (0, utils_js_1.safely)(() => {
|
|
38
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(directory, "package.json"), "utf8"));
|
|
39
|
+
return parsed;
|
|
40
|
+
}, undefined);
|
|
41
|
+
if (manifest?.name === name && typeof manifest.version === "string") {
|
|
42
|
+
return manifest.version;
|
|
43
|
+
}
|
|
44
|
+
const parent = (0, node_path_1.dirname)(directory);
|
|
45
|
+
if (parent === directory)
|
|
46
|
+
return undefined;
|
|
47
|
+
directory = parent;
|
|
48
|
+
}
|
|
49
|
+
}, undefined);
|
|
50
|
+
}
|
|
51
|
+
// Takes the instrumentation short name ("pg", "http", ...) as produced by
|
|
52
|
+
// instrumentationName(); returns undefined when no version is determinable.
|
|
53
|
+
function instrumentedLibraryVersion(shortName) {
|
|
54
|
+
// Own-property check: `in` would match inherited keys like "toString".
|
|
55
|
+
if (Object.prototype.hasOwnProperty.call(RUNTIME_TARGETS, shortName)) {
|
|
56
|
+
return RUNTIME_TARGETS[shortName];
|
|
57
|
+
}
|
|
58
|
+
return installedPackageVersion(PACKAGE_NAMES[shortName] ?? shortName);
|
|
59
|
+
}
|
package/dist/logs.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Attributes } from "@opentelemetry/api";
|
|
2
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
3
|
+
import { LoggerProvider } from "@opentelemetry/sdk-logs";
|
|
4
|
+
import { type FoamExporterOptions } from "./exporters.js";
|
|
5
|
+
export declare function setFoamLoggerProvider(provider: LoggerProvider): void;
|
|
6
|
+
export declare function getFoamLoggerProvider(): LoggerProvider | undefined;
|
|
7
|
+
export declare function clearFoamLoggerProvider(): void;
|
|
8
|
+
export declare function ensureFoamLoggerProvider(name: string, environment: string, token: string, stamp?: Attributes, options?: FoamExporterOptions): LoggerProvider;
|
|
9
|
+
export declare const log: (body: string, severityNumber?: SeverityNumber, attributes?: Attributes) => void;
|
|
10
|
+
export { SeverityNumber };
|
package/dist/logs.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SeverityNumber = exports.log = void 0;
|
|
4
|
+
exports.setFoamLoggerProvider = setFoamLoggerProvider;
|
|
5
|
+
exports.getFoamLoggerProvider = getFoamLoggerProvider;
|
|
6
|
+
exports.clearFoamLoggerProvider = clearFoamLoggerProvider;
|
|
7
|
+
exports.ensureFoamLoggerProvider = ensureFoamLoggerProvider;
|
|
8
|
+
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
9
|
+
Object.defineProperty(exports, "SeverityNumber", { enumerable: true, get: function () { return api_logs_1.SeverityNumber; } });
|
|
10
|
+
const sdk_logs_1 = require("@opentelemetry/sdk-logs");
|
|
11
|
+
const constants_js_1 = require("./constants.js");
|
|
12
|
+
const exporters_js_1 = require("./exporters.js");
|
|
13
|
+
const resource_js_1 = require("./resource.js");
|
|
14
|
+
const utils_js_1 = require("./utils.js");
|
|
15
|
+
let foamLoggerProvider;
|
|
16
|
+
function setFoamLoggerProvider(provider) {
|
|
17
|
+
if (foamLoggerProvider && foamLoggerProvider !== provider) {
|
|
18
|
+
void foamLoggerProvider.shutdown().catch(() => undefined);
|
|
19
|
+
}
|
|
20
|
+
foamLoggerProvider = provider;
|
|
21
|
+
}
|
|
22
|
+
function getFoamLoggerProvider() {
|
|
23
|
+
return foamLoggerProvider;
|
|
24
|
+
}
|
|
25
|
+
function clearFoamLoggerProvider() {
|
|
26
|
+
foamLoggerProvider = undefined;
|
|
27
|
+
}
|
|
28
|
+
function ensureFoamLoggerProvider(name, environment, token, stamp, options) {
|
|
29
|
+
const existing = foamLoggerProvider;
|
|
30
|
+
if (existing) {
|
|
31
|
+
return existing;
|
|
32
|
+
}
|
|
33
|
+
const provider = new sdk_logs_1.LoggerProvider({
|
|
34
|
+
resource: (0, resource_js_1.createFoamResource)(name, environment),
|
|
35
|
+
processors: [
|
|
36
|
+
new sdk_logs_1.BatchLogRecordProcessor({
|
|
37
|
+
exporter: new exporters_js_1.FoamLogExporter(token, stamp, options),
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
setFoamLoggerProvider(provider);
|
|
42
|
+
process.once("beforeExit", () => {
|
|
43
|
+
void provider.shutdown().catch(() => undefined);
|
|
44
|
+
});
|
|
45
|
+
return provider;
|
|
46
|
+
}
|
|
47
|
+
const log = (body, severityNumber = api_logs_1.SeverityNumber.INFO, attributes) => {
|
|
48
|
+
const provider = foamLoggerProvider;
|
|
49
|
+
if (!provider) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
(0, utils_js_1.safely)(() => {
|
|
53
|
+
provider.getLogger(constants_js_1.FOAM_DISTRO_NAME, constants_js_1.FOAM_DISTRO_VERSION).emit({
|
|
54
|
+
body,
|
|
55
|
+
severityNumber,
|
|
56
|
+
severityText: constants_js_1.SEVERITY_TEXT[severityNumber],
|
|
57
|
+
attributes,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
exports.log = log;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Attributes, type MetricOptions } from "@opentelemetry/api";
|
|
2
|
+
export declare const incrementCounter: (name: string, n?: number | undefined, attributes?: Attributes | undefined, options?: MetricOptions | undefined) => void;
|
|
3
|
+
export declare const recordHistogram: (name: string, value: number, attributes?: Attributes | undefined, options?: MetricOptions | undefined) => void;
|
|
4
|
+
export declare const addUpDownCounter: (name: string, n: number, attributes?: Attributes | undefined, options?: MetricOptions | undefined) => void;
|
|
5
|
+
export declare const setMetric: (name: string, value: number, attributes?: Attributes | undefined, options?: MetricOptions | undefined) => void;
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setMetric = exports.addUpDownCounter = exports.recordHistogram = exports.incrementCounter = void 0;
|
|
4
|
+
const api_1 = require("@opentelemetry/api");
|
|
5
|
+
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
|
6
|
+
const constants_js_1 = require("./constants.js");
|
|
7
|
+
const utils_js_1 = require("./utils.js");
|
|
8
|
+
const state_js_1 = require("./state.js");
|
|
9
|
+
const instruments = new Map();
|
|
10
|
+
const meter = () => api_1.metrics.getMeter(constants_js_1.FOAM_DISTRO_NAME);
|
|
11
|
+
// pcga11: We use cache to avoid wasting time searching for the meter every time we need to create an instrument.
|
|
12
|
+
function cachedInstrument(type, name, create) {
|
|
13
|
+
const key = `${type}:${name}`;
|
|
14
|
+
if (!instruments.has(key))
|
|
15
|
+
instruments.set(key, create());
|
|
16
|
+
return instruments.get(key);
|
|
17
|
+
}
|
|
18
|
+
exports.incrementCounter = (0, utils_js_1.whenInitialized)(state_js_1.Signals.metrics, (name, n = 1, attributes, options = {}) => {
|
|
19
|
+
cachedInstrument(sdk_metrics_1.InstrumentType.COUNTER, name, () => meter().createCounter(name, options)).add(n, attributes);
|
|
20
|
+
});
|
|
21
|
+
exports.recordHistogram = (0, utils_js_1.whenInitialized)(state_js_1.Signals.metrics, (name, value, attributes, options = {}) => {
|
|
22
|
+
cachedInstrument(sdk_metrics_1.InstrumentType.HISTOGRAM, name, () => meter().createHistogram(name, options)).record(value, attributes);
|
|
23
|
+
});
|
|
24
|
+
exports.addUpDownCounter = (0, utils_js_1.whenInitialized)(state_js_1.Signals.metrics, (name, n, attributes, options = {}) => {
|
|
25
|
+
cachedInstrument(sdk_metrics_1.InstrumentType.UP_DOWN_COUNTER, name, () => meter().createUpDownCounter(name, options)).add(n, attributes);
|
|
26
|
+
});
|
|
27
|
+
exports.setMetric = (0, utils_js_1.whenInitialized)(state_js_1.Signals.metrics, (name, value, attributes, options = {}) => {
|
|
28
|
+
cachedInstrument(sdk_metrics_1.InstrumentType.GAUGE, name, () => meter().createGauge(name, options)).record(value, attributes);
|
|
29
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Context, type Span } from "@opentelemetry/api";
|
|
2
|
+
export type CaptureDirection = "request" | "response";
|
|
3
|
+
export type CaptureSide = "client" | "server";
|
|
4
|
+
export type HeaderSnapshot = Readonly<Record<string, unknown>>;
|
|
5
|
+
export declare function setHeaderAttributes(span: Span, direction: CaptureDirection, headers: HeaderSnapshot): void;
|
|
6
|
+
export declare class BodyCollector {
|
|
7
|
+
private readonly span;
|
|
8
|
+
private readonly context;
|
|
9
|
+
private readonly side;
|
|
10
|
+
private readonly direction;
|
|
11
|
+
private readonly headers;
|
|
12
|
+
private readonly id;
|
|
13
|
+
private readonly attr;
|
|
14
|
+
private readonly chunks;
|
|
15
|
+
private readonly cleanups;
|
|
16
|
+
private readonly deadline;
|
|
17
|
+
private observedBytes;
|
|
18
|
+
private capturedBytes;
|
|
19
|
+
private truncated;
|
|
20
|
+
private finalized;
|
|
21
|
+
constructor(span: Span, context: Context, side: CaptureSide, direction: CaptureDirection, headers: () => HeaderSnapshot);
|
|
22
|
+
static create(span: Span, side: CaptureSide, direction: CaptureDirection, headers: () => HeaderSnapshot): BodyCollector | undefined;
|
|
23
|
+
addCleanup(cleanup: () => void): void;
|
|
24
|
+
observe(value: unknown, encoding?: unknown): void;
|
|
25
|
+
finalize(complete: boolean): void;
|
|
26
|
+
private emit;
|
|
27
|
+
}
|