securenow 7.7.4 → 7.7.5

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/NPM_README.md CHANGED
@@ -1261,7 +1261,7 @@ Use `npx securenow help firewall` for complete details on all layers.
1261
1261
 
1262
1262
  | Variable | Description | Default |
1263
1263
  |----------|-------------|---------|
1264
- | `OTEL_LOG_LEVEL` | OpenTelemetry SDK log level. Options: `debug`, `info`, `warn`, `error` | `none` |
1264
+ | `OTEL_LOG_LEVEL` | OpenTelemetry diagnostic override. Options: `debug`, `info`, `warn`, `error`, `none`. Overrides `config.otel.logLevel` for emergency debugging. | `error` |
1265
1265
  | `SECURENOW_TEST_SPAN` | Set to `1` to emit a test span on startup. | `0` |
1266
1266
 
1267
1267
  #### Environment
@@ -1600,7 +1600,16 @@ curl http://localhost:4318/v1/traces
1600
1600
  # Should return 200 or 405 (method not allowed)
1601
1601
  ```
1602
1602
 
1603
- **Check 3: Enable debug logging in credentials**
1603
+ **Check 3: Run doctor and enable debug diagnostics**
1604
+
1605
+ ```bash
1606
+ npx securenow doctor --json
1607
+ OTEL_LOG_LEVEL=debug node -r securenow/register app.js
1608
+ ```
1609
+
1610
+ Doctor checks OTLP reachability and flags duplicate `@opentelemetry/api` versions that can silently leave tracing on the OpenTelemetry noop provider.
1611
+
1612
+ For a persistent setting, update credentials:
1604
1613
 
1605
1614
  ```json
