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.
@@ -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
- ## 1. Install
8
-
9
- ```bash
10
- npm install securenow
11
- ```
12
-
13
- ---
14
-
15
- ## 2. Configure Environment
16
-
17
- Create `.env` file or export variables:
18
-
19
- ```bash
20
- SECURENOW_LOGGING_ENABLED=1
21
- SECURENOW_APPID=my-app
22
- SECURENOW_INSTANCE=http://your-otlp-backend:4318
23
-
24
- # For SecureNow / hosted OTLP (example):
25
- # SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
26
- # OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your-key>"
27
- ```
28
-
29
- ---
30
-
31
- ## 3. Add to Your App
32
-
33
- **Option A: Automatic Console Logging (Easiest)**
34
-
35
- Add these two lines at the top of your main file:
36
-
37
- ```javascript
38
- // app.js, index.js, server.js, or main.ts
39
- require('securenow/register');
40
- require('securenow/console-instrumentation');
41
-
42
- // That's it! Now use console normally
43
- console.log('App started');
44
- console.error('An error occurred', { userId: 123 });
45
- ```
46
-
47
- **Option B: Use NODE_OPTIONS (No code changes)**
48
-
49
- ```bash
50
- NODE_OPTIONS="-r securenow/register -r securenow/console-instrumentation" node app.js
51
- ```
52
-
53
- ---
54
-
55
- ## 4. Run Your App
56
-
57
- ```bash
58
- node app.js
59
- # or
60
- npm start
61
- ```
62
-
63
- You should see:
64
-
65
- ```
66
- [securenow] OTel SDK started → http://your-otlp-backend:4318/v1/traces
67
- [securenow] 📋 Logging: ENABLED → http://your-otlp-backend:4318/v1/logs
68
- [securenow] Console instrumentation installed
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
- require('securenow/console-instrumentation');
90
-
91
- const express = require('express');
92
- const app = express();
93
-
94
- app.get('/', (req, res) => {
95
- console.log('Request received');
96
- res.send('Hello World');
97
- });
98
-
99
- app.listen(3000);
100
- ```
101
-
102
- ### Next.js
103
-
104
- ```typescript
105
- // instrumentation.ts (in project root)
106
- export async function register() {
107
- if (process.env.NEXT_RUNTIME === 'nodejs') {
108
- process.env.SECURENOW_LOGGING_ENABLED = '1';
109
- await import('securenow/register');
110
- await import('securenow/console-instrumentation');
111
- }
112
- }
113
- ```
114
-
115
- ```bash
116
- # .env.local
117
- SECURENOW_LOGGING_ENABLED=1
118
- SECURENOW_APPID=my-nextjs-app
119
- SECURENOW_INSTANCE=http://localhost:4318
120
- ```
121
-
122
- ### Fastify
123
-
124
- ```javascript
125
- // server.js
126
- require('securenow/register');
127
- require('securenow/console-instrumentation');
128
-
129
- const fastify = require('fastify')();
130
-
131
- fastify.get('/', async () => {
132
- console.log('Route called');
133
- return { hello: 'world' };
134
- });
135
-
136
- fastify.listen({ port: 3000 });
137
- ```
138
-
139
- ### NestJS
140
-
141
- ```typescript
142
- // main.ts
143
- require('securenow/register');
144
- require('securenow/console-instrumentation');
145
-
146
- import { NestFactory } from '@nestjs/core';
147
- import { AppModule } from './app.module';
148
-
149
- async function bootstrap() {
150
- const app = await NestFactory.create(AppModule);
151
- console.log('App starting');
152
- await app.listen(3000);
153
- }
154
-
155
- bootstrap();
156
- ```
157
-
158
- ---
159
-
160
- ## Troubleshooting
161
-
162
- **Logs not appearing?**
163
-
164
- 1. Check `SECURENOW_LOGGING_ENABLED=1` is set
165
- 2. Verify your OTLP / SecureNow endpoint is correct
166
- 3. Enable debug: `OTEL_LOG_LEVEL=debug`
167
-
168
- **Console instrumentation not working?**
169
-
170
- Make sure require order is correct:
171
-
172
- ```javascript
173
- // ✅ Correct
174
- require('securenow/register'); // First
175
- require('securenow/console-instrumentation'); // Second
176
-
177
- // ❌ Wrong
178
- require('express');
179
- require('securenow/register'); // Too late!
180
- ```
181
-
182
- ---
183
-
184
- ## What Gets Logged?
185
-
186
- All standard console methods:
187
-
188
- - `console.log()` → INFO
189
- - `console.info()` → INFO
190
- - `console.warn()` → WARN
191
- - `console.error()` → ERROR
192
- - `console.debug()` DEBUG
193
-
194
- **Structured logging example:**
195
-
196
- ```javascript
197
- console.log('User logged in', {
198
- userId: 123,
199
- email: 'user@example.com',
200
- ip: '192.168.1.1'
201
- });
202
- ```
203
-
204
- This creates a log with attributes you can filter/search in SecureNow!
205
-
206
- ---
207
-
208
- ## Advanced Usage
209
-
210
- **Direct Logger API:**
211
-
212
- ```javascript
213
- const { getLogger } = require('securenow/tracing');
214
- const logger = getLogger('my-module', '1.0.0');
215
-
216
- logger.emit({
217
- severityNumber: 9,
218
- severityText: 'INFO',
219
- body: 'Custom log message',
220
- attributes: {
221
- customField: 'value',
222
- },
223
- });
224
- ```
225
-
226
- ---
227
-
228
- ## Next Steps
229
-
230
- - [Complete Logging Guide](./LOGGING-GUIDE.md) - All features and options
231
- - [SecureNow](https://securenow.ai/)
232
- - [Documentation](./INDEX.md)
233
- - [Combine with Tracing](../README.md) - Full observability
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
- try { logger.emit({ severityNumber: SeverityNumber.INFO, severityText: 'INFO', body: args.map(String).join(' ') }); } catch (_) {}
569
+ _emitLog(SeverityNumber.INFO, 'INFO', args);
557
570
  };
558
571
  console.warn = (...args) => {
559
572
  origWarn.apply(console, args);
560
- try { logger.emit({ severityNumber: SeverityNumber.WARN, severityText: 'WARN', body: args.map(String).join(' ') }); } catch (_) {}
573
+ _emitLog(SeverityNumber.WARN, 'WARN', args);
561
574
  };
562
575
  console.error = (...args) => {
563
576
  origError.apply(console, args);
564
- try { logger.emit({ severityNumber: SeverityNumber.ERROR, severityText: 'ERROR', body: args.map(String).join(' ') }); } catch (_) {}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "5.5.0",
3
+ "version": "5.6.1",
4
4
  "description": "OpenTelemetry instrumentation for Node.js and Next.js - Send traces and logs to any OTLP-compatible backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",
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 --------