securenow 5.6.0 → 5.7.0
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 +1594 -1609
- package/README.md +72 -36
- package/cli/monitor.js +13 -1
- package/cli/run.js +133 -0
- package/cli.js +22 -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/INDEX.md +14 -3
- package/docs/LOGGING-GUIDE.md +701 -708
- package/docs/LOGGING-QUICKSTART.md +234 -239
- package/docs/NUXT-GUIDE.md +166 -0
- package/nextjs-webpack-config.js +77 -0
- package/nextjs.js +19 -3
- package/nuxt-server-plugin.mjs +400 -0
- package/nuxt.d.ts +60 -0
- package/nuxt.mjs +75 -0
- package/package.json +21 -4
- package/register.js +39 -4
- package/tracing.js +15 -7
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SecureNow
|
|
2
2
|
|
|
3
|
-
OpenTelemetry instrumentation for Node.js
|
|
3
|
+
OpenTelemetry instrumentation for Node.js, Next.js, and Nuxt applications - send **traces and logs** to any OTLP-compatible backend (including SecureNow).
|
|
4
4
|
|
|
5
5
|
**Official npm package:** [securenow](http://securenow.ai/)
|
|
6
6
|
|
|
@@ -8,6 +8,39 @@ OpenTelemetry instrumentation for Node.js and Next.js applications - send **trac
|
|
|
8
8
|
|
|
9
9
|
## 🚀 Quick Start
|
|
10
10
|
|
|
11
|
+
### For Any Node.js App (Express, Fastify, NestJS, Koa, Hapi, etc.)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# 1. Install
|
|
15
|
+
npm install securenow
|
|
16
|
+
|
|
17
|
+
# 2. Set env vars
|
|
18
|
+
export SECURENOW_APPID=my-app
|
|
19
|
+
export SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
20
|
+
|
|
21
|
+
# 3. Add -r securenow/register to your start command
|
|
22
|
+
node -r securenow/register src/app.js
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. One `-r` flag is all you need — ESM and CJS apps are handled automatically (Node >=20.6 auto-registers the ESM loader hook).
|
|
26
|
+
|
|
27
|
+
> **package.json** example:
|
|
28
|
+
> ```json
|
|
29
|
+
> "scripts": {
|
|
30
|
+
> "start": "node -r securenow/register src/index.js",
|
|
31
|
+
> "dev": "node -r securenow/register --watch src/index.js"
|
|
32
|
+
> }
|
|
33
|
+
> ```
|
|
34
|
+
|
|
35
|
+
You can also use `NODE_OPTIONS` so your existing scripts stay unchanged:
|
|
36
|
+
```bash
|
|
37
|
+
NODE_OPTIONS="-r securenow/register" npm start
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
See the [All Frameworks Quick Start](./docs/ALL-FRAMEWORKS-QUICKSTART.md) for tested setup guides.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
11
44
|
### For Next.js Applications
|
|
12
45
|
|
|
13
46
|
**The easiest way to add observability to Next.js!**
|
|
@@ -40,6 +73,32 @@ npx securenow init
|
|
|
40
73
|
|
|
41
74
|
---
|
|
42
75
|
|
|
76
|
+
### For Nuxt 3 Applications
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# 1. Install
|
|
80
|
+
npm install securenow
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Add the module to your `nuxt.config.ts`:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
export default defineNuxtConfig({
|
|
87
|
+
modules: ['securenow/nuxt'],
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Set environment variables in `.env`:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
SECURENOW_APPID=my-nuxt-app
|
|
95
|
+
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Done!** All server-side requests are now traced automatically. See the [Nuxt 3 Complete Guide](./docs/NUXT-GUIDE.md) for details.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
43
102
|
### CLI — Manage Everything from the Terminal
|
|
44
103
|
|
|
45
104
|
```bash
|
|
@@ -70,41 +129,6 @@ Run `npx securenow help` for all commands. See the [CLI Reference](#cli-referenc
|
|
|
70
129
|
|
|
71
130
|
---
|
|
72
131
|
|
|
73
|
-
### For Node.js Applications (Express, Fastify, NestJS, etc.)
|
|
74
|
-
|
|
75
|
-
#### Tracing Only
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
# 1. Install
|
|
79
|
-
npm install securenow
|
|
80
|
-
|
|
81
|
-
# 2. Set environment variables
|
|
82
|
-
export SECURENOW_APPID=my-app
|
|
83
|
-
export SECURENOW_INSTANCE=http://your-otlp-collector:4318
|
|
84
|
-
|
|
85
|
-
# 3. Run with preload
|
|
86
|
-
NODE_OPTIONS="-r securenow/register" node app.js
|
|
87
|
-
# or
|
|
88
|
-
NODE_OPTIONS="-r securenow/register" npm start
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
#### Tracing + Logging (Recommended)
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
# 1. Install
|
|
95
|
-
npm install securenow
|
|
96
|
-
|
|
97
|
-
# 2. Set environment variables
|
|
98
|
-
export SECURENOW_APPID=my-app
|
|
99
|
-
export SECURENOW_INSTANCE=http://your-otlp-collector:4318
|
|
100
|
-
export SECURENOW_LOGGING_ENABLED=1
|
|
101
|
-
|
|
102
|
-
# 3. Run with preload (adds logging)
|
|
103
|
-
NODE_OPTIONS="-r securenow/register -r securenow/console-instrumentation" node app.js
|
|
104
|
-
|
|
105
|
-
# Now all console.log/info/warn/error automatically go to your OTLP backend (for example SecureNow)!
|
|
106
|
-
```
|
|
107
|
-
|
|
108
132
|
---
|
|
109
133
|
|
|
110
134
|
## 📦 Installation
|
|
@@ -161,6 +185,7 @@ SecureNow automatically instruments:
|
|
|
161
185
|
|
|
162
186
|
### Web Frameworks
|
|
163
187
|
- ✅ Next.js (App Router & Pages Router)
|
|
188
|
+
- ✅ Nuxt 3 (Nitro server)
|
|
164
189
|
- ✅ Express.js
|
|
165
190
|
- ✅ Fastify
|
|
166
191
|
- ✅ NestJS
|
|
@@ -191,10 +216,12 @@ SecureNow automatically instruments:
|
|
|
191
216
|
|
|
192
217
|
### Quick Starts
|
|
193
218
|
- **[Next.js Quick Start](./docs/NEXTJS-QUICKSTART.md)** - Get started in 30 seconds
|
|
219
|
+
- **[Nuxt 3 Guide](./docs/NUXT-GUIDE.md)** - One-line Nuxt module setup
|
|
194
220
|
- **[Logging Quick Start](./docs/LOGGING-QUICKSTART.md)** - Add logging in 2 minutes
|
|
195
221
|
|
|
196
222
|
### Complete Guides
|
|
197
223
|
- **[Next.js Complete Guide](./docs/NEXTJS-GUIDE.md)** - Full Next.js integration guide
|
|
224
|
+
- **[Nuxt 3 Complete Guide](./docs/NUXT-GUIDE.md)** - Full Nuxt 3 integration guide
|
|
198
225
|
- **[Logging Complete Guide](./docs/LOGGING-GUIDE.md)** - Full logging setup for all frameworks
|
|
199
226
|
- **[📚 Complete Documentation](./docs/INDEX.md)** - All guides and references
|
|
200
227
|
|
|
@@ -207,6 +234,15 @@ SecureNow automatically instruments:
|
|
|
207
234
|
|
|
208
235
|
After installing the package, the `securenow` CLI is available via `npx securenow` or globally after `npm install -g securenow`.
|
|
209
236
|
|
|
237
|
+
### Run (convenience wrapper)
|
|
238
|
+
|
|
239
|
+
| Command | Description |
|
|
240
|
+
|---------|-------------|
|
|
241
|
+
| `securenow run <script>` | Run a Node.js app with `-r securenow/register` injected |
|
|
242
|
+
| `securenow run --watch <script>` | Same, with Node.js watch mode |
|
|
243
|
+
|
|
244
|
+
Most users won't need this — just add `-r securenow/register` to your existing start script.
|
|
245
|
+
|
|
210
246
|
### Authentication
|
|
211
247
|
|
|
212
248
|
| Command | Description |
|
package/cli/monitor.js
CHANGED
|
@@ -105,7 +105,19 @@ async function tracesAnalyze(args, flags) {
|
|
|
105
105
|
|
|
106
106
|
const s = ui.spinner('Analyzing trace with AI');
|
|
107
107
|
try {
|
|
108
|
-
|
|
108
|
+
let result = await api.post('/traces/analyze', { traceId });
|
|
109
|
+
|
|
110
|
+
if (result.analysisId && result.status === 'running') {
|
|
111
|
+
const analysisId = result.analysisId;
|
|
112
|
+
for (let i = 0; i < 120; i++) {
|
|
113
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
114
|
+
result = await api.get(`/traces/analyze/${analysisId}`);
|
|
115
|
+
if (result.status === 'completed' || result.status === 'failed') break;
|
|
116
|
+
}
|
|
117
|
+
if (result.status === 'failed') throw new Error(result.error || 'Analysis failed');
|
|
118
|
+
if (result.status === 'running') throw new Error('Analysis timed out');
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
s.stop('Analysis complete');
|
|
110
122
|
|
|
111
123
|
if (flags.json) { ui.json(result); return; }
|
package/cli/run.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const ui = require('./ui');
|
|
7
|
+
|
|
8
|
+
function isESM(scriptPath) {
|
|
9
|
+
if (scriptPath.endsWith('.mjs')) return true;
|
|
10
|
+
if (scriptPath.endsWith('.cjs')) return false;
|
|
11
|
+
|
|
12
|
+
let dir = path.resolve(path.dirname(scriptPath));
|
|
13
|
+
while (true) {
|
|
14
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
15
|
+
if (fs.existsSync(pkgPath)) {
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
18
|
+
return pkg.type === 'module';
|
|
19
|
+
} catch { return false; }
|
|
20
|
+
}
|
|
21
|
+
const parent = path.dirname(dir);
|
|
22
|
+
if (parent === dir) break;
|
|
23
|
+
dir = parent;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveSecurenowRegister() {
|
|
29
|
+
// When running from the published package, securenow/register resolves to our own register.js.
|
|
30
|
+
// When running from the monorepo, resolve relative to this file.
|
|
31
|
+
try {
|
|
32
|
+
return require.resolve('securenow/register');
|
|
33
|
+
} catch {
|
|
34
|
+
return path.resolve(__dirname, '..', 'register.js');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveESMHook() {
|
|
39
|
+
// --import uses ESM resolution which requires file:// URLs for absolute paths
|
|
40
|
+
// on Windows. Using the bare specifier lets Node's own resolver handle it.
|
|
41
|
+
try {
|
|
42
|
+
const resolved = require.resolve('@opentelemetry/instrumentation/hook.mjs');
|
|
43
|
+
const { pathToFileURL } = require('url');
|
|
44
|
+
return pathToFileURL(resolved).href;
|
|
45
|
+
} catch {
|
|
46
|
+
return '@opentelemetry/instrumentation/hook.mjs';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* securenow run [node-flags...] <script> [app-args...]
|
|
52
|
+
*
|
|
53
|
+
* Spawns node with the correct OTel preload flags.
|
|
54
|
+
* Passes through Node flags (--watch, --inspect, etc.) and app arguments.
|
|
55
|
+
*
|
|
56
|
+
* We re-parse from process.argv directly so that flags like --watch and
|
|
57
|
+
* --inspect are forwarded to node verbatim (the CLI's own arg parser
|
|
58
|
+
* would otherwise consume them).
|
|
59
|
+
*/
|
|
60
|
+
function run(rawArgs) {
|
|
61
|
+
// rawArgs comes directly from process.argv, everything after `run` (or the file path)
|
|
62
|
+
if (!rawArgs || rawArgs.length === 0) {
|
|
63
|
+
ui.error('Missing script path.');
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(` ${ui.c.bold('Usage:')} securenow run [node-flags] <script> [app-args]`);
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log(` ${ui.c.bold('Examples:')}`);
|
|
68
|
+
console.log(` securenow run src/index.js`);
|
|
69
|
+
console.log(` securenow run --watch src/index.js`);
|
|
70
|
+
console.log(` securenow run --inspect src/server.js --port 3000`);
|
|
71
|
+
console.log('');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const args = rawArgs;
|
|
76
|
+
|
|
77
|
+
// Split into: node flags (before the script), script path, and app args (after)
|
|
78
|
+
const nodeFlags = [];
|
|
79
|
+
let scriptIdx = -1;
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < args.length; i++) {
|
|
82
|
+
if (args[i].startsWith('-')) {
|
|
83
|
+
nodeFlags.push(args[i]);
|
|
84
|
+
} else {
|
|
85
|
+
scriptIdx = i;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (scriptIdx === -1) {
|
|
91
|
+
ui.error('No script path found. Provide a .js/.mjs/.ts file to run.');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const scriptPath = args[scriptIdx];
|
|
96
|
+
const appArgs = args.slice(scriptIdx + 1);
|
|
97
|
+
|
|
98
|
+
const esm = isESM(scriptPath);
|
|
99
|
+
const registerPath = resolveSecurenowRegister();
|
|
100
|
+
|
|
101
|
+
const otelFlags = [];
|
|
102
|
+
if (esm) {
|
|
103
|
+
otelFlags.push('--import', resolveESMHook());
|
|
104
|
+
}
|
|
105
|
+
otelFlags.push('--require', registerPath);
|
|
106
|
+
|
|
107
|
+
const finalArgs = [...otelFlags, ...nodeFlags, scriptPath, ...appArgs];
|
|
108
|
+
|
|
109
|
+
const nodeExe = process.execPath;
|
|
110
|
+
|
|
111
|
+
if (process.env.SECURENOW_DEBUG) {
|
|
112
|
+
console.log(`[securenow] ${ui.c.dim('exec:')} ${nodeExe} ${finalArgs.join(' ')}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const child = spawn(nodeExe, finalArgs, {
|
|
116
|
+
stdio: 'inherit',
|
|
117
|
+
env: process.env,
|
|
118
|
+
cwd: process.cwd(),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
child.on('close', (code) => process.exit(code ?? 1));
|
|
122
|
+
child.on('error', (err) => {
|
|
123
|
+
ui.error(`Failed to start: ${err.message}`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Forward termination signals to the child
|
|
128
|
+
for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
|
|
129
|
+
process.on(sig, () => child.kill(sig));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = { run };
|
package/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const ui = require('./cli/ui');
|
|
@@ -235,6 +235,13 @@ const COMMANDS = {
|
|
|
235
235
|
flags: { typescript: 'Force TypeScript', javascript: 'Force JavaScript', src: 'Create in src/', root: 'Create in root', force: 'Overwrite existing' },
|
|
236
236
|
run: (a, f) => require('./cli/init').init(a, f),
|
|
237
237
|
},
|
|
238
|
+
run: {
|
|
239
|
+
desc: 'Run a Node.js app with automatic OTel instrumentation',
|
|
240
|
+
usage: 'securenow run [node-flags] <script> [app-args]',
|
|
241
|
+
flags: { watch: 'Enable Node.js watch mode', inspect: 'Enable Node.js inspector' },
|
|
242
|
+
rawArgv: true,
|
|
243
|
+
run: (rawArgs) => require('./cli/run').run(rawArgs),
|
|
244
|
+
},
|
|
238
245
|
version: {
|
|
239
246
|
desc: 'Show CLI version',
|
|
240
247
|
run: () => {
|
|
@@ -293,6 +300,7 @@ function showHelp(commandName) {
|
|
|
293
300
|
showBanner();
|
|
294
301
|
|
|
295
302
|
const groups = {
|
|
303
|
+
'Run': ['run'],
|
|
296
304
|
'Authentication': ['login', 'logout', 'whoami'],
|
|
297
305
|
'Applications': ['apps', 'init', 'status'],
|
|
298
306
|
'Observe': ['traces', 'logs', 'analytics'],
|
|
@@ -334,11 +342,24 @@ async function main() {
|
|
|
334
342
|
|
|
335
343
|
const cmd = COMMANDS[cmdName];
|
|
336
344
|
if (!cmd) {
|
|
345
|
+
// Auto-detect: if the first arg looks like a file path, treat it as `securenow run <file>`
|
|
346
|
+
if (/\.(m?[jt]sx?|cjs)$/.test(cmdName) || cmdName.includes('/') || cmdName.includes('\\')) {
|
|
347
|
+
return COMMANDS.run.run(process.argv.slice(2));
|
|
348
|
+
}
|
|
337
349
|
ui.error(`Unknown command: ${cmdName}`);
|
|
338
350
|
ui.info('Run `securenow help` for a list of commands.');
|
|
339
351
|
process.exit(1);
|
|
340
352
|
}
|
|
341
353
|
|
|
354
|
+
if (cmd.rawArgv) {
|
|
355
|
+
// Pass raw argv (everything after the command name) so the command can
|
|
356
|
+
// forward flags like --watch, --inspect verbatim to child processes.
|
|
357
|
+
const cmdIdx = process.argv.indexOf(cmdName);
|
|
358
|
+
const rawArgs = cmdIdx !== -1 ? process.argv.slice(cmdIdx + 1) : positional.slice(1);
|
|
359
|
+
await cmd.run(rawArgs);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
342
363
|
if (cmd.run && !cmd.sub) {
|
|
343
364
|
await cmd.run(positional.slice(1), flags);
|
|
344
365
|
return;
|
|
@@ -71,13 +71,17 @@ function formatMessage(args) {
|
|
|
71
71
|
.join(' ');
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
const { context, trace } = require('@opentelemetry/api');
|
|
75
|
+
|
|
74
76
|
/**
|
|
75
|
-
* Emit a log record
|
|
77
|
+
* Emit a log record, correlating with the active trace/span when available
|
|
76
78
|
*/
|
|
77
79
|
function emitLog(severityNumber, severityText, args) {
|
|
78
80
|
const message = formatMessage(args);
|
|
79
81
|
|
|
80
82
|
try {
|
|
83
|
+
const activeCtx = context.active();
|
|
84
|
+
const spanCtx = trace.getSpanContext(activeCtx);
|
|
81
85
|
logger.emit({
|
|
82
86
|
severityNumber,
|
|
83
87
|
severityText,
|
|
@@ -86,6 +90,7 @@ function emitLog(severityNumber, severityText, args) {
|
|
|
86
90
|
'log.source': 'console',
|
|
87
91
|
'log.method': severityText.toLowerCase(),
|
|
88
92
|
},
|
|
93
|
+
...(spanCtx && { context: activeCtx }),
|
|
89
94
|
});
|
|
90
95
|
} catch (e) {
|
|
91
96
|
// Silently fail to avoid breaking the application
|