@tracepanel/js 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +271 -2
- package/dist/agent.d.ts +42 -0
- package/dist/agent.js +298 -0
- package/dist/agent.js.map +1 -0
- package/dist/api.d.ts +11 -0
- package/dist/api.js +34 -0
- package/dist/api.js.map +1 -0
- package/dist/browser.d.ts +25 -0
- package/dist/browser.js +438 -0
- package/dist/browser.js.map +1 -0
- package/dist/bullmq.d.ts +7 -0
- package/dist/bullmq.js +86 -0
- package/dist/bullmq.js.map +1 -0
- package/dist/bun.d.ts +15 -0
- package/dist/bun.js +114 -0
- package/dist/bun.js.map +1 -0
- package/dist/calls.d.ts +20 -0
- package/dist/calls.js +145 -0
- package/dist/calls.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +39 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -0
- package/dist/console.d.ts +6 -0
- package/dist/console.js +44 -0
- package/dist/console.js.map +1 -0
- package/dist/explain.d.ts +25 -0
- package/dist/explain.js +145 -0
- package/dist/explain.js.map +1 -0
- package/dist/express.d.ts +2 -0
- package/dist/express.js +77 -0
- package/dist/express.js.map +1 -0
- package/dist/frameworks.d.ts +2 -0
- package/dist/frameworks.js +39 -0
- package/dist/frameworks.js.map +1 -0
- package/dist/headers.d.ts +2 -0
- package/dist/headers.js +42 -0
- package/dist/headers.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/logs.d.ts +8 -0
- package/dist/logs.js +54 -0
- package/dist/logs.js.map +1 -0
- package/dist/model.d.ts +68 -0
- package/dist/model.js +41 -0
- package/dist/model.js.map +1 -0
- package/dist/next-widget.d.ts +3 -0
- package/dist/next-widget.js +17 -0
- package/dist/next-widget.js.map +1 -0
- package/dist/next.d.ts +2 -0
- package/dist/next.js +16 -0
- package/dist/next.js.map +1 -0
- package/dist/nuxt-plugin.d.ts +24 -0
- package/dist/nuxt-plugin.js +39 -0
- package/dist/nuxt-plugin.js.map +1 -0
- package/dist/nuxt.d.ts +10 -0
- package/dist/nuxt.js +16 -0
- package/dist/nuxt.js.map +1 -0
- package/dist/processor.d.ts +105 -0
- package/dist/processor.js +491 -0
- package/dist/processor.js.map +1 -0
- package/dist/register.d.ts +1 -0
- package/dist/register.js +5 -0
- package/dist/register.js.map +1 -0
- package/dist/runtime.d.ts +17 -0
- package/dist/runtime.js +27 -0
- package/dist/runtime.js.map +1 -0
- package/dist/setup.d.ts +10 -0
- package/dist/setup.js +102 -0
- package/dist/setup.js.map +1 -0
- package/dist/sql.d.ts +3 -0
- package/dist/sql.js +18 -0
- package/dist/sql.js.map +1 -0
- package/dist/stack.d.ts +13 -0
- package/dist/stack.js +66 -0
- package/dist/stack.js.map +1 -0
- package/dist/vite.d.ts +36 -0
- package/dist/vite.js +139 -0
- package/dist/vite.js.map +1 -0
- package/dist/widget.d.ts +26 -0
- package/dist/widget.js +70 -0
- package/dist/widget.js.map +1 -0
- package/package.json +125 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DevRealm OÜ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,272 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Tracepanel for Node.js and Bun
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/tracepanel/tracepanel-js/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@tracepanel/js)
|
|
5
|
+
[](https://www.npmjs.com/package/@tracepanel/js)
|
|
6
|
+
|
|
7
|
+
**See what your app does on every request.** Tracepanel records each request
|
|
8
|
+
of your Express, NestJS, Next.js, Nuxt or Bun app with its SQL queries, logs
|
|
9
|
+
and errors. It finds N+1 queries, duplicate queries and slow SQL, and names
|
|
10
|
+
the line of code to fix. You read it in a panel on your pages and in a
|
|
11
|
+
console.
|
|
12
|
+
|
|
13
|
+
It runs while you develop, and everything stays on your machine.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @tracepanel/js
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Tracepanel is on unless `NODE_ENV` is `production`. `TRACEPANEL_ENABLED=0`
|
|
22
|
+
turns it off and `TRACEPANEL_ENABLED=1` turns it on. It starts its agent on
|
|
23
|
+
the first request. The agent keeps the traces in `.tracepanel/` in your
|
|
24
|
+
project and serves the console. Open a page and click the badge in the
|
|
25
|
+
corner.
|
|
26
|
+
|
|
27
|
+
### Express
|
|
28
|
+
|
|
29
|
+
Start the app with Tracepanel loaded before it, so OpenTelemetry can
|
|
30
|
+
instrument its HTTP server, Express and its database driver, and add the
|
|
31
|
+
middleware. It puts the badge on HTML pages and gives every response its
|
|
32
|
+
trace id:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node --import @tracepanel/js/register server.js
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import express from "express";
|
|
40
|
+
import { tracepanel } from "@tracepanel/js/express";
|
|
41
|
+
|
|
42
|
+
const app = express();
|
|
43
|
+
app.use(tracepanel());
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The flag is how Tracepanel gets in before the app. An ES module's imports
|
|
47
|
+
all load before any of its code runs, so `import "@tracepanel/js/register"` at
|
|
48
|
+
the top of `server.js` comes too late for Express and the database driver.
|
|
49
|
+
Without the flag, either let the CLI add it:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{ "scripts": { "dev": "tracepanel run node server.js" } }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
or start from a file that loads Tracepanel, then the app:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
// main.js, run with `node main.js`
|
|
59
|
+
import "@tracepanel/js/register";
|
|
60
|
+
await import("./server.js");
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The agent starts by itself either way, on the app's first request.
|
|
64
|
+
|
|
65
|
+
### NestJS
|
|
66
|
+
|
|
67
|
+
Nest runs on Express: load Tracepanel the same way and add the same
|
|
68
|
+
middleware in `main.ts`. Each request's handler is its controller method,
|
|
69
|
+
such as `BooksController.list`, and the Nest `Logger`'s records go to the
|
|
70
|
+
request's trace. An `HttpException` that answers below 500 is the response,
|
|
71
|
+
not an error.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
node --import @tracepanel/js/register dist/main.js
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { NestFactory } from "@nestjs/core";
|
|
79
|
+
import { tracepanel } from "@tracepanel/js/express";
|
|
80
|
+
import { AppModule } from "./app.module.js";
|
|
81
|
+
|
|
82
|
+
const app = await NestFactory.create(AppModule);
|
|
83
|
+
app.use(tracepanel());
|
|
84
|
+
await app.listen(3000);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Next.js
|
|
88
|
+
|
|
89
|
+
Next.js starts its own server: Tracepanel goes in from the instrumentation
|
|
90
|
+
hook, and Next.js makes each request's root span itself. Keep the package out
|
|
91
|
+
of the bundles, and put the badge in the root layout:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// instrumentation.ts
|
|
95
|
+
export async function register() {
|
|
96
|
+
if (process.env.NEXT_RUNTIME === "nodejs") (await import("@tracepanel/js/next")).register();
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// next.config.ts
|
|
102
|
+
const config = { serverExternalPackages: ["@tracepanel/js"] };
|
|
103
|
+
export default config;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
// app/layout.tsx
|
|
108
|
+
import { Tracepanel } from "@tracepanel/js/next/widget";
|
|
109
|
+
|
|
110
|
+
export default function RootLayout({ children }) {
|
|
111
|
+
return (
|
|
112
|
+
<html lang="en">
|
|
113
|
+
<head>
|
|
114
|
+
<Tracepanel />
|
|
115
|
+
</head>
|
|
116
|
+
<body>{children}</body>
|
|
117
|
+
</html>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A request's handler is its page or route handler, named by its route
|
|
123
|
+
(`/api/books/[id]`).
|
|
124
|
+
|
|
125
|
+
### Nuxt
|
|
126
|
+
|
|
127
|
+
Add the module and start `nuxt dev` with Tracepanel loaded, so it hooks the
|
|
128
|
+
database driver as Nitro loads it:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// nuxt.config.ts
|
|
132
|
+
export default defineNuxtConfig({ modules: ["@tracepanel/js/nuxt"] });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
NODE_OPTIONS=--import=@tracepanel/js/register nuxt dev
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The module names each request by its route (`/api/books/:id`), puts the
|
|
140
|
+
badge on server-rendered pages, and records the errors h3 answers with a 500.
|
|
141
|
+
|
|
142
|
+
### Bun
|
|
143
|
+
|
|
144
|
+
`serve()` is `Bun.serve` with each route traced under its pattern, and
|
|
145
|
+
`withTracepanel()` wraps any fetch handler, such as Hono's `app.fetch`:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
bun --preload @tracepanel/js/register server.ts
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { serve } from "@tracepanel/js/bun";
|
|
153
|
+
|
|
154
|
+
serve({
|
|
155
|
+
routes: {
|
|
156
|
+
"/api/books/:id": async (req) => Response.json(await findBook(req.params.id)),
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### An SPA on Vite
|
|
162
|
+
|
|
163
|
+
A single-page app on Vite's dev server gets the badge from the plugin, whatever
|
|
164
|
+
serves its API: an app Tracepanel traces, in Node or in Python. It puts the
|
|
165
|
+
widget into `index.html` and serves the backend's console on Vite's origin,
|
|
166
|
+
from the backend project's `.tracepanel/`:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
// vite.config.ts
|
|
170
|
+
import { tracepanel } from "@tracepanel/js/vite";
|
|
171
|
+
|
|
172
|
+
export default { plugins: [tracepanel({ dir: "../backend" })] };
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
When the page loads before the API's first request, the plugin starts the
|
|
176
|
+
backend's agent itself, as the SDK would. The console answers this machine
|
|
177
|
+
only, even with Vite on the network (`vite --host`): its traces and what it
|
|
178
|
+
lets a local caller do, such as switching the user, are the developer's.
|
|
179
|
+
|
|
180
|
+
An API on another origin exposes the trace's headers to the page on its CORS
|
|
181
|
+
responses, so the badge lists each call the app makes. A backend that serves
|
|
182
|
+
its own console (Django, FastAPI) is named with `console`:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
tracepanel({ console: "http://localhost:8000/__tracepanel__/" });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The plugin also records the pages in the browser. Each view (the page's load,
|
|
189
|
+
or a route change) is a trace, and so is each action on it: a click or a
|
|
190
|
+
submit that called the API, named after the control (`click "Save"`), or
|
|
191
|
+
background calls nobody started. Each has its calls, its errors and its
|
|
192
|
+
console, with the line that logged each record, and lasts until its last
|
|
193
|
+
call answered. The console lists them in Pages, the page's view first and
|
|
194
|
+
its actions under it, and the badge's panel lists each action over the
|
|
195
|
+
requests it made.
|
|
196
|
+
|
|
197
|
+
A call to the page's own origin carries a W3C `traceparent`, so the console
|
|
198
|
+
links it to the API's trace. `propagateTo` names other origins, whose CORS
|
|
199
|
+
must allow the header:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
tracepanel({ dir: "../backend", browser: { service: "shop-web", propagateTo: ["http://127.0.0.1:8000"] } });
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`browser: false` leaves the recording out. Vite's own client logs (its
|
|
206
|
+
connection, hot reload) stay out of the pages' traces.
|
|
207
|
+
|
|
208
|
+
Without Vite, call `record()` from `@tracepanel/js/browser` in the page, with the
|
|
209
|
+
console on the page's origin (`api`, `/__tracepanel__/` by default).
|
|
210
|
+
`ignoreSources` takes the path prefixes of scripts whose console isn't the
|
|
211
|
+
page's, such as a dev server's client.
|
|
212
|
+
|
|
213
|
+
## What it records
|
|
214
|
+
|
|
215
|
+
- HTTP requests, with the route and the handler that served them.
|
|
216
|
+
- SQL from `pg` and `mysql2` (and so Drizzle, Knex, Sequelize, TypeORM and
|
|
217
|
+
Prisma's driver adapters), with the line of your code that ran each
|
|
218
|
+
statement. Prisma's operations show as steps.
|
|
219
|
+
- Redis commands (`ioredis`, `redis`) and MongoDB commands, with their own
|
|
220
|
+
N+1 and duplicates.
|
|
221
|
+
- Outgoing HTTP calls: `fetch()`, and `node:http` clients such as axios.
|
|
222
|
+
- BullMQ: each job a worker runs is a task, each job the app adds a step.
|
|
223
|
+
- `console` calls made during a request, and Nest's `Logger`.
|
|
224
|
+
- Errors thrown in a handler, with your frames and their source.
|
|
225
|
+
- The caller's trace, when a request carries a W3C `traceparent`: the agent
|
|
226
|
+
links the two.
|
|
227
|
+
|
|
228
|
+
## Your own steps and traces
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
import { span, trace } from "@tracepanel/js";
|
|
232
|
+
|
|
233
|
+
await trace("nightly cleanup", async () => {
|
|
234
|
+
await span("load stale rows", () => loadStaleRows());
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`trace()` makes a script or a scheduled job a trace of its own, and inside a
|
|
239
|
+
request it is a step. `span()` is a step of the current trace. Outside
|
|
240
|
+
development they run the function and nothing else. Run a script with
|
|
241
|
+
`npx tracepanel run node script.js`.
|
|
242
|
+
|
|
243
|
+
## Settings
|
|
244
|
+
|
|
245
|
+
Environment variables, as in the Python SDK:
|
|
246
|
+
|
|
247
|
+
| Variable | Default | What it sets |
|
|
248
|
+
| --- | --- | --- |
|
|
249
|
+
| `TRACEPANEL_ENABLED` | on unless `NODE_ENV=production` | `0` turns Tracepanel off, `1` on |
|
|
250
|
+
| `TRACEPANEL_SERVICE` | `package.json`'s name | the app's name in the console |
|
|
251
|
+
| `TRACEPANEL_DIR` | `.tracepanel` | the project's store, where the agent keeps the traces |
|
|
252
|
+
| `TRACEPANEL_AGENT_BIN` | the one npm installed | the agent to start, such as a build of your own |
|
|
253
|
+
| `TRACEPANEL_AGENT` | `auto` | an agent elsewhere (`tcp:host:port`), never started |
|
|
254
|
+
| `TRACEPANEL_N_PLUS_ONE_THRESHOLD` | `3` | repeats that make an N+1 |
|
|
255
|
+
| `TRACEPANEL_N_PLUS_ONE_MIN_MS` | `5` | for Redis and other fast stores, the time repeats must take together |
|
|
256
|
+
| `TRACEPANEL_SLOW_SQL_MS` | `100` | a slow query, SQL or MongoDB |
|
|
257
|
+
| `TRACEPANEL_SLOW_HTTP_MS` | `500` | a slow outgoing HTTP call |
|
|
258
|
+
| `TRACEPANEL_LONG_TRANSACTION_MS` | `500` | a long transaction |
|
|
259
|
+
| `TRACEPANEL_QUEUE_WAIT_MS` | `1000` | a task that waited long for a worker |
|
|
260
|
+
| `TRACEPANEL_LARGE_RESULT_ROWS` | `1000` | a query without a LIMIT that read too many rows |
|
|
261
|
+
|
|
262
|
+
## Command line
|
|
263
|
+
|
|
264
|
+
| Command | What it does |
|
|
265
|
+
| --- | --- |
|
|
266
|
+
| `tracepanel run <command>` | runs a Node command with Tracepanel on, such as `tracepanel run node server.js` |
|
|
267
|
+
| `tracepanel dev` | runs the agent in the foreground, with the console on http://127.0.0.1:8765/ |
|
|
268
|
+
| `tracepanel mcp` | gives an AI coding agent your traces over MCP |
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
Licensed under the [MIT License](LICENSE).
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Config } from "./config.js";
|
|
2
|
+
import type { Trace } from "./model.js";
|
|
3
|
+
/** Starts the project's agent when none answers, in its own session, serving the console on a free port. */
|
|
4
|
+
export declare class Starter {
|
|
5
|
+
private readonly storeDir;
|
|
6
|
+
private failedAt;
|
|
7
|
+
constructor(storeDir: string);
|
|
8
|
+
/**
|
|
9
|
+
* Whether an agent runs for the project: one already does, or this process starts it (RESTART_MS after a failure).
|
|
10
|
+
* `gone`: the agent at the project's address didn't answer. One that didn't exit cleanly (killed, a reboot) leaves
|
|
11
|
+
* its addresses: they go before a new one starts.
|
|
12
|
+
*/
|
|
13
|
+
ensure(gone?: boolean): boolean;
|
|
14
|
+
private start;
|
|
15
|
+
}
|
|
16
|
+
/** Where the widget loads from and opens the panel: the console the project's agent serves, read every few seconds. */
|
|
17
|
+
export declare class ConsoleUrl {
|
|
18
|
+
private readonly storeDir;
|
|
19
|
+
private url;
|
|
20
|
+
private checked;
|
|
21
|
+
constructor(storeDir: string);
|
|
22
|
+
get(): string;
|
|
23
|
+
}
|
|
24
|
+
/** Batches traces to the agent in the background; a full queue drops a trace rather than slow a request down. */
|
|
25
|
+
export declare class Sender {
|
|
26
|
+
private readonly config;
|
|
27
|
+
private readonly starter;
|
|
28
|
+
private queue;
|
|
29
|
+
private timer;
|
|
30
|
+
private socket;
|
|
31
|
+
private connecting;
|
|
32
|
+
private retryAt;
|
|
33
|
+
private warned;
|
|
34
|
+
constructor(config: Config, starter: Starter);
|
|
35
|
+
submit(trace: Trace): void;
|
|
36
|
+
private flushSoon;
|
|
37
|
+
private flush;
|
|
38
|
+
private connection;
|
|
39
|
+
private open;
|
|
40
|
+
/** A connection the agent accepted after the hello line, or undefined. */
|
|
41
|
+
private handshake;
|
|
42
|
+
}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Traces go to the project's agent, which finds their issues, keeps them and serves the console. Its addresses are in
|
|
2
|
+
// <store>/agent.addr and console.addr. The wire: a hello line, then frames of a 4-byte big-endian length and a JSON
|
|
3
|
+
// array of traces. The agent starts on demand, as the other SDKs start it, and outlives the app's restarts.
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { closeSync, mkdirSync, openSync, readFileSync, rmSync } from "node:fs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { connect } from "node:net";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { explain } from "./explain.js";
|
|
10
|
+
const PROTOCOL = 1;
|
|
11
|
+
const QUEUE_SIZE = 1000;
|
|
12
|
+
const BATCH_SIZE = 50;
|
|
13
|
+
const BATCH_WAIT_MS = 50;
|
|
14
|
+
const RETRY_AFTER_MS = 5000;
|
|
15
|
+
const START_WAIT_MS = 1500;
|
|
16
|
+
const CONSOLE_CHECK_MS = 2000;
|
|
17
|
+
const IDLE_EXIT_S = "1800";
|
|
18
|
+
// An agent that exited (idle, killed, replaced by a new build) is started anew at once; one that couldn't start is
|
|
19
|
+
// tried again after this long, not on every trace.
|
|
20
|
+
const RESTART_MS = 30_000;
|
|
21
|
+
function readAddress(path) {
|
|
22
|
+
try {
|
|
23
|
+
return readFileSync(path, "utf8").trim();
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** TRACEPANEL_AGENT (an agent outside the app's container, tcp:host:port), or the address the project's agent wrote. */
|
|
30
|
+
function agentAddress(storeDir) {
|
|
31
|
+
const named = process.env.TRACEPANEL_AGENT;
|
|
32
|
+
if (named && named !== "auto")
|
|
33
|
+
return named;
|
|
34
|
+
return readAddress(join(storeDir, "agent.addr"));
|
|
35
|
+
}
|
|
36
|
+
function autoStart() {
|
|
37
|
+
const named = process.env.TRACEPANEL_AGENT;
|
|
38
|
+
return !named || named === "auto";
|
|
39
|
+
}
|
|
40
|
+
/** The agent's binary: TRACEPANEL_AGENT_BIN, or the one npm installed with @tracepanel/agent. */
|
|
41
|
+
function agentBinary() {
|
|
42
|
+
try {
|
|
43
|
+
const agent = createRequire(import.meta.url)("@tracepanel/agent");
|
|
44
|
+
return agent.binaryPath();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
warn(error instanceof Error ? error.message : String(error));
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function warn(message) {
|
|
52
|
+
process.stderr.write(`tracepanel: ${message}\n`);
|
|
53
|
+
}
|
|
54
|
+
/** Starts the project's agent when none answers, in its own session, serving the console on a free port. */
|
|
55
|
+
export class Starter {
|
|
56
|
+
storeDir;
|
|
57
|
+
failedAt = Number.NEGATIVE_INFINITY;
|
|
58
|
+
constructor(storeDir) {
|
|
59
|
+
this.storeDir = storeDir;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Whether an agent runs for the project: one already does, or this process starts it (RESTART_MS after a failure).
|
|
63
|
+
* `gone`: the agent at the project's address didn't answer. One that didn't exit cleanly (killed, a reboot) leaves
|
|
64
|
+
* its addresses: they go before a new one starts.
|
|
65
|
+
*/
|
|
66
|
+
ensure(gone = false) {
|
|
67
|
+
if (!gone && agentAddress(this.storeDir))
|
|
68
|
+
return true;
|
|
69
|
+
if (!autoStart() || Date.now() - this.failedAt < RESTART_MS)
|
|
70
|
+
return false;
|
|
71
|
+
if (gone)
|
|
72
|
+
for (const name of ["agent.addr", "console.addr"])
|
|
73
|
+
rmSync(join(this.storeDir, name), { force: true });
|
|
74
|
+
this.start();
|
|
75
|
+
const started = agentAddress(this.storeDir) !== "";
|
|
76
|
+
if (!started)
|
|
77
|
+
this.failedAt = Date.now();
|
|
78
|
+
return started;
|
|
79
|
+
}
|
|
80
|
+
start() {
|
|
81
|
+
const binary = agentBinary();
|
|
82
|
+
if (!binary)
|
|
83
|
+
return;
|
|
84
|
+
mkdirSync(this.storeDir, { recursive: true });
|
|
85
|
+
const log = openSync(join(this.storeDir, "agent.log"), "a");
|
|
86
|
+
try {
|
|
87
|
+
const child = spawn(binary, ["--dir", this.storeDir, "--serve", "127.0.0.1:0", "--idle-exit", IDLE_EXIT_S], {
|
|
88
|
+
detached: true,
|
|
89
|
+
stdio: ["ignore", log, log],
|
|
90
|
+
});
|
|
91
|
+
child.on("error", (error) => warn(`the agent didn't start: ${error.message}`));
|
|
92
|
+
child.unref();
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
closeSync(log);
|
|
96
|
+
}
|
|
97
|
+
const deadline = Date.now() + START_WAIT_MS;
|
|
98
|
+
while (Date.now() < deadline && !agentAddress(this.storeDir)) {
|
|
99
|
+
// The first trace waits a moment for the agent's address: it's the one place a request waits on it.
|
|
100
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
|
|
101
|
+
}
|
|
102
|
+
if (!agentAddress(this.storeDir))
|
|
103
|
+
warn(`the agent didn't start (see ${join(this.storeDir, "agent.log")})`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Where the widget loads from and opens the panel: the console the project's agent serves, read every few seconds. */
|
|
107
|
+
export class ConsoleUrl {
|
|
108
|
+
storeDir;
|
|
109
|
+
url = "";
|
|
110
|
+
checked = 0;
|
|
111
|
+
constructor(storeDir) {
|
|
112
|
+
this.storeDir = storeDir;
|
|
113
|
+
}
|
|
114
|
+
get() {
|
|
115
|
+
const configured = process.env.TRACEPANEL_CONSOLE_URL;
|
|
116
|
+
if (configured)
|
|
117
|
+
return configured.endsWith("/") ? configured : `${configured}/`;
|
|
118
|
+
if (!this.url || Date.now() - this.checked > CONSOLE_CHECK_MS) {
|
|
119
|
+
this.checked = Date.now();
|
|
120
|
+
this.url = readAddress(join(this.storeDir, "console.addr"));
|
|
121
|
+
}
|
|
122
|
+
return this.url;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Batches traces to the agent in the background; a full queue drops a trace rather than slow a request down. */
|
|
126
|
+
export class Sender {
|
|
127
|
+
config;
|
|
128
|
+
starter;
|
|
129
|
+
queue = [];
|
|
130
|
+
timer;
|
|
131
|
+
socket;
|
|
132
|
+
connecting;
|
|
133
|
+
retryAt = 0;
|
|
134
|
+
warned = false;
|
|
135
|
+
constructor(config, starter) {
|
|
136
|
+
this.config = config;
|
|
137
|
+
this.starter = starter;
|
|
138
|
+
}
|
|
139
|
+
submit(trace) {
|
|
140
|
+
if (this.queue.length >= QUEUE_SIZE)
|
|
141
|
+
return;
|
|
142
|
+
this.queue.push(trace);
|
|
143
|
+
if (this.queue.length >= BATCH_SIZE)
|
|
144
|
+
this.flushSoon(0);
|
|
145
|
+
else
|
|
146
|
+
this.flushSoon(BATCH_WAIT_MS);
|
|
147
|
+
}
|
|
148
|
+
flushSoon(delay) {
|
|
149
|
+
if (this.timer && delay > 0)
|
|
150
|
+
return;
|
|
151
|
+
clearTimeout(this.timer);
|
|
152
|
+
this.timer = setTimeout(() => {
|
|
153
|
+
this.timer = undefined;
|
|
154
|
+
void this.flush();
|
|
155
|
+
}, delay);
|
|
156
|
+
this.timer.unref();
|
|
157
|
+
}
|
|
158
|
+
async flush() {
|
|
159
|
+
const batch = this.queue.splice(0, BATCH_SIZE);
|
|
160
|
+
if (batch.length === 0)
|
|
161
|
+
return;
|
|
162
|
+
const socket = await this.connection();
|
|
163
|
+
if (!socket)
|
|
164
|
+
return;
|
|
165
|
+
const data = Buffer.from(JSON.stringify(batch));
|
|
166
|
+
const length = Buffer.alloc(4);
|
|
167
|
+
length.writeUInt32BE(data.length);
|
|
168
|
+
socket.write(Buffer.concat([length, data]));
|
|
169
|
+
if (this.queue.length > 0)
|
|
170
|
+
this.flushSoon(0);
|
|
171
|
+
}
|
|
172
|
+
async connection() {
|
|
173
|
+
if (this.socket && !this.socket.destroyed)
|
|
174
|
+
return this.socket;
|
|
175
|
+
if (Date.now() < this.retryAt)
|
|
176
|
+
return undefined;
|
|
177
|
+
this.connecting ??= this.open().finally(() => {
|
|
178
|
+
this.connecting = undefined;
|
|
179
|
+
});
|
|
180
|
+
const socket = await this.connecting;
|
|
181
|
+
if (!socket) {
|
|
182
|
+
this.retryAt = Date.now() + RETRY_AFTER_MS;
|
|
183
|
+
if (!this.warned && !autoStart()) {
|
|
184
|
+
this.warned = true;
|
|
185
|
+
warn(`no agent at ${agentAddress(this.config.storeDir)}: traces are dropped until it answers`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return socket;
|
|
189
|
+
}
|
|
190
|
+
async open() {
|
|
191
|
+
let socket = await this.handshake(agentAddress(this.config.storeDir));
|
|
192
|
+
if (!socket && this.starter.ensure(true))
|
|
193
|
+
socket = await this.handshake(agentAddress(this.config.storeDir));
|
|
194
|
+
if (socket) {
|
|
195
|
+
this.socket = socket;
|
|
196
|
+
socket.unref();
|
|
197
|
+
socket.on("error", () => socket?.destroy());
|
|
198
|
+
socket.on("close", () => {
|
|
199
|
+
if (this.socket === socket)
|
|
200
|
+
this.socket = undefined;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return socket;
|
|
204
|
+
}
|
|
205
|
+
/** A connection the agent accepted after the hello line, or undefined. */
|
|
206
|
+
handshake(address) {
|
|
207
|
+
const target = address.startsWith("unix:")
|
|
208
|
+
? { path: address.slice("unix:".length) }
|
|
209
|
+
: address.startsWith("tcp:")
|
|
210
|
+
? hostPort(address.slice("tcp:".length))
|
|
211
|
+
: undefined;
|
|
212
|
+
if (!target)
|
|
213
|
+
return Promise.resolve(undefined);
|
|
214
|
+
return new Promise((done) => {
|
|
215
|
+
const socket = connect(target);
|
|
216
|
+
const fail = () => {
|
|
217
|
+
socket.destroy();
|
|
218
|
+
done(undefined);
|
|
219
|
+
};
|
|
220
|
+
socket.setTimeout(500, fail);
|
|
221
|
+
socket.once("error", fail);
|
|
222
|
+
socket.once("connect", () => {
|
|
223
|
+
// The agent finds the issues with this project's limits.
|
|
224
|
+
// It can EXPLAIN a query on the app's database for the console.
|
|
225
|
+
const hello = {
|
|
226
|
+
protocol: PROTOCOL,
|
|
227
|
+
max_traces: this.config.maxTraces,
|
|
228
|
+
thresholds: this.config.thresholds,
|
|
229
|
+
service: this.config.service,
|
|
230
|
+
capabilities: ["explain"],
|
|
231
|
+
};
|
|
232
|
+
socket.write(`${JSON.stringify(hello)}\n`);
|
|
233
|
+
});
|
|
234
|
+
let received = Buffer.alloc(0);
|
|
235
|
+
socket.on("data", function onData(chunk) {
|
|
236
|
+
received = Buffer.concat([received, chunk]);
|
|
237
|
+
const newline = received.indexOf(10);
|
|
238
|
+
if (newline < 0)
|
|
239
|
+
return;
|
|
240
|
+
socket.off("data", onData);
|
|
241
|
+
socket.setTimeout(0);
|
|
242
|
+
socket.removeListener("error", fail);
|
|
243
|
+
const reply = JSON.parse(received.subarray(0, newline).toString("utf8"));
|
|
244
|
+
if (reply.ok) {
|
|
245
|
+
// The agent's requests come down the same connection, a frame each (EXPLAIN on the app's database): what
|
|
246
|
+
// came with the answer is the start of the first.
|
|
247
|
+
readFrames(socket, received.subarray(newline + 1), (request) => void answer(socket, request));
|
|
248
|
+
done(socket);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
warn(`the agent refused: ${reply.error}`);
|
|
252
|
+
fail();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function hostPort(value) {
|
|
259
|
+
const at = value.lastIndexOf(":");
|
|
260
|
+
return { host: value.slice(0, at).replace(/^\[|\]$/g, ""), port: Number(value.slice(at + 1)) };
|
|
261
|
+
}
|
|
262
|
+
function readFrames(socket, start, onFrame) {
|
|
263
|
+
let pending = Buffer.alloc(0);
|
|
264
|
+
const read = (chunk) => {
|
|
265
|
+
pending = Buffer.concat([pending, chunk]);
|
|
266
|
+
while (pending.length >= 4 && pending.length >= 4 + pending.readUInt32BE(0)) {
|
|
267
|
+
const length = pending.readUInt32BE(0);
|
|
268
|
+
const body = pending.subarray(4, 4 + length).toString("utf8");
|
|
269
|
+
pending = pending.subarray(4 + length);
|
|
270
|
+
try {
|
|
271
|
+
onFrame(JSON.parse(body));
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// A frame this SDK can't read: the agent times its request out.
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
socket.on("data", read);
|
|
279
|
+
read(start);
|
|
280
|
+
}
|
|
281
|
+
/** The agent's request run, and its answer sent back up: the rows, or what went wrong. */
|
|
282
|
+
async function answer(socket, request) {
|
|
283
|
+
if (!request.explain)
|
|
284
|
+
return;
|
|
285
|
+
let reply;
|
|
286
|
+
try {
|
|
287
|
+
reply = { answer: request.id, ...(await explain(request.explain)) };
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
reply = { answer: request.id, error: error instanceof Error ? error.message : String(error) };
|
|
291
|
+
}
|
|
292
|
+
const data = Buffer.from(JSON.stringify(reply));
|
|
293
|
+
const length = Buffer.alloc(4);
|
|
294
|
+
length.writeUInt32BE(data.length);
|
|
295
|
+
if (!socket.destroyed)
|
|
296
|
+
socket.write(Buffer.concat([length, data]));
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Attributes } from "@opentelemetry/api";
|
|
2
|
+
export interface TraceOptions {
|
|
3
|
+
/** `command` (a script, a cron job; the default) or `task` (a job a worker took). */
|
|
4
|
+
kind?: "command" | "task";
|
|
5
|
+
/** Attributes of the trace: task.queue, task.id and the like. */
|
|
6
|
+
attrs?: Attributes;
|
|
7
|
+
}
|
|
8
|
+
/** Runs the function as a trace of its own: a script, a scheduled job, a worker's job. Inside a trace, a step. */
|
|
9
|
+
export declare function trace<T>(name: string, fn: () => T | Promise<T>, options?: TraceOptions): Promise<Awaited<T>>;
|
|
10
|
+
/** Runs the function as a step of the current trace, with its SQL, calls and logs inside it. */
|
|
11
|
+
export declare function span<T>(name: string, fn: () => T | Promise<T>, attrs?: Attributes): Promise<Awaited<T>>;
|