1606
1615
  {
package/SKILL-API.md CHANGED
@@ -495,7 +495,7 @@ Local development and production use `.securenow/credentials.json`. Every settin
495
495
  | `SECURENOW_DISABLE_INSTRUMENTATIONS` | Comma-separated packages to skip (e.g. `fs,dns`) | — |
496
496
  | `SECURENOW_TEST_SPAN` | `1` to emit a test span on startup | `0` |
497
497
  | `SECURENOW_HIDE_BANNER` | `1` to suppress free-trial upgrade banner | `0` |
498
- | `OTEL_LOG_LEVEL` | SDK log level: `none`, `error`, `warn`, `info`, `debug` | `none` |
498
+ | `OTEL_LOG_LEVEL` | SDK diagnostic override: `error`, `warn`, `info`, `debug`, or `none` | `error` |
499
499
  | `SECURENOW_ENVIRONMENT` / `SECURENOW_DEPLOYMENT_ENVIRONMENT` / `NODE_ENV` | Legacy fallback for `config.runtime.deploymentEnvironment` | `production` |
500
500
 
501
501
  ### Firewall
@@ -634,7 +634,7 @@ On startup, securenow logs its configuration:
634
634
  [securenow] Firewall: synced 142 blocked IPs
635
635
  ```
636
636
 
637
- Set `config.otel.logLevel` to `debug` in `.securenow/credentials.json` or run `securenow test-span --env <env>` to troubleshoot connectivity issues.
637
+ Set `config.otel.logLevel` to `debug`, or temporarily run with `OTEL_LOG_LEVEL=debug`, and run `securenow doctor --json` to troubleshoot connectivity, duplicate OpenTelemetry API packages, and provider registration issues.
638
638
 
639
639
  **CLI equivalent** (works without booting the SDK — useful when the app won't start):
640
640
 
package/app-config.js CHANGED
@@ -46,7 +46,7 @@ const DEFAULT_CONFIG = Object.freeze({
46
46
  tracesEndpoint: null,
47
47
  logsEndpoint: null,
48
48
  headers: {},
49
- logLevel: 'none',
49
+ logLevel: 'error',
50
50
  disableInstrumentations: [],
51
51
  },
52
52
  runtime: {
@@ -102,7 +102,7 @@ const CONFIG_EXPLANATIONS = Object.freeze({
102
102
  'config.otel.tracesEndpoint': 'Optional full traces endpoint override, for split collectors.',
103
103
  'config.otel.logsEndpoint': 'Optional full logs endpoint override, for split collectors.',
104
104
  'config.otel.headers': 'Optional OTLP headers. The SDK auto-adds x-api-key from app.key when missing.',
105
- 'config.otel.logLevel': 'OpenTelemetry diagnostic log level: none, error, warn, info, or debug.',
105
+ 'config.otel.logLevel': 'OpenTelemetry diagnostic log level: error, warn, info, debug, or none.',
106
106
  'config.otel.disableInstrumentations': 'Optional OTel instrumentation package names to disable.',
107
107
  'config.runtime.deploymentEnvironment': 'deployment.environment resource attribute. Set this in the credentials file for production.',
108
108
  'config.runtime.noUuid': 'null means auto: true when app.key is present. Set true/false only for advanced routing needs.',
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const crypto = require('crypto');
4
+ const fs = require('fs');
5
+ const path = require('path');
4
6
  const url = require('url');
5
7
  const ui = require('./ui');
6
8
  const config = require('./config');
@@ -37,6 +39,7 @@ function resolvedConfig(options = {}) {
37
39
  loggingEnabled: appConfig.boolEnv('SECURENOW_LOGGING_ENABLED', true),
38
40
  captureBody: appConfig.boolEnv('SECURENOW_CAPTURE_BODY', true),
39
41
  captureMultipart: appConfig.boolEnv('SECURENOW_CAPTURE_MULTIPART', true),
42
+ otelLogLevel: (process.env.OTEL_LOG_LEVEL != null ? process.env.OTEL_LOG_LEVEL : appConfig.env('OTEL_LOG_LEVEL')) || 'error',
40
43
  firewallEnabled,
41
44
  firewallLayers: {
42
45
  http: firewallEnabled,
@@ -47,6 +50,68 @@ function resolvedConfig(options = {}) {
47
50
  };
48
51
  }
49
52
 
53
+ function readPackageVersion(packageJsonPath, label) {
54
+ try {
55
+ if (!packageJsonPath || !fs.existsSync(packageJsonPath)) return null;
56
+ const realPath = fs.realpathSync(packageJsonPath);
57
+ const pkg = JSON.parse(fs.readFileSync(realPath, 'utf8').replace(/^\uFEFF/, ''));
58
+ return { label, path: realPath, version: pkg.version || null };
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ function resolvePackageJson(pkgName, paths, label) {
65
+ try {
66
+ let current = path.dirname(require.resolve(pkgName, { paths }));
67
+ while (current && current !== path.dirname(current)) {
68
+ const candidate = path.join(current, 'package.json');
69
+ const pkg = readPackageVersion(candidate, label);
70
+ if (pkg) return pkg;
71
+ current = path.dirname(current);
72
+ }
73
+ return null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+
79
+ function collectOtelApiPackages() {
80
+ const cwd = process.cwd();
81
+ const packageName = '@opentelemetry/api';
82
+ const candidates = [
83
+ readPackageVersion(path.join(cwd, 'node_modules', '@opentelemetry', 'api', 'package.json'), 'project node_modules'),
84
+ readPackageVersion(path.join(cwd, 'node_modules', 'securenow', 'node_modules', '@opentelemetry', 'api', 'package.json'), 'nested under securenow'),
85
+ resolvePackageJson(packageName, [cwd], 'resolved from project'),
86
+ resolvePackageJson(packageName, [path.resolve(__dirname, '..')], 'resolved from securenow CLI'),
87
+ ].filter(Boolean);
88
+
89
+ const byPath = new Map();
90
+ for (const item of candidates) {
91
+ if (!byPath.has(item.path)) byPath.set(item.path, { ...item, labels: [item.label] });
92
+ else byPath.get(item.path).labels.push(item.label);
93
+ }
94
+
95
+ return [...byPath.values()].map(({ label, labels, ...rest }) => ({
96
+ ...rest,
97
+ label: labels.join(', '),
98
+ }));
99
+ }
100
+
101
+ function otelApiSingletonCheck() {
102
+ const packages = collectOtelApiPackages();
103
+ const versions = [...new Set(packages.map((p) => p.version).filter(Boolean))];
104
+ return {
105
+ name: 'otel-api-singleton',
106
+ ok: versions.length <= 1,
107
+ versions,
108
+ packages,
109
+ ...(versions.length > 1 ? {
110
+ error: `Multiple @opentelemetry/api versions detected (${versions.join(', ')}). This can leave tracing on the NoopTracerProvider.`,
111
+ } : {}),
112
+ };
113
+ }
114
+
50
115
  function maskSecret(value) {
51
116
  if (!value) return '';
52
117
  const text = String(value);
@@ -308,6 +373,7 @@ function env(_args, flags) {
308
373
  firewallApiKey: cfg.apiKey ? `${cfg.apiKey.slice(0, 12)}...` : null,
309
374
  apiUrl: cfg.apiUrl,
310
375
  loggingEnabled: cfg.loggingEnabled,
376
+ otelLogLevel: cfg.otelLogLevel,
311
377
  captureBody: cfg.captureBody,
312
378
  captureMultipart: cfg.captureMultipart,
313
379
  noUuid: appConfig.resolveNoUuid(),
@@ -326,6 +392,7 @@ function env(_args, flags) {
326
392
  ['Environment', cfg.deploymentEnvironment],
327
393
  ['Traces endpoint', cfg.tracesEndpoint],
328
394
  ['Logs endpoint', cfg.logsEndpoint],
395
+ ['OTel diagnostics', cfg.otelLogLevel],
329
396
  ['Logging', cfg.loggingEnabled ? ui.c.green('enabled') : ui.c.dim('disabled')],
330
397
  ['Body capture', cfg.captureBody ? ui.c.green('enabled') : ui.c.dim('disabled')],
331
398
  ['Multipart capture', cfg.captureMultipart ? ui.c.green('enabled') : ui.c.dim('disabled')],
@@ -342,6 +409,8 @@ function env(_args, flags) {
342
409
  async function doctor(_args, flags) {
343
410
  const cfg = resolvedConfig();
344
411
  const checks = [];
412
+ const otelApiCheck = otelApiSingletonCheck();
413
+ checks.push(otelApiCheck);
345
414
 
346
415
  const jsonHeaders = { 'Content-Type': 'application/json', ...cfg.headers };
347
416
 
@@ -386,6 +455,15 @@ async function doctor(_args, flags) {
386
455
  }
387
456
 
388
457
  const warnings = [];
458
+ const singletonOkMessage = otelApiCheck.ok && otelApiCheck.packages.length
459
+ ? `OpenTelemetry API singleton OK (${otelApiCheck.versions[0] || 'unknown'})`
460
+ : null;
461
+ if (!otelApiCheck.ok) {
462
+ warnings.push(`${otelApiCheck.error} Align the app and SecureNow to one 1.9.x copy, then reinstall.`);
463
+ }
464
+ if (String(cfg.otelLogLevel || '').toLowerCase() === 'none') {
465
+ warnings.push('OpenTelemetry diagnostic log level is `none`; provider registration/export errors are hidden. Set config.otel.logLevel to `error`/`warn`, or temporarily run with OTEL_LOG_LEVEL=debug.');
466
+ }
389
467
  if (!cfg.appKey) {
390
468
  warnings.push('No app key resolved. Run `npx securenow login` or set app.key in .securenow/credentials.json.');
391
469
  }
@@ -410,6 +488,8 @@ async function doctor(_args, flags) {
410
488
  for (const w of warnings) ui.warn(w);
411
489
  }
412
490
 
491
+ if (singletonOkMessage) ui.success(singletonOkMessage);
492
+
413
493
  console.log('');
414
494
  if (ok) ui.success('All checks passed.');
415
495
  else ui.error('One or more checks failed. Run with --json for details.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "7.7.4",
3
+ "version": "7.7.5",
4
4
  "description": "OpenTelemetry instrumentation for Node.js, Next.js, and Nuxt - Send traces and logs to any OTLP-compatible backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",
@@ -141,7 +141,7 @@
141
141
  "SKILL-API.md"
142
142
  ],
143
143
  "dependencies": {
144
- "@opentelemetry/api": "1.9.1",
144
+ "@opentelemetry/api": ">=1.9.0 <1.10.0",
145
145
  "@opentelemetry/api-logs": "0.218.0",
146
146
  "@opentelemetry/auto-instrumentations-node": "0.76.0",
147
147
  "@opentelemetry/core": "2.7.1",
package/tracing.js CHANGED
@@ -21,7 +21,7 @@
21
21
  * OTEL_EXPORTER_OTLP_HEADERS="k=v,k2=v2"
22
22
  * SECURENOW_DISABLE_INSTRUMENTATIONS="pkg1,pkg2"
23
23
  * SECURENOW_CAPTURE_MULTIPART=1 # capture multipart/form-data fields & file metadata (streaming, no file content buffered)
24
- * OTEL_LOG_LEVEL=info|debug
24
+ * OTEL_LOG_LEVEL=error|warn|info|debug|none
25
25
  * SECURENOW_TEST_SPAN=1
26
26
  *
27
27
  * Safety:
@@ -264,7 +264,7 @@ function collectMultipartMeta(request, contentType, sensitiveFields, maxTextFiel
264
264
  })();
265
265
 
266
266
  // -------- diagnostics --------
267
- const diagLevel = (env('OTEL_LOG_LEVEL') || '').toLowerCase();
267
+ const diagLevel = ((process.env.OTEL_LOG_LEVEL != null ? process.env.OTEL_LOG_LEVEL : env('OTEL_LOG_LEVEL')) || '').toLowerCase();
268
268
  (() => {
269
269
  const level = diagLevel === 'debug' ? DiagLogLevel.DEBUG :
270
270
  diagLevel === 'info' ? DiagLogLevel.INFO :