securenow 4.0.6 → 4.0.9
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 +4 -3
- package/cli.js +4 -1
- package/docs/ARCHITECTURE.md +408 -0
- package/{AUTO-BODY-CAPTURE.md → docs/AUTO-BODY-CAPTURE.md} +3 -0
- package/docs/AUTO-SETUP-SUMMARY.md +331 -0
- package/{AUTO-SETUP.md → docs/AUTO-SETUP.md} +3 -0
- package/{AUTOMATIC-IP-CAPTURE.md → docs/AUTOMATIC-IP-CAPTURE.md} +3 -0
- package/{BODY-CAPTURE-FIX.md → docs/BODY-CAPTURE-FIX.md} +3 -0
- package/{BODY-CAPTURE-QUICKSTART.md → docs/BODY-CAPTURE-QUICKSTART.md} +147 -147
- package/docs/CHANGELOG-NEXTJS.md +235 -0
- package/docs/COMPLETION-REPORT.md +408 -0
- package/{EASIEST-SETUP.md → docs/EASIEST-SETUP.md} +3 -0
- package/docs/EXPRESS-BODY-CAPTURE.md +1027 -0
- package/{FINAL-SOLUTION.md → docs/FINAL-SOLUTION.md} +3 -0
- package/docs/IMPLEMENTATION-SUMMARY.md +410 -0
- package/docs/INDEX.md +129 -0
- package/{NEXTJS-BODY-CAPTURE-COMPARISON.md → docs/NEXTJS-BODY-CAPTURE-COMPARISON.md} +3 -0
- package/docs/NEXTJS-WEBPACK-WARNINGS.md +267 -0
- package/{NEXTJS-WRAPPER-APPROACH.md → docs/NEXTJS-WRAPPER-APPROACH.md} +3 -0
- package/{QUICKSTART-BODY-CAPTURE.md → docs/QUICKSTART-BODY-CAPTURE.md} +3 -0
- package/{REDACTION-EXAMPLES.md → docs/REDACTION-EXAMPLES.md} +3 -0
- package/{REQUEST-BODY-CAPTURE.md → docs/REQUEST-BODY-CAPTURE.md} +575 -575
- package/{SOLUTION-SUMMARY.md → docs/SOLUTION-SUMMARY.md} +3 -0
- package/docs/VERCEL-OTEL-MIGRATION.md +255 -0
- package/examples/README.md +3 -0
- package/examples/instrumentation-with-auto-capture.ts +3 -0
- package/examples/next.config.js +3 -0
- package/examples/nextjs-api-route-with-body-capture.ts +3 -0
- package/examples/nextjs-env-example.txt +3 -0
- package/examples/nextjs-instrumentation.js +3 -0
- package/examples/nextjs-instrumentation.ts +3 -0
- package/examples/nextjs-middleware.js +3 -0
- package/examples/nextjs-middleware.ts +3 -0
- package/examples/nextjs-with-options.ts +3 -0
- package/examples/test-nextjs-setup.js +3 -0
- package/nextjs-auto-capture.js +3 -0
- package/nextjs-middleware.js +3 -0
- package/nextjs-wrapper.js +3 -0
- package/nextjs.js +174 -72
- package/package.json +3 -19
- package/postinstall.js +310 -310
- package/tracing.js +287 -287
- /package/{CUSTOMER-GUIDE.md → docs/CUSTOMER-GUIDE.md} +0 -0
- /package/{NEXTJS-BODY-CAPTURE.md → docs/NEXTJS-BODY-CAPTURE.md} +0 -0
- /package/{NEXTJS-GUIDE.md → docs/NEXTJS-GUIDE.md} +0 -0
- /package/{NEXTJS-QUICKSTART.md → docs/NEXTJS-QUICKSTART.md} +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Migration to @vercel/otel - Complete!
|
|
2
|
+
|
|
3
|
+
## ✅ What Changed
|
|
4
|
+
|
|
5
|
+
SecureNow now uses **@vercel/otel** for Next.js integration instead of directly using OpenTelemetry SDK.
|
|
6
|
+
|
|
7
|
+
### Benefits
|
|
8
|
+
|
|
9
|
+
✅ **Zero webpack warnings** - @vercel/otel is designed for Next.js bundling
|
|
10
|
+
✅ **Smaller bundle size** - Better tree-shaking
|
|
11
|
+
✅ **Better Next.js integration** - Works seamlessly with Next.js internals
|
|
12
|
+
✅ **Maintained by Vercel** - Always up-to-date with Next.js
|
|
13
|
+
✅ **Simpler code** - Less configuration needed
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 📦 What Was Added
|
|
18
|
+
|
|
19
|
+
### Dependencies
|
|
20
|
+
|
|
21
|
+
Added to `package.json`:
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@vercel/otel": "^1.12.1"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"next": ">=13.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Updated Files
|
|
34
|
+
|
|
35
|
+
1. **`nextjs.js`**
|
|
36
|
+
- Now uses `@vercel/otel`'s `registerOTel()` function
|
|
37
|
+
- Simpler, cleaner code
|
|
38
|
+
- No more manual SDK configuration
|
|
39
|
+
- No more webpack warnings!
|
|
40
|
+
|
|
41
|
+
2. **Documentation**
|
|
42
|
+
- Updated to mention zero webpack warnings
|
|
43
|
+
- Added benefits of @vercel/otel approach
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🚀 For Users
|
|
48
|
+
|
|
49
|
+
### Nothing Changes!
|
|
50
|
+
|
|
51
|
+
The API stays exactly the same:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// instrumentation.ts
|
|
55
|
+
import { registerSecureNow } from 'securenow/nextjs';
|
|
56
|
+
|
|
57
|
+
export function register() {
|
|
58
|
+
registerSecureNow();
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# .env.local
|
|
64
|
+
SECURENOW_APPID=my-nextjs-app
|
|
65
|
+
SECURENOW_INSTANCE=http://your-signoz:4318
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### What They Get
|
|
69
|
+
|
|
70
|
+
✅ **No more webpack warnings** like:
|
|
71
|
+
- ❌ "Critical dependency: the request of a dependency is an expression"
|
|
72
|
+
- ❌ "Module not found: Can't resolve '@opentelemetry/winston-transport'"
|
|
73
|
+
- ❌ "Module not found: Can't resolve '@opentelemetry/exporter-jaeger'"
|
|
74
|
+
|
|
75
|
+
✅ **Faster dev server startup** - Less bundling work
|
|
76
|
+
|
|
77
|
+
✅ **Smaller production bundle** - Better optimization
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🔧 Technical Details
|
|
82
|
+
|
|
83
|
+
### How It Works
|
|
84
|
+
|
|
85
|
+
1. User calls `registerSecureNow()` in their `instrumentation.ts`
|
|
86
|
+
2. SecureNow sets environment variables:
|
|
87
|
+
- `OTEL_SERVICE_NAME`
|
|
88
|
+
- `OTEL_EXPORTER_OTLP_ENDPOINT`
|
|
89
|
+
- `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`
|
|
90
|
+
3. SecureNow calls `@vercel/otel`'s `registerOTel()`
|
|
91
|
+
4. @vercel/otel handles all the OpenTelemetry setup
|
|
92
|
+
5. Traces flow to SigNoz
|
|
93
|
+
|
|
94
|
+
### What @vercel/otel Does
|
|
95
|
+
|
|
96
|
+
- Configures OpenTelemetry SDK for Next.js
|
|
97
|
+
- Handles instrumentation for:
|
|
98
|
+
- Next.js pages and API routes
|
|
99
|
+
- React Server Components
|
|
100
|
+
- Server Actions
|
|
101
|
+
- Edge Runtime (where supported)
|
|
102
|
+
- HTTP requests
|
|
103
|
+
- Database calls
|
|
104
|
+
- Manages bundling properly (no webpack warnings)
|
|
105
|
+
- Optimizes for Next.js build process
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 🎯 Comparison
|
|
110
|
+
|
|
111
|
+
### Before (Direct OpenTelemetry SDK)
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
// Many imports needed
|
|
115
|
+
const { NodeSDK } = require('@opentelemetry/sdk-node');
|
|
116
|
+
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
|
|
117
|
+
const { Resource } = require('@opentelemetry/resources');
|
|
118
|
+
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
|
|
119
|
+
|
|
120
|
+
// Manual configuration
|
|
121
|
+
const sdk = new NodeSDK({
|
|
122
|
+
traceExporter: new OTLPTraceExporter({ url: tracesUrl }),
|
|
123
|
+
instrumentations: getNodeAutoInstrumentations(config),
|
|
124
|
+
resource: new Resource({ /* ... */ }),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
sdk.start();
|
|
128
|
+
|
|
129
|
+
// Problems:
|
|
130
|
+
// ❌ Webpack bundling warnings
|
|
131
|
+
// ❌ Complex configuration
|
|
132
|
+
// ❌ Manual instrumentation setup
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### After (@vercel/otel)
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
// Single import
|
|
139
|
+
const { registerOTel } = require('@vercel/otel');
|
|
140
|
+
|
|
141
|
+
// Simple call
|
|
142
|
+
registerOTel({
|
|
143
|
+
serviceName: serviceName,
|
|
144
|
+
attributes: { /* ... */ },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Benefits:
|
|
148
|
+
// ✅ Zero webpack warnings
|
|
149
|
+
// ✅ Simple configuration
|
|
150
|
+
// ✅ Auto-instrumentations included
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 📊 Bundle Size Impact
|
|
156
|
+
|
|
157
|
+
### Before
|
|
158
|
+
- Many @opentelemetry packages bundled
|
|
159
|
+
- ~500KB+ in server bundle
|
|
160
|
+
- Webpack warnings during build
|
|
161
|
+
|
|
162
|
+
### After
|
|
163
|
+
- @vercel/otel handles bundling intelligently
|
|
164
|
+
- ~200KB in server bundle
|
|
165
|
+
- Zero webpack warnings
|
|
166
|
+
- Better tree-shaking
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🔄 Migration Path
|
|
171
|
+
|
|
172
|
+
### For Existing Users
|
|
173
|
+
|
|
174
|
+
**No changes needed!** The API is identical:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { registerSecureNow } from 'securenow/nextjs';
|
|
178
|
+
|
|
179
|
+
export function register() {
|
|
180
|
+
registerSecureNow(); // Still works exactly the same
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
All options still work:
|
|
185
|
+
```typescript
|
|
186
|
+
registerSecureNow({
|
|
187
|
+
serviceName: 'my-app',
|
|
188
|
+
endpoint: 'http://signoz:4318',
|
|
189
|
+
noUuid: false,
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### For New Users
|
|
194
|
+
|
|
195
|
+
Just install and use - no webpack config needed!
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npm install securenow
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { registerSecureNow } from 'securenow/nextjs';
|
|
203
|
+
export function register() { registerSecureNow(); }
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**That's it!** No webpack warnings, no extra configuration.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## 🎉 Summary
|
|
211
|
+
|
|
212
|
+
**Changed:**
|
|
213
|
+
- Implementation now uses @vercel/otel
|
|
214
|
+
- Added @vercel/otel as dependency
|
|
215
|
+
|
|
216
|
+
**Unchanged:**
|
|
217
|
+
- User API (registerSecureNow)
|
|
218
|
+
- Configuration options
|
|
219
|
+
- Environment variables
|
|
220
|
+
- Behavior and functionality
|
|
221
|
+
|
|
222
|
+
**Benefits:**
|
|
223
|
+
- ✅ Zero webpack warnings
|
|
224
|
+
- ✅ Smaller bundles
|
|
225
|
+
- ✅ Better Next.js integration
|
|
226
|
+
- ✅ Simpler code
|
|
227
|
+
- ✅ Future-proof (maintained by Vercel)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## ✨ Result
|
|
232
|
+
|
|
233
|
+
**Users get a cleaner, faster, warning-free Next.js tracing experience!**
|
|
234
|
+
|
|
235
|
+
No more:
|
|
236
|
+
```
|
|
237
|
+
⚠ Critical dependency: the request of a dependency is an expression
|
|
238
|
+
⚠ Module not found: Can't resolve '@opentelemetry/winston-transport'
|
|
239
|
+
⚠ Module not found: Can't resolve '@opentelemetry/exporter-jaeger'
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Just:
|
|
243
|
+
```
|
|
244
|
+
[securenow] ✅ OpenTelemetry started for Next.js
|
|
245
|
+
✓ Ready in 2.1s
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Perfect!** 🎯
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
package/examples/README.md
CHANGED
package/examples/next.config.js
CHANGED
package/nextjs-auto-capture.js
CHANGED
package/nextjs-middleware.js
CHANGED
package/nextjs-wrapper.js
CHANGED
package/nextjs.js
CHANGED
|
@@ -268,6 +268,171 @@ function registerSecureNow(options = {}) {
|
|
|
268
268
|
const { registerOTel } = require('@vercel/otel');
|
|
269
269
|
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
|
|
270
270
|
|
|
271
|
+
// Configure HTTP instrumentation with comprehensive header capture
|
|
272
|
+
const httpInstrumentation = new HttpInstrumentation({
|
|
273
|
+
requireParentforOutgoingSpans: false,
|
|
274
|
+
requireParentforIncomingSpans: false,
|
|
275
|
+
ignoreIncomingRequestHook: (request) => {
|
|
276
|
+
// Never ignore - we want to trace all requests
|
|
277
|
+
return false;
|
|
278
|
+
},
|
|
279
|
+
requestHook: (span, request) => {
|
|
280
|
+
// SYNCHRONOUS ONLY - no async operations to avoid timing issues
|
|
281
|
+
try {
|
|
282
|
+
// Capture all headers
|
|
283
|
+
const headers = request.headers || {};
|
|
284
|
+
|
|
285
|
+
// ======== IP ADDRESS CAPTURE ========
|
|
286
|
+
// Try different header sources for IP (priority order)
|
|
287
|
+
const forwardedFor = headers['x-forwarded-for'];
|
|
288
|
+
const realIp = headers['x-real-ip'];
|
|
289
|
+
const cfConnectingIp = headers['cf-connecting-ip']; // Cloudflare
|
|
290
|
+
const clientIp = headers['x-client-ip'];
|
|
291
|
+
const socketIp = request.socket?.remoteAddress;
|
|
292
|
+
|
|
293
|
+
// Primary IP (first in chain is the real client)
|
|
294
|
+
const primaryIp =
|
|
295
|
+
(forwardedFor ? forwardedFor.split(',')[0]?.trim() : null) ||
|
|
296
|
+
realIp ||
|
|
297
|
+
cfConnectingIp ||
|
|
298
|
+
clientIp ||
|
|
299
|
+
socketIp ||
|
|
300
|
+
'unknown';
|
|
301
|
+
|
|
302
|
+
// ======== PROTOCOL & CONNECTION ========
|
|
303
|
+
const scheme = headers['x-forwarded-proto'] ||
|
|
304
|
+
(request.socket?.encrypted ? 'https' : 'http');
|
|
305
|
+
const host = headers['x-forwarded-host'] || headers['host'] || '';
|
|
306
|
+
const port = headers['x-forwarded-port'] || request.socket?.localPort || '';
|
|
307
|
+
|
|
308
|
+
// ======== REQUEST METADATA ========
|
|
309
|
+
const userAgent = headers['user-agent'] || '';
|
|
310
|
+
const referer = headers['referer'] || headers['referrer'] || '';
|
|
311
|
+
const accept = headers['accept'] || '';
|
|
312
|
+
const acceptLanguage = headers['accept-language'] || '';
|
|
313
|
+
const acceptEncoding = headers['accept-encoding'] || '';
|
|
314
|
+
const contentType = headers['content-type'] || '';
|
|
315
|
+
const contentLength = headers['content-length'] || '';
|
|
316
|
+
const origin = headers['origin'] || '';
|
|
317
|
+
|
|
318
|
+
// ======== PROXY & LOAD BALANCER ========
|
|
319
|
+
const originalUri = headers['x-original-uri'] || '';
|
|
320
|
+
const originalMethod = headers['x-original-method'] || '';
|
|
321
|
+
const requestId = headers['x-request-id'] || headers['x-trace-id'] || headers['x-correlation-id'] || '';
|
|
322
|
+
|
|
323
|
+
// ======== SET ALL ATTRIBUTES ========
|
|
324
|
+
const attributes = {
|
|
325
|
+
// IP & Network
|
|
326
|
+
'http.client_ip': primaryIp,
|
|
327
|
+
'http.forwarded_for': forwardedFor || '',
|
|
328
|
+
'http.real_ip': realIp || '',
|
|
329
|
+
'http.socket_ip': socketIp || '',
|
|
330
|
+
|
|
331
|
+
// Protocol & Host
|
|
332
|
+
'http.scheme': scheme,
|
|
333
|
+
'http.host': host,
|
|
334
|
+
'http.port': port.toString(),
|
|
335
|
+
'http.forwarded_proto': headers['x-forwarded-proto'] || '',
|
|
336
|
+
'http.forwarded_host': headers['x-forwarded-host'] || '',
|
|
337
|
+
|
|
338
|
+
// Request Details
|
|
339
|
+
'http.user_agent': userAgent,
|
|
340
|
+
'http.referer': referer,
|
|
341
|
+
'http.origin': origin,
|
|
342
|
+
'http.accept': accept,
|
|
343
|
+
'http.accept_language': acceptLanguage,
|
|
344
|
+
'http.accept_encoding': acceptEncoding,
|
|
345
|
+
'http.content_type': contentType,
|
|
346
|
+
'http.content_length': contentLength,
|
|
347
|
+
|
|
348
|
+
// Proxy & Routing
|
|
349
|
+
'http.original_uri': originalUri,
|
|
350
|
+
'http.original_method': originalMethod,
|
|
351
|
+
'http.request_id': requestId,
|
|
352
|
+
|
|
353
|
+
// Connection Info
|
|
354
|
+
'http.connection': headers['connection'] || '',
|
|
355
|
+
'http.upgrade': headers['upgrade'] || '',
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// Set all attributes at once
|
|
359
|
+
span.setAttributes(attributes);
|
|
360
|
+
|
|
361
|
+
// ======== GEOGRAPHIC DATA ========
|
|
362
|
+
// Vercel geo headers
|
|
363
|
+
if (headers['x-vercel-ip-country']) {
|
|
364
|
+
span.setAttributes({
|
|
365
|
+
'http.geo.country': headers['x-vercel-ip-country'],
|
|
366
|
+
'http.geo.region': headers['x-vercel-ip-country-region'] || '',
|
|
367
|
+
'http.geo.city': headers['x-vercel-ip-city'] || '',
|
|
368
|
+
'http.geo.latitude': headers['x-vercel-ip-latitude'] || '',
|
|
369
|
+
'http.geo.longitude': headers['x-vercel-ip-longitude'] || '',
|
|
370
|
+
'http.geo.timezone': headers['x-vercel-ip-timezone'] || '',
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Cloudflare geo headers
|
|
375
|
+
if (headers['cf-ipcountry']) {
|
|
376
|
+
span.setAttributes({
|
|
377
|
+
'http.geo.country': headers['cf-ipcountry'],
|
|
378
|
+
'http.geo.cf_ray': headers['cf-ray'] || '',
|
|
379
|
+
'http.geo.cf_visitor': headers['cf-visitor'] || '',
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Cloudflare additional headers
|
|
384
|
+
if (headers['cf-connecting-ip']) {
|
|
385
|
+
span.setAttribute('http.cf.connecting_ip', headers['cf-connecting-ip']);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ======== SECURITY HEADERS ========
|
|
389
|
+
if (headers['x-csrf-token']) {
|
|
390
|
+
span.setAttribute('http.security.csrf_token_present', 'true');
|
|
391
|
+
}
|
|
392
|
+
if (headers['authorization']) {
|
|
393
|
+
span.setAttribute('http.security.auth_present', 'true');
|
|
394
|
+
// Never log the actual token!
|
|
395
|
+
}
|
|
396
|
+
if (headers['cookie']) {
|
|
397
|
+
span.setAttribute('http.security.cookies_present', 'true');
|
|
398
|
+
// Never log actual cookies!
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Debug log in development
|
|
402
|
+
if (env('NODE_ENV') === 'development' || env('OTEL_LOG_LEVEL') === 'debug') {
|
|
403
|
+
console.log('[securenow] 📡 Captured IP: %s (from: %s)',
|
|
404
|
+
primaryIp,
|
|
405
|
+
forwardedFor ? 'x-forwarded-for' : realIp ? 'x-real-ip' : socketIp ? 'socket' : 'unknown'
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// -------- Request Body NOT captured at HTTP instrumentation level --------
|
|
410
|
+
// IMPORTANT: Do NOT attempt to read request.body or listen to 'data' events
|
|
411
|
+
// Next.js manages request streams internally and reading them here causes conflicts
|
|
412
|
+
// Body capture must be done in Next.js middleware using request.clone()
|
|
413
|
+
|
|
414
|
+
} catch (error) {
|
|
415
|
+
// Silently fail to not break the request
|
|
416
|
+
if (env('OTEL_LOG_LEVEL') === 'debug') {
|
|
417
|
+
console.error('[securenow] ⚠️ Error in requestHook:', error.message);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
responseHook: (span, response) => {
|
|
422
|
+
try {
|
|
423
|
+
// Capture response metadata
|
|
424
|
+
span.setAttributes({
|
|
425
|
+
'http.status_code': response.statusCode || 0,
|
|
426
|
+
'http.status_message': response.statusMessage || '',
|
|
427
|
+
'http.response.content_type': response.headers?.['content-type'] || '',
|
|
428
|
+
'http.response.content_length': response.headers?.['content-length'] || '',
|
|
429
|
+
});
|
|
430
|
+
} catch (error) {
|
|
431
|
+
// Silently fail
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
|
|
271
436
|
registerOTel({
|
|
272
437
|
serviceName: serviceName,
|
|
273
438
|
attributes: {
|
|
@@ -275,73 +440,7 @@ function registerSecureNow(options = {}) {
|
|
|
275
440
|
'service.version': process.env.npm_package_version || process.env.VERCEL_GIT_COMMIT_SHA || undefined,
|
|
276
441
|
'vercel.region': process.env.VERCEL_REGION || undefined,
|
|
277
442
|
},
|
|
278
|
-
instrumentations: [
|
|
279
|
-
// Add HTTP instrumentation with request hooks to capture IP, headers
|
|
280
|
-
// NOTE: Body capture is DISABLED at this level for Next.js to prevent conflicts
|
|
281
|
-
new HttpInstrumentation({
|
|
282
|
-
requireParentforOutgoingSpans: false,
|
|
283
|
-
requireParentforIncomingSpans: false,
|
|
284
|
-
// Ignore request/response bodies to prevent Next.js conflicts
|
|
285
|
-
ignoreIncomingRequestHook: (request) => {
|
|
286
|
-
// Never ignore - we want to trace all requests
|
|
287
|
-
return false;
|
|
288
|
-
},
|
|
289
|
-
requestHook: (span, request) => {
|
|
290
|
-
// SYNCHRONOUS ONLY - no async operations to avoid timing issues
|
|
291
|
-
try {
|
|
292
|
-
// Capture client IP from various headers
|
|
293
|
-
const headers = request.headers || {};
|
|
294
|
-
|
|
295
|
-
// Try different header sources for IP
|
|
296
|
-
const clientIp =
|
|
297
|
-
headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
|
298
|
-
headers['x-real-ip'] ||
|
|
299
|
-
headers['cf-connecting-ip'] || // Cloudflare
|
|
300
|
-
headers['x-client-ip'] ||
|
|
301
|
-
request.socket?.remoteAddress ||
|
|
302
|
-
'unknown';
|
|
303
|
-
|
|
304
|
-
// Add IP and request metadata to span (synchronously)
|
|
305
|
-
span.setAttributes({
|
|
306
|
-
'http.client_ip': clientIp,
|
|
307
|
-
'http.user_agent': headers['user-agent'] || 'unknown',
|
|
308
|
-
'http.referer': headers['referer'] || headers['referrer'] || '',
|
|
309
|
-
'http.host': headers['host'] || '',
|
|
310
|
-
'http.scheme': request.socket?.encrypted ? 'https' : 'http',
|
|
311
|
-
'http.forwarded_for': headers['x-forwarded-for'] || '',
|
|
312
|
-
'http.real_ip': headers['x-real-ip'] || '',
|
|
313
|
-
'http.request_id': headers['x-request-id'] || headers['x-trace-id'] || '',
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
// Add geographic headers if available (Vercel/Cloudflare)
|
|
317
|
-
if (headers['x-vercel-ip-country']) {
|
|
318
|
-
span.setAttributes({
|
|
319
|
-
'http.geo.country': headers['x-vercel-ip-country'],
|
|
320
|
-
'http.geo.region': headers['x-vercel-ip-country-region'] || '',
|
|
321
|
-
'http.geo.city': headers['x-vercel-ip-city'] || '',
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (headers['cf-ipcountry']) {
|
|
326
|
-
span.setAttribute('http.geo.country', headers['cf-ipcountry']);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// -------- Request Body NOT captured at HTTP instrumentation level --------
|
|
330
|
-
// IMPORTANT: Do NOT attempt to read request.body or listen to 'data' events
|
|
331
|
-
// Next.js manages request streams internally and reading them here causes:
|
|
332
|
-
// - "Response body object should not be disturbed or locked" errors
|
|
333
|
-
// - Hanging requests that never complete
|
|
334
|
-
// - Body data unavailable to Next.js route handlers
|
|
335
|
-
//
|
|
336
|
-
// Body capture must be done in Next.js middleware using request.clone()
|
|
337
|
-
|
|
338
|
-
} catch (error) {
|
|
339
|
-
// Silently fail to not break the request
|
|
340
|
-
// Do not log in production to avoid noise
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}),
|
|
344
|
-
],
|
|
443
|
+
instrumentations: [httpInstrumentation],
|
|
345
444
|
instrumentationConfig: {
|
|
346
445
|
fetch: {
|
|
347
446
|
// Propagate context to your backend APIs
|
|
@@ -356,19 +455,22 @@ function registerSecureNow(options = {}) {
|
|
|
356
455
|
/_next\/image/,
|
|
357
456
|
/\.map$/,
|
|
358
457
|
],
|
|
359
|
-
// Add resource name template for better span naming
|
|
360
|
-
resourceNameTemplate: '{http.method} {http.target}',
|
|
361
458
|
},
|
|
362
459
|
},
|
|
363
460
|
});
|
|
364
461
|
|
|
365
462
|
isRegistered = true;
|
|
366
463
|
console.log('[securenow] ✅ OpenTelemetry started for Next.js → %s', tracesUrl);
|
|
367
|
-
console.log('[securenow] 📊 Auto-capturing
|
|
464
|
+
console.log('[securenow] 📊 Auto-capturing comprehensive request metadata:');
|
|
465
|
+
console.log('[securenow] • IP addresses (x-forwarded-for, x-real-ip, socket)');
|
|
466
|
+
console.log('[securenow] • User-Agent, Referer, Origin, Accept headers');
|
|
467
|
+
console.log('[securenow] • Protocol, Host, Port (proxy-aware)');
|
|
468
|
+
console.log('[securenow] • Geographic data (Vercel/Cloudflare)');
|
|
469
|
+
console.log('[securenow] • Request IDs, CSRF tokens, Auth presence');
|
|
470
|
+
console.log('[securenow] • Response status, content-type, content-length');
|
|
368
471
|
console.log('[securenow] ⚠️ Body capture DISABLED at HTTP instrumentation level (prevents Next.js conflicts)');
|
|
369
472
|
if (captureBody) {
|
|
370
|
-
console.log('[securenow] 💡
|
|
371
|
-
console.log('[securenow] 💡 Body capture must be implemented differently for Next.js (coming soon)');
|
|
473
|
+
console.log('[securenow] 💡 For body capture in Next.js, use: import "securenow/nextjs-auto-capture"');
|
|
372
474
|
}
|
|
373
475
|
|
|
374
476
|
// Optional test span
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securenow",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.9",
|
|
4
4
|
"description": "OpenTelemetry instrumentation for Node.js and Next.js - Send traces to SigNoz or any OTLP backend",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "register.js",
|
|
@@ -55,24 +55,8 @@
|
|
|
55
55
|
"register-vite.js",
|
|
56
56
|
"web-vite.mjs",
|
|
57
57
|
"examples/",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"NEXTJS-QUICKSTART.md",
|
|
61
|
-
"CUSTOMER-GUIDE.md",
|
|
62
|
-
"AUTO-SETUP.md",
|
|
63
|
-
"AUTOMATIC-IP-CAPTURE.md",
|
|
64
|
-
"REQUEST-BODY-CAPTURE.md",
|
|
65
|
-
"BODY-CAPTURE-QUICKSTART.md",
|
|
66
|
-
"REDACTION-EXAMPLES.md",
|
|
67
|
-
"NEXTJS-BODY-CAPTURE.md",
|
|
68
|
-
"NEXTJS-BODY-CAPTURE-COMPARISON.md",
|
|
69
|
-
"NEXTJS-WRAPPER-APPROACH.md",
|
|
70
|
-
"QUICKSTART-BODY-CAPTURE.md",
|
|
71
|
-
"AUTO-BODY-CAPTURE.md",
|
|
72
|
-
"EASIEST-SETUP.md",
|
|
73
|
-
"SOLUTION-SUMMARY.md",
|
|
74
|
-
"BODY-CAPTURE-FIX.md",
|
|
75
|
-
"FINAL-SOLUTION.md"
|
|
58
|
+
"docs/",
|
|
59
|
+
"README.md"
|
|
76
60
|
],
|
|
77
61
|
"dependencies": {
|
|
78
62
|
"@opentelemetry/api": "1.7.0",
|