@foam-ai/node 0.1.0-alpha.1 → 0.1.0-alpha.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/README.md +123 -0
- package/dist/node/src/init.js +12 -2
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# @foam-ai/node
|
|
2
|
+
|
|
3
|
+
Foam observability SDK for Node.js. Sends traces, logs, and metrics to Foam with zero configuration overhead.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @foam-ai/node
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
Call `init()` as early as possible in your application entry point:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import foam from '@foam-ai/node';
|
|
17
|
+
|
|
18
|
+
foam.init({
|
|
19
|
+
apiKey: process.env.FOAM_API_KEY,
|
|
20
|
+
serviceName: 'my-service',
|
|
21
|
+
isProduction: process.env.NODE_ENV === 'production',
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. In production, Foam will automatically collect:
|
|
26
|
+
|
|
27
|
+
- **Traces** — distributed traces via OpenTelemetry
|
|
28
|
+
- **Logs** — console.log/info/error/warn/debug output
|
|
29
|
+
- **Metrics** — runtime and custom metrics
|
|
30
|
+
- **Uncaught errors** — process-level `uncaughtException` and `unhandledRejection`
|
|
31
|
+
|
|
32
|
+
In non-production environments, `init()` is a silent no-op.
|
|
33
|
+
|
|
34
|
+
## Capturing errors
|
|
35
|
+
|
|
36
|
+
Unhandled exceptions and rejections are captured automatically. For caught errors you want to report, use `captureException`:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { captureException } from '@foam-ai/node';
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
await chargeCustomer(order);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
captureException(err);
|
|
45
|
+
return res.status(500).json({ error: 'Payment failed' });
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`captureException` works with or without an active trace span. It always emits an OpenTelemetry log record so errors reach Foam regardless of instrumentation setup.
|
|
50
|
+
|
|
51
|
+
## Auto-instrumentation (optional)
|
|
52
|
+
|
|
53
|
+
By default, no auto-instrumentations are registered. This keeps the SDK webpack/Turbopack-safe with no bundler configuration needed.
|
|
54
|
+
|
|
55
|
+
For plain Node.js servers (Express, Fastify, etc.), you can opt into automatic HTTP, DNS, and framework instrumentation:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import foam from '@foam-ai/node';
|
|
59
|
+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
60
|
+
|
|
61
|
+
foam.init({
|
|
62
|
+
apiKey: process.env.FOAM_API_KEY,
|
|
63
|
+
serviceName: 'my-api',
|
|
64
|
+
isProduction: process.env.NODE_ENV === 'production',
|
|
65
|
+
instrumentations: getNodeAutoInstrumentations(),
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install @opentelemetry/auto-instrumentations-node
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Next.js
|
|
74
|
+
|
|
75
|
+
In a Next.js app, create an `instrumentation.ts` file at the project root:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
export async function register() {
|
|
79
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
80
|
+
const foam = await import('@foam-ai/node');
|
|
81
|
+
foam.init({
|
|
82
|
+
apiKey: process.env.FOAM_API_KEY!,
|
|
83
|
+
serviceName: 'my-nextjs-app',
|
|
84
|
+
isProduction: process.env.NODE_ENV === 'production',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
No webpack externals or bundler config needed.
|
|
91
|
+
|
|
92
|
+
## Coexistence with other SDKs
|
|
93
|
+
|
|
94
|
+
Foam detects each OpenTelemetry signal independently. If another SDK (Sentry, @vercel/otel, Datadog, etc.) has already registered a TracerProvider, LoggerProvider, or MeterProvider, Foam attaches its exporters on top rather than replacing them. Both SDKs receive data.
|
|
95
|
+
|
|
96
|
+
If no providers exist, Foam creates its own. This means Foam works in any combination:
|
|
97
|
+
|
|
98
|
+
- Foam alone
|
|
99
|
+
- Foam + Sentry
|
|
100
|
+
- Foam + @vercel/otel
|
|
101
|
+
- Foam + any OpenTelemetry-compatible SDK
|
|
102
|
+
|
|
103
|
+
## API
|
|
104
|
+
|
|
105
|
+
### `init(options)`
|
|
106
|
+
|
|
107
|
+
| Option | Type | Required | Description |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `apiKey` | `string` | Yes | Your Foam API key |
|
|
110
|
+
| `serviceName` | `string` | Yes | Name of your service (appears in Foam dashboard) |
|
|
111
|
+
| `isProduction` | `boolean` | Yes | Set to `true` to enable telemetry. `false` = no-op. |
|
|
112
|
+
| `instrumentations` | `InstrumentationBase[]` | No | OpenTelemetry instrumentations to register |
|
|
113
|
+
|
|
114
|
+
### `captureException(error)`
|
|
115
|
+
|
|
116
|
+
Captures a caught error and sends it to Foam. Works with or without an active span.
|
|
117
|
+
|
|
118
|
+
## Design
|
|
119
|
+
|
|
120
|
+
- **HTTP-only exporters** — no gRPC, no Node.js built-in dependencies that break bundlers
|
|
121
|
+
- **Zero latency** — all setup is synchronous; exporting happens in background batch intervals
|
|
122
|
+
- **Crash-safe** — each signal (traces, logs, metrics, console, process handlers) is independently wrapped in try/catch
|
|
123
|
+
- **Idempotent** — safe to call `init()` multiple times; console patching and process handlers use Symbols to prevent double-registration
|
package/dist/node/src/init.js
CHANGED
|
@@ -190,8 +190,18 @@ function patchConsole() {
|
|
|
190
190
|
const original = console[method].bind(console);
|
|
191
191
|
console[method] = (...args) => {
|
|
192
192
|
original(...args);
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
const ctx = api_1.context.active();
|
|
194
|
+
const spanContext = api_1.trace.getSpanContext(ctx);
|
|
195
|
+
const traceAttributes = spanContext && (0, api_1.isSpanContextValid)(spanContext)
|
|
196
|
+
? { 'trace.id': spanContext.traceId, 'span.id': spanContext.spanId }
|
|
197
|
+
: undefined;
|
|
198
|
+
logger.emit({
|
|
199
|
+
context: ctx,
|
|
200
|
+
severityNumber,
|
|
201
|
+
severityText,
|
|
202
|
+
body: (0, util_1.format)(...args),
|
|
203
|
+
attributes: traceAttributes,
|
|
204
|
+
});
|
|
195
205
|
};
|
|
196
206
|
};
|
|
197
207
|
patch('log', api_logs_1.SeverityNumber.INFO, 'INFO');
|