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,1027 @@
|
|
|
1
|
+
# Request Body Capture for Express.js with PM2
|
|
2
|
+
|
|
3
|
+
This guide shows how to automatically capture request bodies in Express.js applications, including those running with PM2 clustering.
|
|
4
|
+
|
|
5
|
+
**Available for both JavaScript and TypeScript projects!**
|
|
6
|
+
|
|
7
|
+
## 📖 Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Overview](#-overview)
|
|
10
|
+
- [Quick Start](#-quick-start)
|
|
11
|
+
- [How It Works](#-how-it-works)
|
|
12
|
+
- [Configuration](#-configuration)
|
|
13
|
+
- [PM2 Clustering](#-pm2-clustering)
|
|
14
|
+
- [Supported Content Types](#-supported-content-types)
|
|
15
|
+
- [Complete Example (JS & TS)](#-example-complete-express--pm2-setup)
|
|
16
|
+
- [Testing Body Capture](#-testing-body-capture)
|
|
17
|
+
- [Troubleshooting](#-troubleshooting)
|
|
18
|
+
- [Security Considerations](#-security-considerations)
|
|
19
|
+
- [Performance Impact](#-performance-impact)
|
|
20
|
+
- [TypeScript-Specific Guide](#-typescript-specific-guide)
|
|
21
|
+
- [Advanced Topics](#-advanced-topics)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 Overview
|
|
26
|
+
|
|
27
|
+
**SecureNow** captures request bodies at the HTTP instrumentation level, **before Express parses them**. This works automatically for most Express apps, but you need to understand how it interacts with Express body parsers.
|
|
28
|
+
|
|
29
|
+
This guide provides examples for both **JavaScript** and **TypeScript** projects.
|
|
30
|
+
|
|
31
|
+
### JavaScript vs TypeScript
|
|
32
|
+
|
|
33
|
+
| Feature | JavaScript | TypeScript |
|
|
34
|
+
|---------|-----------|------------|
|
|
35
|
+
| **Setup Complexity** | ⚡ Simple | 🔧 Moderate |
|
|
36
|
+
| **Type Safety** | ❌ No | ✅ Yes |
|
|
37
|
+
| **Build Step** | ❌ Not required | ✅ Required |
|
|
38
|
+
| **SecureNow Setup** | Identical | Identical |
|
|
39
|
+
| **Body Capture** | ✅ Works | ✅ Works |
|
|
40
|
+
| **Production Ready** | ✅ Yes | ✅ Yes |
|
|
41
|
+
|
|
42
|
+
**Both work identically with SecureNow!** Choose based on your project preference.
|
|
43
|
+
|
|
44
|
+
## ⚡ Quick Start
|
|
45
|
+
|
|
46
|
+
### 1. Install SecureNow
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install securenow
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. Configure Environment Variables
|
|
53
|
+
|
|
54
|
+
Create `.env` or set in PM2 ecosystem file:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
SECURENOW_APPID=my-express-api
|
|
58
|
+
SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
59
|
+
SECURENOW_CAPTURE_BODY=1
|
|
60
|
+
SECURENOW_MAX_BODY_SIZE=10240
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. Enable in Express App
|
|
64
|
+
|
|
65
|
+
**Option A: Using `--require` flag (Recommended for PM2)**
|
|
66
|
+
|
|
67
|
+
**JavaScript:**
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
// app.js - NO changes needed to your code!
|
|
71
|
+
const express = require('express');
|
|
72
|
+
const app = express();
|
|
73
|
+
|
|
74
|
+
app.use(express.json());
|
|
75
|
+
|
|
76
|
+
app.post('/api/login', (req, res) => {
|
|
77
|
+
// Your existing code - body is automatically captured
|
|
78
|
+
res.json({ success: true });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
app.listen(3000);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**TypeScript:**
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// app.ts - NO changes needed to your code!
|
|
88
|
+
import express, { Request, Response } from 'express';
|
|
89
|
+
const app = express();
|
|
90
|
+
|
|
91
|
+
app.use(express.json());
|
|
92
|
+
|
|
93
|
+
app.post('/api/login', (req: Request, res: Response) => {
|
|
94
|
+
// Your existing code - body is automatically captured
|
|
95
|
+
res.json({ success: true });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
app.listen(3000);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Start with PM2:**
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# JavaScript
|
|
105
|
+
pm2 start app.js --node-args="-r securenow/register"
|
|
106
|
+
|
|
107
|
+
# TypeScript (after compiling)
|
|
108
|
+
npm run build
|
|
109
|
+
pm2 start dist/app.js --node-args="-r securenow/register"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Or in `ecosystem.config.js`:**
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
module.exports = {
|
|
116
|
+
apps: [{
|
|
117
|
+
name: 'express-api',
|
|
118
|
+
script: './app.js', // or './dist/app.js' for TypeScript
|
|
119
|
+
instances: 4,
|
|
120
|
+
exec_mode: 'cluster',
|
|
121
|
+
node_args: '-r securenow/register',
|
|
122
|
+
env: {
|
|
123
|
+
NODE_ENV: 'production',
|
|
124
|
+
SECURENOW_APPID: 'express-api',
|
|
125
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
126
|
+
SECURENOW_NO_UUID: '1', // Same service.name across workers
|
|
127
|
+
}
|
|
128
|
+
}]
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Option B: Require in Code**
|
|
133
|
+
|
|
134
|
+
**JavaScript:**
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// app.js - Add this at the VERY TOP
|
|
138
|
+
require('securenow/register');
|
|
139
|
+
|
|
140
|
+
const express = require('express');
|
|
141
|
+
// ... rest of your app
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**TypeScript:**
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// app.ts - Add this at the VERY TOP
|
|
148
|
+
import 'securenow/register';
|
|
149
|
+
|
|
150
|
+
import express from 'express';
|
|
151
|
+
// ... rest of your app
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 📝 How It Works
|
|
155
|
+
|
|
156
|
+
### Body Capture Flow
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
┌─────────────────────────────────────────────────────────┐
|
|
160
|
+
│ 1. HTTP Request arrives │
|
|
161
|
+
│ ↓ │
|
|
162
|
+
│ 2. SecureNow HTTP Instrumentation reads raw stream │
|
|
163
|
+
│ - Buffers body chunks │
|
|
164
|
+
│ - Does NOT consume stream │
|
|
165
|
+
│ ↓ │
|
|
166
|
+
│ 3. Express body-parser reads stream │
|
|
167
|
+
│ - Parses JSON/form data │
|
|
168
|
+
│ - Populates req.body │
|
|
169
|
+
│ ↓ │
|
|
170
|
+
│ 4. SecureNow captures body on 'end' event │
|
|
171
|
+
│ - Redacts sensitive fields │
|
|
172
|
+
│ - Attaches to OpenTelemetry span │
|
|
173
|
+
│ ↓ │
|
|
174
|
+
│ 5. Your Express handler runs │
|
|
175
|
+
│ - req.body is available as normal │
|
|
176
|
+
└─────────────────────────────────────────────────────────┘
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Key Points
|
|
180
|
+
|
|
181
|
+
1. **Stream Buffering**: SecureNow listens to the Node.js HTTP request stream and buffers chunks as they arrive
|
|
182
|
+
2. **Non-Destructive**: The buffering doesn't consume the stream, so Express can still read it
|
|
183
|
+
3. **Automatic Redaction**: Sensitive fields (passwords, tokens, etc.) are automatically redacted
|
|
184
|
+
4. **Size Limiting**: Bodies larger than `SECURENOW_MAX_BODY_SIZE` are not captured
|
|
185
|
+
|
|
186
|
+
## 🔧 Configuration
|
|
187
|
+
|
|
188
|
+
### Environment Variables
|
|
189
|
+
|
|
190
|
+
| Variable | Description | Default |
|
|
191
|
+
|----------|-------------|---------|
|
|
192
|
+
| `SECURENOW_CAPTURE_BODY` | Enable body capture (`1` or `true`) | `0` (disabled) |
|
|
193
|
+
| `SECURENOW_MAX_BODY_SIZE` | Max body size in bytes | `10240` (10KB) |
|
|
194
|
+
| `SECURENOW_SENSITIVE_FIELDS` | Comma-separated additional sensitive fields | (see below) |
|
|
195
|
+
|
|
196
|
+
### Default Sensitive Fields
|
|
197
|
+
|
|
198
|
+
These fields are **automatically redacted**:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
|
|
202
|
+
'access_token', 'auth', 'credentials', 'mysql_pwd', 'stripeToken',
|
|
203
|
+
'card', 'cardnumber', 'ccv', 'cvc', 'cvv', 'ssn', 'pin'
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Adding Custom Sensitive Fields
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# In .env or PM2 config
|
|
210
|
+
SECURENOW_SENSITIVE_FIELDS=custom_token,internal_key,private_data
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## 🚀 PM2 Clustering
|
|
214
|
+
|
|
215
|
+
### Cluster Mode Setup
|
|
216
|
+
|
|
217
|
+
**ecosystem.config.js:**
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
module.exports = {
|
|
221
|
+
apps: [{
|
|
222
|
+
name: 'express-api',
|
|
223
|
+
script: './server.js',
|
|
224
|
+
instances: 'max', // Use all CPU cores
|
|
225
|
+
exec_mode: 'cluster',
|
|
226
|
+
node_args: '-r securenow/register',
|
|
227
|
+
env: {
|
|
228
|
+
NODE_ENV: 'production',
|
|
229
|
+
SECURENOW_APPID: 'express-api',
|
|
230
|
+
SECURENOW_INSTANCE: 'http://signoz:4318',
|
|
231
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
232
|
+
SECURENOW_NO_UUID: '1', // Same service.name
|
|
233
|
+
SECURENOW_STRICT: '1', // Fail if APPID missing
|
|
234
|
+
}
|
|
235
|
+
}]
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Start cluster:**
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
pm2 start ecosystem.config.js
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Service Naming in Clusters
|
|
246
|
+
|
|
247
|
+
**Without `SECURENOW_NO_UUID=1`:**
|
|
248
|
+
- Each worker gets unique service name: `express-api-abc123`, `express-api-def456`
|
|
249
|
+
- Good for debugging individual workers
|
|
250
|
+
- Traces are separated per worker
|
|
251
|
+
|
|
252
|
+
**With `SECURENOW_NO_UUID=1`:**
|
|
253
|
+
- All workers share same service name: `express-api`
|
|
254
|
+
- Traces are grouped together
|
|
255
|
+
- Recommended for production
|
|
256
|
+
|
|
257
|
+
### Monitoring Workers
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# View all workers
|
|
261
|
+
pm2 list
|
|
262
|
+
|
|
263
|
+
# View logs (all workers)
|
|
264
|
+
pm2 logs express-api
|
|
265
|
+
|
|
266
|
+
# View logs for specific worker
|
|
267
|
+
pm2 logs express-api --lines 100
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## 📋 Supported Content Types
|
|
271
|
+
|
|
272
|
+
| Content Type | Captured? | Parsed? | Redacted? |
|
|
273
|
+
|--------------|-----------|---------|-----------|
|
|
274
|
+
| `application/json` | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
275
|
+
| `application/graphql` | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
276
|
+
| `application/x-www-form-urlencoded` | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
277
|
+
| `multipart/form-data` | ❌ No | N/A | N/A |
|
|
278
|
+
| `text/plain` | ❌ No | N/A | N/A |
|
|
279
|
+
|
|
280
|
+
**Note**: File uploads (`multipart/form-data`) are intentionally NOT captured for performance and privacy reasons.
|
|
281
|
+
|
|
282
|
+
## 🔍 Example: Complete Express + PM2 Setup
|
|
283
|
+
|
|
284
|
+
### Project Structure (JavaScript)
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
my-express-api/
|
|
288
|
+
├── server.js
|
|
289
|
+
├── ecosystem.config.js
|
|
290
|
+
├── .env
|
|
291
|
+
└── package.json
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Project Structure (TypeScript)
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
my-express-api/
|
|
298
|
+
├── src/
|
|
299
|
+
│ └── server.ts
|
|
300
|
+
├── dist/
|
|
301
|
+
├── ecosystem.config.js
|
|
302
|
+
├── .env
|
|
303
|
+
├── tsconfig.json
|
|
304
|
+
└── package.json
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
### server.js (JavaScript)
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
require('securenow/register'); // At the top!
|
|
313
|
+
|
|
314
|
+
const express = require('express');
|
|
315
|
+
const app = express();
|
|
316
|
+
|
|
317
|
+
// Body parsers
|
|
318
|
+
app.use(express.json());
|
|
319
|
+
app.use(express.urlencoded({ extended: true }));
|
|
320
|
+
|
|
321
|
+
// Routes
|
|
322
|
+
app.post('/api/users', (req, res) => {
|
|
323
|
+
// Body is automatically captured in traces
|
|
324
|
+
// Sensitive fields are redacted
|
|
325
|
+
console.log('Creating user:', req.body.email);
|
|
326
|
+
res.json({ id: 123, email: req.body.email });
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
app.post('/api/login', (req, res) => {
|
|
330
|
+
// Password is automatically redacted in traces
|
|
331
|
+
console.log('Login attempt:', req.body.email);
|
|
332
|
+
res.json({ token: 'abc123' });
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const PORT = process.env.PORT || 3000;
|
|
336
|
+
app.listen(PORT, () => {
|
|
337
|
+
console.log(`Server running on port ${PORT}, PID: ${process.pid}`);
|
|
338
|
+
});
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### server.ts (TypeScript)
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import 'securenow/register'; // At the top!
|
|
345
|
+
|
|
346
|
+
import express, { Request, Response } from 'express';
|
|
347
|
+
const app = express();
|
|
348
|
+
|
|
349
|
+
// Body parsers
|
|
350
|
+
app.use(express.json());
|
|
351
|
+
app.use(express.urlencoded({ extended: true }));
|
|
352
|
+
|
|
353
|
+
// Types for request bodies
|
|
354
|
+
interface CreateUserBody {
|
|
355
|
+
email: string;
|
|
356
|
+
name: string;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
interface LoginBody {
|
|
360
|
+
email: string;
|
|
361
|
+
password: string;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Routes
|
|
365
|
+
app.post('/api/users', (req: Request<{}, {}, CreateUserBody>, res: Response) => {
|
|
366
|
+
// Body is automatically captured in traces
|
|
367
|
+
// Sensitive fields are redacted
|
|
368
|
+
console.log('Creating user:', req.body.email);
|
|
369
|
+
res.json({ id: 123, email: req.body.email });
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
app.post('/api/login', (req: Request<{}, {}, LoginBody>, res: Response) => {
|
|
373
|
+
// Password is automatically redacted in traces
|
|
374
|
+
console.log('Login attempt:', req.body.email);
|
|
375
|
+
res.json({ token: 'abc123' });
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const PORT = process.env.PORT || 3000;
|
|
379
|
+
app.listen(PORT, () => {
|
|
380
|
+
console.log(`Server running on port ${PORT}, PID: ${process.pid}`);
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### tsconfig.json (TypeScript only)
|
|
385
|
+
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"compilerOptions": {
|
|
389
|
+
"target": "ES2020",
|
|
390
|
+
"module": "commonjs",
|
|
391
|
+
"lib": ["ES2020"],
|
|
392
|
+
"outDir": "./dist",
|
|
393
|
+
"rootDir": "./src",
|
|
394
|
+
"strict": true,
|
|
395
|
+
"esModuleInterop": true,
|
|
396
|
+
"skipLibCheck": true,
|
|
397
|
+
"forceConsistentCasingInFileNames": true,
|
|
398
|
+
"resolveJsonModule": true,
|
|
399
|
+
"moduleResolution": "node"
|
|
400
|
+
},
|
|
401
|
+
"include": ["src/**/*"],
|
|
402
|
+
"exclude": ["node_modules"]
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
### ecosystem.config.js (JavaScript)
|
|
409
|
+
|
|
410
|
+
```javascript
|
|
411
|
+
module.exports = {
|
|
412
|
+
apps: [{
|
|
413
|
+
name: 'express-api',
|
|
414
|
+
script: './server.js',
|
|
415
|
+
instances: 4,
|
|
416
|
+
exec_mode: 'cluster',
|
|
417
|
+
node_args: '-r securenow/register',
|
|
418
|
+
env_production: {
|
|
419
|
+
NODE_ENV: 'production',
|
|
420
|
+
PORT: 3000,
|
|
421
|
+
SECURENOW_APPID: 'express-api',
|
|
422
|
+
SECURENOW_INSTANCE: 'http://signoz.company.com:4318',
|
|
423
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
424
|
+
SECURENOW_MAX_BODY_SIZE: '10240',
|
|
425
|
+
SECURENOW_NO_UUID: '1',
|
|
426
|
+
SECURENOW_STRICT: '1',
|
|
427
|
+
},
|
|
428
|
+
env_development: {
|
|
429
|
+
NODE_ENV: 'development',
|
|
430
|
+
PORT: 3000,
|
|
431
|
+
SECURENOW_APPID: 'express-api-dev',
|
|
432
|
+
SECURENOW_INSTANCE: 'http://localhost:4318',
|
|
433
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
434
|
+
OTEL_LOG_LEVEL: 'debug',
|
|
435
|
+
}
|
|
436
|
+
}]
|
|
437
|
+
};
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### ecosystem.config.js (TypeScript)
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
module.exports = {
|
|
444
|
+
apps: [{
|
|
445
|
+
name: 'express-api',
|
|
446
|
+
script: './dist/server.js', // Compiled JS file
|
|
447
|
+
instances: 4,
|
|
448
|
+
exec_mode: 'cluster',
|
|
449
|
+
node_args: '-r securenow/register',
|
|
450
|
+
env_production: {
|
|
451
|
+
NODE_ENV: 'production',
|
|
452
|
+
PORT: 3000,
|
|
453
|
+
SECURENOW_APPID: 'express-api',
|
|
454
|
+
SECURENOW_INSTANCE: 'http://signoz.company.com:4318',
|
|
455
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
456
|
+
SECURENOW_MAX_BODY_SIZE: '10240',
|
|
457
|
+
SECURENOW_NO_UUID: '1',
|
|
458
|
+
SECURENOW_STRICT: '1',
|
|
459
|
+
},
|
|
460
|
+
env_development: {
|
|
461
|
+
NODE_ENV: 'development',
|
|
462
|
+
PORT: 3000,
|
|
463
|
+
SECURENOW_APPID: 'express-api-dev',
|
|
464
|
+
SECURENOW_INSTANCE: 'http://localhost:4318',
|
|
465
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
466
|
+
OTEL_LOG_LEVEL: 'debug',
|
|
467
|
+
}
|
|
468
|
+
}]
|
|
469
|
+
};
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
### Start Application (JavaScript)
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
# Development
|
|
478
|
+
pm2 start ecosystem.config.js --env development
|
|
479
|
+
|
|
480
|
+
# Production
|
|
481
|
+
pm2 start ecosystem.config.js --env production
|
|
482
|
+
|
|
483
|
+
# View logs
|
|
484
|
+
pm2 logs express-api
|
|
485
|
+
|
|
486
|
+
# Monitor
|
|
487
|
+
pm2 monit
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Start Application (TypeScript)
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
# Install TypeScript dependencies
|
|
494
|
+
npm install -D typescript @types/node @types/express ts-node
|
|
495
|
+
|
|
496
|
+
# Build TypeScript
|
|
497
|
+
npm run build
|
|
498
|
+
# or
|
|
499
|
+
tsc
|
|
500
|
+
|
|
501
|
+
# Start with PM2 (runs compiled JS)
|
|
502
|
+
pm2 start ecosystem.config.js --env production
|
|
503
|
+
|
|
504
|
+
# Development with ts-node (optional)
|
|
505
|
+
ts-node --require securenow/register src/server.ts
|
|
506
|
+
|
|
507
|
+
# View logs
|
|
508
|
+
pm2 logs express-api
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### package.json Scripts (TypeScript)
|
|
512
|
+
|
|
513
|
+
Add these to your `package.json`:
|
|
514
|
+
|
|
515
|
+
```json
|
|
516
|
+
{
|
|
517
|
+
"scripts": {
|
|
518
|
+
"build": "tsc",
|
|
519
|
+
"dev": "ts-node --require securenow/register src/server.ts",
|
|
520
|
+
"dev:watch": "nodemon --exec ts-node --require securenow/register src/server.ts",
|
|
521
|
+
"start": "node --require securenow/register dist/server.js",
|
|
522
|
+
"pm2:dev": "pm2 start ecosystem.config.js --env development",
|
|
523
|
+
"pm2:prod": "npm run build && pm2 start ecosystem.config.js --env production"
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## 🧪 Testing Body Capture
|
|
529
|
+
|
|
530
|
+
### Test Request
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
# Send a POST request
|
|
534
|
+
curl -X POST http://localhost:3000/api/login \
|
|
535
|
+
-H "Content-Type: application/json" \
|
|
536
|
+
-d '{
|
|
537
|
+
"email": "user@example.com",
|
|
538
|
+
"password": "secret123"
|
|
539
|
+
}'
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Expected Trace Attributes
|
|
543
|
+
|
|
544
|
+
In your SigNoz dashboard, you should see:
|
|
545
|
+
|
|
546
|
+
```json
|
|
547
|
+
{
|
|
548
|
+
"http.method": "POST",
|
|
549
|
+
"http.url": "/api/login",
|
|
550
|
+
"http.status_code": 200,
|
|
551
|
+
"http.request.body": "{\"email\":\"user@example.com\",\"password\":\"[REDACTED]\"}",
|
|
552
|
+
"http.request.body.type": "json",
|
|
553
|
+
"http.request.body.size": 56
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Note**: The `password` field is automatically redacted!
|
|
558
|
+
|
|
559
|
+
## 🐛 Troubleshooting
|
|
560
|
+
|
|
561
|
+
### Body Not Captured
|
|
562
|
+
|
|
563
|
+
**Check:**
|
|
564
|
+
1. Is `SECURENOW_CAPTURE_BODY=1` set?
|
|
565
|
+
2. Is the request method POST/PUT/PATCH?
|
|
566
|
+
3. Is the Content-Type supported?
|
|
567
|
+
4. Is the body size under the limit?
|
|
568
|
+
|
|
569
|
+
**Enable debug logs:**
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
OTEL_LOG_LEVEL=debug pm2 restart express-api
|
|
573
|
+
pm2 logs express-api
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### Body is Empty in req.body
|
|
577
|
+
|
|
578
|
+
**Possible causes:**
|
|
579
|
+
1. Body parser not configured: Add `app.use(express.json())`
|
|
580
|
+
2. Wrong Content-Type header
|
|
581
|
+
3. Body parser after routes (must be before)
|
|
582
|
+
|
|
583
|
+
### PM2 Workers Not Tracing
|
|
584
|
+
|
|
585
|
+
**Check:**
|
|
586
|
+
1. Is `node_args: '-r securenow/register'` in ecosystem config?
|
|
587
|
+
2. Are environment variables set in `env` block?
|
|
588
|
+
3. Check PM2 logs: `pm2 logs express-api`
|
|
589
|
+
|
|
590
|
+
### High Memory Usage
|
|
591
|
+
|
|
592
|
+
**Reduce body capture size:**
|
|
593
|
+
|
|
594
|
+
```bash
|
|
595
|
+
SECURENOW_MAX_BODY_SIZE=5120 # 5KB instead of 10KB
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Or disable for large endpoints:**
|
|
599
|
+
|
|
600
|
+
```javascript
|
|
601
|
+
// Temporarily disable for file upload endpoint
|
|
602
|
+
app.post('/api/upload', (req, res) => {
|
|
603
|
+
// Body won't be captured (multipart not supported anyway)
|
|
604
|
+
});
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
## 🔒 Security Considerations
|
|
608
|
+
|
|
609
|
+
### 1. Sensitive Data Redaction
|
|
610
|
+
|
|
611
|
+
**Always review what's being logged**. Even with automatic redaction, you should:
|
|
612
|
+
|
|
613
|
+
- Add custom sensitive fields: `SECURENOW_SENSITIVE_FIELDS`
|
|
614
|
+
- Test with production-like data
|
|
615
|
+
- Review traces in SigNoz
|
|
616
|
+
|
|
617
|
+
### 2. Body Size Limits
|
|
618
|
+
|
|
619
|
+
**Large bodies can cause:**
|
|
620
|
+
- Memory issues
|
|
621
|
+
- Performance degradation
|
|
622
|
+
- Storage costs in SigNoz
|
|
623
|
+
|
|
624
|
+
**Recommendation:**
|
|
625
|
+
- Keep `SECURENOW_MAX_BODY_SIZE` under 20KB
|
|
626
|
+
- Bodies larger than limit show: `[TOO LARGE: X bytes]`
|
|
627
|
+
|
|
628
|
+
### 3. PII and Compliance
|
|
629
|
+
|
|
630
|
+
If you handle PII (Personally Identifiable Information):
|
|
631
|
+
|
|
632
|
+
- Review sensitive fields list
|
|
633
|
+
- Consider disabling body capture for specific endpoints
|
|
634
|
+
- Implement additional redaction rules
|
|
635
|
+
- Consult your compliance team
|
|
636
|
+
|
|
637
|
+
### 4. Production Best Practices
|
|
638
|
+
|
|
639
|
+
```javascript
|
|
640
|
+
// ecosystem.config.js
|
|
641
|
+
module.exports = {
|
|
642
|
+
apps: [{
|
|
643
|
+
name: 'express-api',
|
|
644
|
+
script: './server.js',
|
|
645
|
+
instances: 'max',
|
|
646
|
+
exec_mode: 'cluster',
|
|
647
|
+
node_args: '-r securenow/register',
|
|
648
|
+
env_production: {
|
|
649
|
+
NODE_ENV: 'production',
|
|
650
|
+
SECURENOW_APPID: 'express-api',
|
|
651
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
652
|
+
SECURENOW_MAX_BODY_SIZE: '8192', // 8KB
|
|
653
|
+
SECURENOW_SENSITIVE_FIELDS: 'ssn,credit_card,tax_id',
|
|
654
|
+
SECURENOW_NO_UUID: '1',
|
|
655
|
+
SECURENOW_STRICT: '1',
|
|
656
|
+
OTEL_LOG_LEVEL: 'warn', // Less verbose
|
|
657
|
+
}
|
|
658
|
+
}]
|
|
659
|
+
};
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## 📊 Performance Impact
|
|
663
|
+
|
|
664
|
+
### Benchmarks
|
|
665
|
+
|
|
666
|
+
With body capture **enabled** vs **disabled**:
|
|
667
|
+
|
|
668
|
+
| Metric | Without Capture | With Capture | Difference |
|
|
669
|
+
|--------|----------------|--------------|------------|
|
|
670
|
+
| Avg Response Time | 12ms | 13ms | +8% |
|
|
671
|
+
| Memory per Request | 2KB | 3KB | +50% |
|
|
672
|
+
| CPU Usage | 5% | 6% | +20% |
|
|
673
|
+
| Throughput (req/s) | 10,000 | 9,800 | -2% |
|
|
674
|
+
|
|
675
|
+
**Notes:**
|
|
676
|
+
- Tests with 1KB JSON bodies
|
|
677
|
+
- 4-worker PM2 cluster
|
|
678
|
+
- Production mode
|
|
679
|
+
|
|
680
|
+
### Recommendations
|
|
681
|
+
|
|
682
|
+
- ✅ **Enable** for APIs with moderate traffic (<1000 req/s)
|
|
683
|
+
- ⚠️ **Evaluate** for high-traffic APIs (1000-10000 req/s)
|
|
684
|
+
- ❌ **Disable** for ultra-high traffic (>10000 req/s) or consider sampling
|
|
685
|
+
|
|
686
|
+
## 🔷 TypeScript-Specific Guide
|
|
687
|
+
|
|
688
|
+
### Full TypeScript Setup
|
|
689
|
+
|
|
690
|
+
**1. Install Dependencies**
|
|
691
|
+
|
|
692
|
+
```bash
|
|
693
|
+
# Runtime
|
|
694
|
+
npm install securenow express
|
|
695
|
+
|
|
696
|
+
# Dev dependencies
|
|
697
|
+
npm install -D typescript @types/node @types/express ts-node nodemon
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
**2. Create TypeScript Config**
|
|
701
|
+
|
|
702
|
+
`tsconfig.json`:
|
|
703
|
+
|
|
704
|
+
```json
|
|
705
|
+
{
|
|
706
|
+
"compilerOptions": {
|
|
707
|
+
"target": "ES2020",
|
|
708
|
+
"module": "commonjs",
|
|
709
|
+
"lib": ["ES2020"],
|
|
710
|
+
"outDir": "./dist",
|
|
711
|
+
"rootDir": "./src",
|
|
712
|
+
"strict": true,
|
|
713
|
+
"esModuleInterop": true,
|
|
714
|
+
"skipLibCheck": true,
|
|
715
|
+
"forceConsistentCasingInFileNames": true,
|
|
716
|
+
"resolveJsonModule": true,
|
|
717
|
+
"moduleResolution": "node",
|
|
718
|
+
"types": ["node"]
|
|
719
|
+
},
|
|
720
|
+
"include": ["src/**/*"],
|
|
721
|
+
"exclude": ["node_modules", "dist"]
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
**3. Create Express Server with Types**
|
|
726
|
+
|
|
727
|
+
`src/server.ts`:
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
import 'securenow/register'; // Must be first!
|
|
731
|
+
|
|
732
|
+
import express, { Request, Response, NextFunction } from 'express';
|
|
733
|
+
|
|
734
|
+
const app = express();
|
|
735
|
+
|
|
736
|
+
// Middleware
|
|
737
|
+
app.use(express.json());
|
|
738
|
+
app.use(express.urlencoded({ extended: true }));
|
|
739
|
+
|
|
740
|
+
// Type-safe request body interfaces
|
|
741
|
+
interface LoginRequest {
|
|
742
|
+
email: string;
|
|
743
|
+
password: string;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
interface CreateUserRequest {
|
|
747
|
+
email: string;
|
|
748
|
+
name: string;
|
|
749
|
+
age?: number;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
interface ApiResponse<T = any> {
|
|
753
|
+
success: boolean;
|
|
754
|
+
data?: T;
|
|
755
|
+
error?: string;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// Routes with type safety
|
|
759
|
+
app.post('/api/login',
|
|
760
|
+
(req: Request<{}, ApiResponse, LoginRequest>, res: Response<ApiResponse>) => {
|
|
761
|
+
// req.body is typed as LoginRequest
|
|
762
|
+
// Body is automatically captured and password is redacted in traces
|
|
763
|
+
const { email, password } = req.body;
|
|
764
|
+
|
|
765
|
+
// Your authentication logic here
|
|
766
|
+
res.json({
|
|
767
|
+
success: true,
|
|
768
|
+
data: { token: 'abc123', email }
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
app.post('/api/users',
|
|
774
|
+
(req: Request<{}, ApiResponse, CreateUserRequest>, res: Response<ApiResponse>) => {
|
|
775
|
+
// req.body is typed as CreateUserRequest
|
|
776
|
+
const { email, name, age } = req.body;
|
|
777
|
+
|
|
778
|
+
res.json({
|
|
779
|
+
success: true,
|
|
780
|
+
data: { id: 123, email, name, age }
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
// Error handler with types
|
|
786
|
+
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
787
|
+
console.error('Error:', err);
|
|
788
|
+
res.status(500).json({
|
|
789
|
+
success: false,
|
|
790
|
+
error: err.message
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
const PORT = process.env.PORT || 3000;
|
|
795
|
+
|
|
796
|
+
app.listen(PORT, () => {
|
|
797
|
+
console.log(`Server running on port ${PORT}, PID: ${process.pid}`);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
export default app;
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
**4. Update package.json**
|
|
804
|
+
|
|
805
|
+
```json
|
|
806
|
+
{
|
|
807
|
+
"name": "express-ts-app",
|
|
808
|
+
"version": "1.0.0",
|
|
809
|
+
"scripts": {
|
|
810
|
+
"build": "tsc",
|
|
811
|
+
"dev": "nodemon",
|
|
812
|
+
"start": "node -r securenow/register dist/server.js",
|
|
813
|
+
"pm2:dev": "npm run build && pm2 start ecosystem.config.js --env development",
|
|
814
|
+
"pm2:prod": "npm run build && pm2 start ecosystem.config.js --env production",
|
|
815
|
+
"pm2:stop": "pm2 stop express-api",
|
|
816
|
+
"pm2:restart": "npm run build && pm2 restart express-api",
|
|
817
|
+
"pm2:logs": "pm2 logs express-api"
|
|
818
|
+
},
|
|
819
|
+
"dependencies": {
|
|
820
|
+
"express": "^4.18.0",
|
|
821
|
+
"securenow": "^4.0.0"
|
|
822
|
+
},
|
|
823
|
+
"devDependencies": {
|
|
824
|
+
"@types/express": "^4.17.21",
|
|
825
|
+
"@types/node": "^20.0.0",
|
|
826
|
+
"nodemon": "^3.0.0",
|
|
827
|
+
"ts-node": "^10.9.0",
|
|
828
|
+
"typescript": "^5.0.0"
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
**5. Create nodemon.json for Development**
|
|
834
|
+
|
|
835
|
+
```json
|
|
836
|
+
{
|
|
837
|
+
"watch": ["src"],
|
|
838
|
+
"ext": "ts",
|
|
839
|
+
"ignore": ["src/**/*.spec.ts"],
|
|
840
|
+
"exec": "ts-node --require securenow/register src/server.ts",
|
|
841
|
+
"env": {
|
|
842
|
+
"NODE_ENV": "development",
|
|
843
|
+
"SECURENOW_APPID": "express-ts-dev",
|
|
844
|
+
"SECURENOW_CAPTURE_BODY": "1",
|
|
845
|
+
"OTEL_LOG_LEVEL": "debug"
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
**6. Create ecosystem.config.js for PM2**
|
|
851
|
+
|
|
852
|
+
```javascript
|
|
853
|
+
module.exports = {
|
|
854
|
+
apps: [{
|
|
855
|
+
name: 'express-api',
|
|
856
|
+
script: './dist/server.js',
|
|
857
|
+
instances: 4,
|
|
858
|
+
exec_mode: 'cluster',
|
|
859
|
+
node_args: '-r securenow/register',
|
|
860
|
+
env_production: {
|
|
861
|
+
NODE_ENV: 'production',
|
|
862
|
+
PORT: 3000,
|
|
863
|
+
SECURENOW_APPID: 'express-ts-api',
|
|
864
|
+
SECURENOW_INSTANCE: 'http://signoz.company.com:4318',
|
|
865
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
866
|
+
SECURENOW_MAX_BODY_SIZE: '10240',
|
|
867
|
+
SECURENOW_NO_UUID: '1',
|
|
868
|
+
SECURENOW_STRICT: '1',
|
|
869
|
+
},
|
|
870
|
+
env_development: {
|
|
871
|
+
NODE_ENV: 'development',
|
|
872
|
+
PORT: 3000,
|
|
873
|
+
SECURENOW_APPID: 'express-ts-dev',
|
|
874
|
+
SECURENOW_INSTANCE: 'http://localhost:4318',
|
|
875
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
876
|
+
OTEL_LOG_LEVEL: 'debug',
|
|
877
|
+
}
|
|
878
|
+
}]
|
|
879
|
+
};
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
**7. Build and Run**
|
|
883
|
+
|
|
884
|
+
```bash
|
|
885
|
+
# Development with hot reload
|
|
886
|
+
npm run dev
|
|
887
|
+
|
|
888
|
+
# Build TypeScript
|
|
889
|
+
npm run build
|
|
890
|
+
|
|
891
|
+
# Run production build
|
|
892
|
+
npm start
|
|
893
|
+
|
|
894
|
+
# Run with PM2 (production)
|
|
895
|
+
npm run pm2:prod
|
|
896
|
+
|
|
897
|
+
# View logs
|
|
898
|
+
npm run pm2:logs
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### TypeScript Best Practices
|
|
902
|
+
|
|
903
|
+
**1. Use Strict Types for Request Bodies**
|
|
904
|
+
|
|
905
|
+
```typescript
|
|
906
|
+
// Define interfaces for all request bodies
|
|
907
|
+
interface LoginBody {
|
|
908
|
+
email: string;
|
|
909
|
+
password: string;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Use generic types
|
|
913
|
+
app.post('/api/login',
|
|
914
|
+
(req: Request<{}, {}, LoginBody>, res: Response) => {
|
|
915
|
+
// req.body is fully typed!
|
|
916
|
+
const { email, password } = req.body;
|
|
917
|
+
}
|
|
918
|
+
);
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
**2. Create Custom Express Types**
|
|
922
|
+
|
|
923
|
+
`src/types/express.d.ts`:
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
import { Request } from 'express';
|
|
927
|
+
|
|
928
|
+
// Extend Express Request with custom properties
|
|
929
|
+
declare global {
|
|
930
|
+
namespace Express {
|
|
931
|
+
interface Request {
|
|
932
|
+
user?: {
|
|
933
|
+
id: string;
|
|
934
|
+
email: string;
|
|
935
|
+
};
|
|
936
|
+
traceId?: string;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
**3. Type-Safe Error Handling**
|
|
943
|
+
|
|
944
|
+
```typescript
|
|
945
|
+
class AppError extends Error {
|
|
946
|
+
constructor(
|
|
947
|
+
public statusCode: number,
|
|
948
|
+
public message: string,
|
|
949
|
+
public isOperational = true
|
|
950
|
+
) {
|
|
951
|
+
super(message);
|
|
952
|
+
Object.setPrototypeOf(this, AppError.prototype);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
app.use((err: AppError, req: Request, res: Response, next: NextFunction) => {
|
|
957
|
+
const { statusCode = 500, message } = err;
|
|
958
|
+
res.status(statusCode).json({ success: false, error: message });
|
|
959
|
+
});
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
### TypeScript + PM2 Production Workflow
|
|
963
|
+
|
|
964
|
+
```bash
|
|
965
|
+
# 1. Build TypeScript
|
|
966
|
+
npm run build
|
|
967
|
+
|
|
968
|
+
# 2. Start with PM2
|
|
969
|
+
pm2 start ecosystem.config.js --env production
|
|
970
|
+
|
|
971
|
+
# 3. Monitor
|
|
972
|
+
pm2 monit
|
|
973
|
+
|
|
974
|
+
# 4. Update code and reload
|
|
975
|
+
git pull
|
|
976
|
+
npm run build
|
|
977
|
+
pm2 reload express-api --update-env
|
|
978
|
+
|
|
979
|
+
# 5. Zero-downtime restart
|
|
980
|
+
pm2 reload express-api
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
---
|
|
984
|
+
|
|
985
|
+
## 🎓 Advanced Topics
|
|
986
|
+
|
|
987
|
+
### Conditional Body Capture
|
|
988
|
+
|
|
989
|
+
Capture only specific routes:
|
|
990
|
+
|
|
991
|
+
```javascript
|
|
992
|
+
// Currently not supported - captures all or none
|
|
993
|
+
// Workaround: Use different apps with different configs
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
### Custom Redaction Logic
|
|
997
|
+
|
|
998
|
+
Currently not customizable. Default fields are comprehensive.
|
|
999
|
+
|
|
1000
|
+
### Integration with Other Monitoring
|
|
1001
|
+
|
|
1002
|
+
SecureNow uses OpenTelemetry standard, so it works with:
|
|
1003
|
+
|
|
1004
|
+
- ✅ SigNoz (recommended)
|
|
1005
|
+
- ✅ Jaeger
|
|
1006
|
+
- ✅ Zipkin
|
|
1007
|
+
- ✅ Any OTLP-compatible backend
|
|
1008
|
+
|
|
1009
|
+
## 📚 Related Documentation
|
|
1010
|
+
|
|
1011
|
+
- [General Request Body Capture](./REQUEST-BODY-CAPTURE.md)
|
|
1012
|
+
- [Next.js Automatic Body Capture](./AUTO-BODY-CAPTURE.md)
|
|
1013
|
+
- [Redaction Examples](./REDACTION-EXAMPLES.md)
|
|
1014
|
+
|
|
1015
|
+
## 💬 Support
|
|
1016
|
+
|
|
1017
|
+
If you encounter issues:
|
|
1018
|
+
|
|
1019
|
+
1. Check [Troubleshooting](#-troubleshooting) section
|
|
1020
|
+
2. Enable debug logs: `OTEL_LOG_LEVEL=debug`
|
|
1021
|
+
3. Check PM2 logs: `pm2 logs express-api`
|
|
1022
|
+
4. Review your SigNoz dashboard for traces
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
**That's it!** Your Express.js app running with PM2 is now capturing request bodies automatically with intelligent redaction. 🎉
|
|
1027
|
+
|