securenow 5.5.0 → 5.6.1
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/CONSUMING-APPS-GUIDE.md +411 -415
- package/NPM_README.md +1544 -1609
- package/README.md +9 -21
- package/cli/monitor.js +13 -1
- package/cli.js +1 -1
- package/console-instrumentation.js +6 -1
- package/docs/ALL-FRAMEWORKS-QUICKSTART.md +1282 -455
- package/docs/EXPRESS-SETUP-GUIDE.md +719 -720
- package/docs/LOGGING-GUIDE.md +701 -708
- package/docs/LOGGING-QUICKSTART.md +234 -239
- package/nextjs.js +19 -3
- package/package.json +1 -1
- package/tracing.d.ts +1 -0
- package/tracing.js +87 -1
|
@@ -1,239 +1,234 @@
|
|
|
1
|
-
# SecureNow Logging - Quick Start
|
|
2
|
-
|
|
3
|
-
Get logging set up in your Node.js app in under 2 minutes!
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
console.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
[securenow]
|
|
68
|
-
[securenow]
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## 5. View Logs in SecureNow
|
|
74
|
-
|
|
75
|
-
1. Open your SecureNow dashboard
|
|
76
|
-
2. Go to **Logs** section
|
|
77
|
-
3. Filter by `service.name = my-app`
|
|
78
|
-
4. See all your logs with automatic trace correlation!
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## Framework-Specific Examples
|
|
83
|
-
|
|
84
|
-
### Express.js
|
|
85
|
-
|
|
86
|
-
```javascript
|
|
87
|
-
// app.js
|
|
88
|
-
require('securenow/register');
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
require('
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
require('securenow/register');
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
**That's it!** 🎉 Your app is now sending logs to SecureNow.
|
|
238
|
-
|
|
239
|
-
Need help? Check the [full documentation](./LOGGING-GUIDE.md) or open an issue.
|
|
1
|
+
# SecureNow Logging - Quick Start
|
|
2
|
+
|
|
3
|
+
Get logging set up in your Node.js app in under 2 minutes!
|
|
4
|
+
|
|
5
|
+
**Since v5.6.0:** When `SECURENOW_LOGGING_ENABLED=1` is set, all `console.log` / `warn` / `error` / `info` / `debug` calls are automatically forwarded as OTLP log records. You only need `require('securenow/register')`—a separate `console-instrumentation` preload is no longer required.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install securenow
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. Configure Environment
|
|
18
|
+
|
|
19
|
+
Create `.env` file or export variables:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
23
|
+
SECURENOW_APPID=my-app
|
|
24
|
+
SECURENOW_INSTANCE=http://your-otlp-backend:4318
|
|
25
|
+
|
|
26
|
+
# For SecureNow / hosted OTLP (example):
|
|
27
|
+
# SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
28
|
+
# OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your-key>"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Add to Your App
|
|
34
|
+
|
|
35
|
+
**Option A: Automatic Console Logging (Easiest)**
|
|
36
|
+
|
|
37
|
+
Add this line at the top of your main file:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// app.js, index.js, server.js, or main.ts
|
|
41
|
+
require('securenow/register');
|
|
42
|
+
|
|
43
|
+
// That's it! Now use console normally
|
|
44
|
+
console.log('App started');
|
|
45
|
+
console.error('An error occurred', { userId: 123 });
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Option B: Use NODE_OPTIONS (No code changes)**
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
NODE_OPTIONS="-r securenow/register" node app.js
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 4. Run Your App
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node app.js
|
|
60
|
+
# or
|
|
61
|
+
npm start
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
You should see:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
[securenow] OTel SDK started → http://your-otlp-backend:4318/v1/traces
|
|
68
|
+
[securenow] 📋 Logging: ENABLED → http://your-otlp-backend:4318/v1/logs
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 5. View Logs in SecureNow
|
|
74
|
+
|
|
75
|
+
1. Open your SecureNow dashboard
|
|
76
|
+
2. Go to **Logs** section
|
|
77
|
+
3. Filter by `service.name = my-app`
|
|
78
|
+
4. See all your logs with automatic trace correlation!
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Framework-Specific Examples
|
|
83
|
+
|
|
84
|
+
### Express.js
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// app.js
|
|
88
|
+
require('securenow/register');
|
|
89
|
+
|
|
90
|
+
const express = require('express');
|
|
91
|
+
const app = express();
|
|
92
|
+
|
|
93
|
+
app.get('/', (req, res) => {
|
|
94
|
+
console.log('Request received');
|
|
95
|
+
res.send('Hello World');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
app.listen(3000);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Next.js
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// instrumentation.ts (in project root)
|
|
105
|
+
export async function register() {
|
|
106
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
107
|
+
process.env.SECURENOW_LOGGING_ENABLED = '1';
|
|
108
|
+
await import('securenow/register');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# .env.local
|
|
115
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
116
|
+
SECURENOW_APPID=my-nextjs-app
|
|
117
|
+
SECURENOW_INSTANCE=http://localhost:4318
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Fastify
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// server.js
|
|
124
|
+
require('securenow/register');
|
|
125
|
+
|
|
126
|
+
const fastify = require('fastify')();
|
|
127
|
+
|
|
128
|
+
fastify.get('/', async () => {
|
|
129
|
+
console.log('Route called');
|
|
130
|
+
return { hello: 'world' };
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
fastify.listen({ port: 3000 });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### NestJS
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// main.ts
|
|
140
|
+
require('securenow/register');
|
|
141
|
+
|
|
142
|
+
import { NestFactory } from '@nestjs/core';
|
|
143
|
+
import { AppModule } from './app.module';
|
|
144
|
+
|
|
145
|
+
async function bootstrap() {
|
|
146
|
+
const app = await NestFactory.create(AppModule);
|
|
147
|
+
console.log('App starting');
|
|
148
|
+
await app.listen(3000);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
bootstrap();
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Troubleshooting
|
|
157
|
+
|
|
158
|
+
**Logs not appearing?**
|
|
159
|
+
|
|
160
|
+
1. Check `SECURENOW_LOGGING_ENABLED=1` is set
|
|
161
|
+
2. Verify your OTLP / SecureNow endpoint is correct
|
|
162
|
+
3. Enable debug: `OTEL_LOG_LEVEL=debug`
|
|
163
|
+
|
|
164
|
+
**Console logs not forwarding?**
|
|
165
|
+
|
|
166
|
+
Load `securenow/register` before other app code so logging hooks run first:
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
// ✅ Correct
|
|
170
|
+
require('securenow/register'); // First
|
|
171
|
+
|
|
172
|
+
// ❌ Wrong
|
|
173
|
+
require('express');
|
|
174
|
+
require('securenow/register'); // Too late!
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## What Gets Logged?
|
|
180
|
+
|
|
181
|
+
All standard console methods:
|
|
182
|
+
|
|
183
|
+
- `console.log()` → INFO
|
|
184
|
+
- `console.info()` → INFO
|
|
185
|
+
- `console.warn()` → WARN
|
|
186
|
+
- `console.error()` → ERROR
|
|
187
|
+
- `console.debug()` → DEBUG
|
|
188
|
+
|
|
189
|
+
**Structured logging example:**
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
console.log('User logged in', {
|
|
193
|
+
userId: 123,
|
|
194
|
+
email: 'user@example.com',
|
|
195
|
+
ip: '192.168.1.1'
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
This creates a log with attributes you can filter/search in SecureNow!
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Advanced Usage
|
|
204
|
+
|
|
205
|
+
**Direct Logger API:**
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
const { getLogger } = require('securenow/tracing');
|
|
209
|
+
const logger = getLogger('my-module', '1.0.0');
|
|
210
|
+
|
|
211
|
+
logger.emit({
|
|
212
|
+
severityNumber: 9,
|
|
213
|
+
severityText: 'INFO',
|
|
214
|
+
body: 'Custom log message',
|
|
215
|
+
attributes: {
|
|
216
|
+
customField: 'value',
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Next Steps
|
|
224
|
+
|
|
225
|
+
- [Complete Logging Guide](./LOGGING-GUIDE.md) - All features and options
|
|
226
|
+
- [SecureNow](https://securenow.ai/)
|
|
227
|
+
- [Documentation](./INDEX.md)
|
|
228
|
+
- [Combine with Tracing](../README.md) - Full observability
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
**That's it!** 🎉 Your app is now sending logs to SecureNow.
|
|
233
|
+
|
|
234
|
+
Need help? Check the [full documentation](./LOGGING-GUIDE.md) or open an issue.
|
package/nextjs.js
CHANGED
|
@@ -551,17 +551,30 @@ function registerSecureNow(options = {}) {
|
|
|
551
551
|
const origWarn = console.warn;
|
|
552
552
|
const origError = console.error;
|
|
553
553
|
|
|
554
|
+
const { context: otelContext, trace: otelTrace } = require('@opentelemetry/api');
|
|
555
|
+
function _emitLog(sn, st, args) {
|
|
556
|
+
try {
|
|
557
|
+
const activeCtx = otelContext.active();
|
|
558
|
+
const spanCtx = otelTrace.getSpanContext(activeCtx);
|
|
559
|
+
logger.emit({
|
|
560
|
+
severityNumber: sn,
|
|
561
|
+
severityText: st,
|
|
562
|
+
body: args.map(String).join(' '),
|
|
563
|
+
...(spanCtx && { context: activeCtx }),
|
|
564
|
+
});
|
|
565
|
+
} catch (_) {}
|
|
566
|
+
}
|
|
554
567
|
console.log = (...args) => {
|
|
555
568
|
origLog.apply(console, args);
|
|
556
|
-
|
|
569
|
+
_emitLog(SeverityNumber.INFO, 'INFO', args);
|
|
557
570
|
};
|
|
558
571
|
console.warn = (...args) => {
|
|
559
572
|
origWarn.apply(console, args);
|
|
560
|
-
|
|
573
|
+
_emitLog(SeverityNumber.WARN, 'WARN', args);
|
|
561
574
|
};
|
|
562
575
|
console.error = (...args) => {
|
|
563
576
|
origError.apply(console, args);
|
|
564
|
-
|
|
577
|
+
_emitLog(SeverityNumber.ERROR, 'ERROR', args);
|
|
565
578
|
};
|
|
566
579
|
|
|
567
580
|
console.log('[securenow] 📋 Logging: ENABLED → %s', logsUrl);
|
|
@@ -575,6 +588,8 @@ function registerSecureNow(options = {}) {
|
|
|
575
588
|
const start = Date.now();
|
|
576
589
|
const method = req.method;
|
|
577
590
|
const url = req.url;
|
|
591
|
+
const reqCtx = otelContext.active();
|
|
592
|
+
const reqSpanCtx = otelTrace.getSpanContext(reqCtx);
|
|
578
593
|
|
|
579
594
|
res.on('finish', () => {
|
|
580
595
|
const duration = Date.now() - start;
|
|
@@ -598,6 +613,7 @@ function registerSecureNow(options = {}) {
|
|
|
598
613
|
'http.client_ip': String(ip).split(',')[0].trim(),
|
|
599
614
|
'http.user_agent': ua,
|
|
600
615
|
},
|
|
616
|
+
...(reqSpanCtx && { context: reqCtx }),
|
|
601
617
|
});
|
|
602
618
|
} catch (_) {}
|
|
603
619
|
});
|
package/package.json
CHANGED
package/tracing.d.ts
CHANGED
|
@@ -178,5 +178,6 @@ export const loggerProvider: LoggerProvider | null;
|
|
|
178
178
|
* - SECURENOW_DISABLE_INSTRUMENTATIONS=pkg1,pkg2
|
|
179
179
|
* - OTEL_LOG_LEVEL=info|debug
|
|
180
180
|
* - SECURENOW_TEST_SPAN=1
|
|
181
|
+
* - SECURENOW_TRUSTED_PROXIES=ip1,ip2 # Additional trusted proxy IPs for X-Forwarded-For
|
|
181
182
|
* - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=... # Override logs endpoint
|
|
182
183
|
*/
|
package/tracing.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* SECURENOW_STRICT=1 -> if no appid/name is provided in cluster, exit(1) so PM2 restarts the worker
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');
|
|
24
|
+
const { diag, DiagConsoleLogger, DiagLogLevel, context, trace } = require('@opentelemetry/api');
|
|
25
25
|
const { NodeSDK } = require('@opentelemetry/sdk-node');
|
|
26
26
|
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
|
|
27
27
|
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
|
|
@@ -188,11 +188,74 @@ const maxBodySize = parseInt(env('SECURENOW_MAX_BODY_SIZE') || '10240'); // 10KB
|
|
|
188
188
|
const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
189
189
|
const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
|
|
190
190
|
|
|
191
|
+
// -------- Trusted proxy IP resolution --------
|
|
192
|
+
// Only trust X-Forwarded-For / X-Real-IP when the direct connection comes from
|
|
193
|
+
// a known proxy (loopback, private RFC-1918/RFC-4193, or an explicit allowlist).
|
|
194
|
+
// This prevents end-users from spoofing their IP via custom headers.
|
|
195
|
+
const os = require('os');
|
|
196
|
+
const LOOPBACK_RE = /^(127\.|::1$|::ffff:127\.)/;
|
|
197
|
+
const PRIVATE_IP_RE = /^(127\.|::1|::ffff:127\.|10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|fc|fd)/;
|
|
198
|
+
const trustedProxyCsv = (env('SECURENOW_TRUSTED_PROXIES') || '').trim();
|
|
199
|
+
const trustedProxySet = trustedProxyCsv ? new Set(trustedProxyCsv.split(',').map(s => s.trim()).filter(Boolean)) : null;
|
|
200
|
+
|
|
201
|
+
// Resolve the host's actual network IP once at startup (used when socket is loopback)
|
|
202
|
+
let _hostIp = null;
|
|
203
|
+
function getHostIp() {
|
|
204
|
+
if (_hostIp !== null) return _hostIp;
|
|
205
|
+
try {
|
|
206
|
+
const ifaces = os.networkInterfaces();
|
|
207
|
+
for (const name of Object.keys(ifaces)) {
|
|
208
|
+
for (const iface of ifaces[name]) {
|
|
209
|
+
if (!iface.internal && iface.family === 'IPv4') { _hostIp = iface.address; return _hostIp; }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (_) {}
|
|
213
|
+
_hostIp = '';
|
|
214
|
+
return _hostIp;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function isFromTrustedProxy(socketIp) {
|
|
218
|
+
if (!socketIp) return false;
|
|
219
|
+
const normalized = socketIp.replace(/^::ffff:/, '');
|
|
220
|
+
if (trustedProxySet && trustedProxySet.has(normalized)) return true;
|
|
221
|
+
return PRIVATE_IP_RE.test(socketIp);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function resolveClientIp(request) {
|
|
225
|
+
const socketIp = request.socket?.remoteAddress || '';
|
|
226
|
+
if (!isFromTrustedProxy(socketIp)) return socketIp;
|
|
227
|
+
|
|
228
|
+
// Connection is from a trusted proxy — read the leftmost untrusted IP
|
|
229
|
+
const fwd = request.headers['x-forwarded-for'];
|
|
230
|
+
if (fwd) {
|
|
231
|
+
const chain = String(fwd).split(',').map(s => s.trim()).filter(Boolean);
|
|
232
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
233
|
+
if (!isFromTrustedProxy(chain[i])) return chain[i];
|
|
234
|
+
}
|
|
235
|
+
return chain[0] || socketIp;
|
|
236
|
+
}
|
|
237
|
+
const headerIp = request.headers['x-real-ip'];
|
|
238
|
+
if (headerIp) return headerIp;
|
|
239
|
+
|
|
240
|
+
// Loopback means the client is on this machine — use the host's network IP
|
|
241
|
+
// so traces are attributed to the actual machine, not a useless ::1 / 127.0.0.1
|
|
242
|
+
if (LOOPBACK_RE.test(socketIp)) {
|
|
243
|
+
const hostIp = getHostIp();
|
|
244
|
+
if (hostIp) return hostIp;
|
|
245
|
+
}
|
|
246
|
+
return socketIp;
|
|
247
|
+
}
|
|
248
|
+
|
|
191
249
|
// Configure HTTP instrumentation with body capture
|
|
192
250
|
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
|
|
193
251
|
const httpInstrumentation = new HttpInstrumentation({
|
|
194
252
|
requestHook: (span, request) => {
|
|
195
253
|
try {
|
|
254
|
+
const clientIp = resolveClientIp(request);
|
|
255
|
+
if (clientIp) {
|
|
256
|
+
span.setAttribute('http.client_ip', clientIp);
|
|
257
|
+
}
|
|
258
|
+
|
|
196
259
|
if (captureBody && request.method && ['POST', 'PUT', 'PATCH'].includes(request.method)) {
|
|
197
260
|
const contentType = request.headers['content-type'] || '';
|
|
198
261
|
|
|
@@ -293,6 +356,29 @@ if (loggingEnabled) {
|
|
|
293
356
|
resource: sharedResource,
|
|
294
357
|
});
|
|
295
358
|
loggerProvider.addLogRecordProcessor(batchLogProcessor);
|
|
359
|
+
|
|
360
|
+
// Auto-patch console.* so every log/warn/error becomes an OTel log record
|
|
361
|
+
const _logger = loggerProvider.getLogger('console', '1.0.0');
|
|
362
|
+
const _orig = { log: console.log, info: console.info, warn: console.warn, error: console.error, debug: console.debug };
|
|
363
|
+
const SEV = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
|
|
364
|
+
function _emit(sn, st, args) {
|
|
365
|
+
try {
|
|
366
|
+
const activeCtx = context.active();
|
|
367
|
+
const spanCtx = trace.getSpanContext(activeCtx);
|
|
368
|
+
_logger.emit({
|
|
369
|
+
severityNumber: sn,
|
|
370
|
+
severityText: st,
|
|
371
|
+
body: args.map(a => (typeof a === 'object' && a !== null) ? JSON.stringify(a) : String(a)).join(' '),
|
|
372
|
+
attributes: { 'log.source': 'console', 'log.method': st.toLowerCase() },
|
|
373
|
+
...(spanCtx && { context: activeCtx }),
|
|
374
|
+
});
|
|
375
|
+
} catch (_) {}
|
|
376
|
+
}
|
|
377
|
+
console.log = function (...a) { _emit(SEV.INFO, 'INFO', a); _orig.log.apply(console, a); };
|
|
378
|
+
console.info = function (...a) { _emit(SEV.INFO, 'INFO', a); _orig.info.apply(console, a); };
|
|
379
|
+
console.warn = function (...a) { _emit(SEV.WARN, 'WARN', a); _orig.warn.apply(console, a); };
|
|
380
|
+
console.error = function (...a) { _emit(SEV.ERROR, 'ERROR', a); _orig.error.apply(console, a); };
|
|
381
|
+
console.debug = function (...a) { _emit(SEV.DEBUG, 'DEBUG', a); _orig.debug.apply(console, a); };
|
|
296
382
|
}
|
|
297
383
|
|
|
298
384
|
// -------- SDK --------
|