@foam-ai/node 0.1.0-alpha.1 → 0.1.0-alpha.10

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 (64) hide show
  1. package/README.md +649 -0
  2. package/dist/before-send.d.ts +16 -0
  3. package/dist/before-send.js +41 -0
  4. package/dist/constants.d.ts +44 -0
  5. package/dist/constants.js +67 -0
  6. package/dist/endpoint.d.ts +2 -0
  7. package/dist/endpoint.js +11 -0
  8. package/dist/exporters.d.ts +33 -0
  9. package/dist/exporters.js +179 -0
  10. package/dist/index.d.ts +10 -0
  11. package/dist/index.js +28 -0
  12. package/dist/ingest.d.ts +12 -0
  13. package/dist/ingest.js +70 -0
  14. package/dist/init.d.ts +26 -0
  15. package/dist/init.js +169 -0
  16. package/dist/instrumentations.d.ts +4 -0
  17. package/dist/instrumentations.js +120 -0
  18. package/dist/library-versions.d.ts +1 -0
  19. package/dist/library-versions.js +59 -0
  20. package/dist/logs.d.ts +10 -0
  21. package/dist/logs.js +61 -0
  22. package/dist/metrics.d.ts +5 -0
  23. package/dist/metrics.js +29 -0
  24. package/dist/network-capture/collector.d.ts +27 -0
  25. package/dist/network-capture/collector.js +411 -0
  26. package/dist/network-capture/http.d.ts +5 -0
  27. package/dist/network-capture/http.js +221 -0
  28. package/dist/network-capture/index.d.ts +3 -0
  29. package/dist/network-capture/index.js +20 -0
  30. package/dist/network-capture/redact.d.ts +9 -0
  31. package/dist/network-capture/redact.js +401 -0
  32. package/dist/network-capture/support.d.ts +2 -0
  33. package/dist/network-capture/support.js +38 -0
  34. package/dist/network-capture/undici.d.ts +5 -0
  35. package/dist/network-capture/undici.js +177 -0
  36. package/dist/otlp.d.ts +8 -0
  37. package/dist/otlp.js +46 -0
  38. package/dist/propagation.d.ts +5 -0
  39. package/dist/propagation.js +47 -0
  40. package/dist/redaction-keys.d.ts +3 -0
  41. package/dist/redaction-keys.js +421 -0
  42. package/dist/redaction.d.ts +24 -0
  43. package/dist/redaction.js +270 -0
  44. package/dist/report.d.ts +9 -0
  45. package/dist/report.js +56 -0
  46. package/dist/resource.d.ts +3 -0
  47. package/dist/resource.js +24 -0
  48. package/dist/state.d.ts +26 -0
  49. package/dist/state.js +61 -0
  50. package/dist/traces.d.ts +1 -0
  51. package/dist/traces.js +21 -0
  52. package/dist/utils.d.ts +4 -0
  53. package/dist/utils.js +20 -0
  54. package/package.json +47 -19
  55. package/dist/node/src/capture-exception.d.ts +0 -9
  56. package/dist/node/src/capture-exception.js +0 -31
  57. package/dist/node/src/index.d.ts +0 -9
  58. package/dist/node/src/index.js +0 -12
  59. package/dist/node/src/init.d.ts +0 -27
  60. package/dist/node/src/init.js +0 -203
  61. package/dist/shared/constants.d.ts +0 -1
  62. package/dist/shared/constants.js +0 -4
  63. package/dist/shared/util.d.ts +0 -9
  64. package/dist/shared/util.js +0 -21
package/dist/init.js ADDED
@@ -0,0 +1,169 @@
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),
111
+ }),
112
+ ...(options.additionalMetricReaders ?? []),
113
+ ],
114
+ });
115
+ const loggerProvider = new sdk_logs_1.LoggerProvider({
116
+ resource,
117
+ processors: [
118
+ new sdk_logs_1.BatchLogRecordProcessor({
119
+ exporter: new exporters_js_1.FoamLogExporter(options.token, undefined, {
120
+ redaction: redactionConfig,
121
+ beforeSend,
122
+ }),
123
+ }),
124
+ ...(options.additionalLogRecordProcessors ?? []),
125
+ ],
126
+ });
127
+ (0, logs_js_1.setFoamLoggerProvider)(loggerProvider);
128
+ // 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.
129
+ (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);
130
+ (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);
131
+ (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);
132
+ const instrumentations = (0, instrumentations_js_1.buildInstrumentations)(networkCapture, disableLogSending, options.ignoredOutboundHosts, options.additionalInstrumentations ?? []);
133
+ // pcga11: registerInstrumentations turns on instrumentation "hooks". OpenTelemetry wraps supported libraries when the app imports them.
134
+ (0, instrumentation_1.registerInstrumentations)({ instrumentations });
135
+ (0, state_js_1.setInstrumentations)(instrumentations.map(instrumentations_js_1.instrumentationLabel));
136
+ process.once("beforeExit", () => {
137
+ void Promise.all([
138
+ (0, state_js_1.getSignal)(state_js_1.Signals.traces) === state_js_1.SignalSources.global ? tracerProvider?.shutdown() : undefined,
139
+ (0, state_js_1.getSignal)(state_js_1.Signals.metrics) === state_js_1.SignalSources.global ? meterProvider?.shutdown() : undefined,
140
+ loggerProvider.shutdown(),
141
+ ]).catch(() => undefined);
142
+ });
143
+ (0, state_js_1.setInitialized)(true);
144
+ (0, report_js_1.report)({
145
+ token: options.token,
146
+ severity: api_logs_1.SeverityNumber.INFO,
147
+ message: 'SDK initialized',
148
+ });
149
+ }
150
+ catch (error) {
151
+ const errorMessage = error instanceof Error ? error.message : String(error);
152
+ for (const signal of [state_js_1.Signals.traces, state_js_1.Signals.metrics, state_js_1.Signals.logs, state_js_1.Signals.baggage]) {
153
+ (0, state_js_1.setSignal)(signal, state_js_1.SignalSources.none);
154
+ }
155
+ void Promise.all([
156
+ tracerProvider?.shutdown(),
157
+ meterProvider?.shutdown(),
158
+ (0, logs_js_1.getFoamLoggerProvider)()?.shutdown(),
159
+ ]).catch(() => undefined);
160
+ (0, logs_js_1.clearFoamLoggerProvider)();
161
+ (0, state_js_1.setInitialized)(false);
162
+ (0, report_js_1.report)({
163
+ token: options.token,
164
+ severity: api_logs_1.SeverityNumber.ERROR,
165
+ message: `SDK initialization failed: ${errorMessage}`,
166
+ error: errorMessage,
167
+ });
168
+ }
169
+ }
@@ -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;
@@ -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
+ }