shoe-box-spanoptic 0.0.0-stage → 0.0.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/README.md +193 -2
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +131 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +177 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1 -0
- package/dist/src-Cnnn7RBN.mjs +646 -0
- package/dist/src-Cnnn7RBN.mjs.map +1 -0
- package/package.json +63 -4
- package/src/cli.ts +105 -0
- package/src/demo-program.ts +94 -0
- package/src/demo.ts +16 -0
- package/src/index.ts +754 -0
- package/src/trace.ts +144 -0
package/README.md
CHANGED
|
@@ -1,3 +1,194 @@
|
|
|
1
|
-
#
|
|
1
|
+
# shoe-box-spanoptic
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TUI OTEL visualizer for Effect programs. Visualize your Effect traces in real-time with a terminal UI.
|
|
4
|
+
|
|
5
|
+
Inspired by [Kit Langton's spanoptic](https://x.com/kitlangton/status/1994799261406695946).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Real-time trace visualization with animated spinners
|
|
10
|
+
- Interactive span navigation with arrow keys
|
|
11
|
+
- Span details panel showing attributes and logs
|
|
12
|
+
- Live logs panel at the bottom of the screen
|
|
13
|
+
- Export traces to JSON for later replay
|
|
14
|
+
- Replay previously exported traces
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
bun add shoe-box-spanoptic
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Programmatic API
|
|
25
|
+
|
|
26
|
+
Wrap your Effect program with `traceVisualize` to visualize traces:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { BunRuntime } from '@effect/platform-bun';
|
|
30
|
+
import { Effect } from 'effect';
|
|
31
|
+
import { traceVisualize, withTracedSpan } from 'shoe-box-spanoptic';
|
|
32
|
+
import type { TraceExport } from 'shoe-box-spanoptic';
|
|
33
|
+
|
|
34
|
+
let lastExport: TraceExport | undefined;
|
|
35
|
+
|
|
36
|
+
const program = Effect.gen(function* () {
|
|
37
|
+
yield* Effect.log('Starting...');
|
|
38
|
+
|
|
39
|
+
yield* Effect.gen(function* () {
|
|
40
|
+
yield* Effect.log('Fetching data');
|
|
41
|
+
yield* Effect.annotateCurrentSpan('user.id', 123);
|
|
42
|
+
yield* Effect.sleep('500 millis');
|
|
43
|
+
yield* Effect.log('Data fetched');
|
|
44
|
+
return { id: 123, name: 'Alice' };
|
|
45
|
+
}).pipe(withTracedSpan('fetchUser'));
|
|
46
|
+
|
|
47
|
+
yield* Effect.log('Done!');
|
|
48
|
+
}).pipe(withTracedSpan('main'));
|
|
49
|
+
|
|
50
|
+
// Run with trace visualization
|
|
51
|
+
traceVisualize(program, {
|
|
52
|
+
serviceName: 'my-app',
|
|
53
|
+
// Called when the user presses 'e' to export
|
|
54
|
+
onExport: (data) => {
|
|
55
|
+
lastExport = data;
|
|
56
|
+
},
|
|
57
|
+
}).pipe(BunRuntime.runMain);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### CLI
|
|
61
|
+
|
|
62
|
+
The package includes a CLI for running demos and replaying traces:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Run the built-in demo
|
|
66
|
+
bunx shoe-box-spanoptic demo
|
|
67
|
+
|
|
68
|
+
# Run demo and export to a specific file
|
|
69
|
+
bunx shoe-box-spanoptic demo --output trace.json
|
|
70
|
+
|
|
71
|
+
# Replay a previously exported trace
|
|
72
|
+
bunx shoe-box-spanoptic replay trace.json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## TUI Layout
|
|
76
|
+
|
|
77
|
+
- **Left panel**: Trace tree with spans and spinners for running operations
|
|
78
|
+
- **Right panel**: Details for the selected span (attributes, status, duration)
|
|
79
|
+
- **Bottom panel**: Logs for the selected span (updates as you navigate)
|
|
80
|
+
|
|
81
|
+
## TUI Controls
|
|
82
|
+
|
|
83
|
+
| Key | Action |
|
|
84
|
+
| -------------- | ------------------------------------------ |
|
|
85
|
+
| `↑` / `k` | Move selection up |
|
|
86
|
+
| `↓` / `j` | Move selection down |
|
|
87
|
+
| `e` | Export trace (calls `onExport`) |
|
|
88
|
+
| `q` / `Escape` | Quit (once the program has finished) |
|
|
89
|
+
| `Ctrl+C` | Exit immediately |
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### `traceVisualize(effect, options)`
|
|
94
|
+
|
|
95
|
+
Runs an Effect program with OTEL tracing and visualizes it in a TUI. The
|
|
96
|
+
returned effect succeeds with the program's result once the user quits.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const traceVisualize: <A, E, R>(
|
|
100
|
+
effect: Effect.Effect<A, E, R>,
|
|
101
|
+
options: TraceVisualizeOptions,
|
|
102
|
+
) => Effect.Effect<A, E, Exclude<R, Scope.Scope>>;
|
|
103
|
+
|
|
104
|
+
type TraceVisualizeOptions = {
|
|
105
|
+
readonly serviceName: string;
|
|
106
|
+
readonly onExport?: (data: TraceExport) => void;
|
|
107
|
+
};
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Logs are attached to the tracing span that is current when they are emitted,
|
|
111
|
+
so both `Effect.withSpan` and `withTracedSpan` spans collect logs.
|
|
112
|
+
|
|
113
|
+
### `withTracedSpan(name, options?)`
|
|
114
|
+
|
|
115
|
+
Wraps an effect in a tracing span and an Effect log span of the same name. The
|
|
116
|
+
log span adds the span name to console log output when the program runs
|
|
117
|
+
outside the TUI.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { withTracedSpan } from 'shoe-box-spanoptic';
|
|
121
|
+
|
|
122
|
+
const myEffect = Effect.gen(function* () {
|
|
123
|
+
yield* Effect.log('This log will be associated with mySpan');
|
|
124
|
+
return 'result';
|
|
125
|
+
}).pipe(withTracedSpan('mySpan'));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `replayVisualize(data, options?)`
|
|
129
|
+
|
|
130
|
+
Replays a previously exported trace in the TUI.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const replayVisualize: (
|
|
134
|
+
data: TraceExport,
|
|
135
|
+
options?: { readonly onExport?: (data: TraceExport) => void },
|
|
136
|
+
) => Effect.Effect<void>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `encodeTraceExport` / `decodeTraceExport`
|
|
140
|
+
|
|
141
|
+
Convert a `TraceExport` to and from the pretty-printed JSON file the CLI writes
|
|
142
|
+
(`TraceExportJson` is the underlying `Schema`):
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { decodeTraceExport, replayVisualize } from 'shoe-box-spanoptic';
|
|
146
|
+
|
|
147
|
+
const program = Effect.gen(function* () {
|
|
148
|
+
const fs = yield* FileSystem.FileSystem;
|
|
149
|
+
const data = yield* decodeTraceExport(yield* fs.readFileString('trace.json'));
|
|
150
|
+
yield* replayVisualize(data);
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Types
|
|
155
|
+
|
|
156
|
+
`LogEntry`, `SpanInfo` and `TraceExport` are exported both as Effect `Schema`s
|
|
157
|
+
and as their (readonly) types:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
type TraceExport = {
|
|
161
|
+
readonly version: 1;
|
|
162
|
+
readonly serviceName: string;
|
|
163
|
+
readonly exportedAt: number;
|
|
164
|
+
readonly spans: ReadonlyArray<SpanInfo>;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
type SpanInfo = {
|
|
168
|
+
readonly spanId: string;
|
|
169
|
+
readonly parentSpanId?: string | undefined;
|
|
170
|
+
readonly name: string;
|
|
171
|
+
readonly startTime: number;
|
|
172
|
+
readonly endTime: number | null;
|
|
173
|
+
readonly status: 'running' | 'ok' | 'error';
|
|
174
|
+
readonly errorMessage?: string | undefined;
|
|
175
|
+
readonly attributes: { readonly [key: string]: unknown };
|
|
176
|
+
readonly logs: ReadonlyArray<LogEntry>;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
type LogEntry = {
|
|
180
|
+
readonly timestamp: number;
|
|
181
|
+
readonly level: string; // 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | ...
|
|
182
|
+
readonly message: string;
|
|
183
|
+
readonly spanName: string | null;
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Requirements
|
|
188
|
+
|
|
189
|
+
- Effect 4 (`effect` and `@effect/opentelemetry` 4.0.6)
|
|
190
|
+
- Bun (`@opentui/core` loads its native renderer through `bun:ffi`)
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { c as decodeTraceExport, l as encodeTraceExport, n as traceVisualize, r as withTracedSpan, t as replayVisualize } from "./src-Cnnn7RBN.mjs";
|
|
3
|
+
import { Clock, Console, Effect, FileSystem, Option, Random, References } from "effect";
|
|
4
|
+
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
|
5
|
+
import { Argument, Command, Flag } from "effect/cli";
|
|
6
|
+
// src/demo-program.ts
|
|
7
|
+
const randomDelay = (min, max) => Effect.flatMap(Random.nextIntBetween(min, max), (ms) => Effect.sleep(ms));
|
|
8
|
+
const fetchUser = Effect.gen(function* () {
|
|
9
|
+
yield* Effect.log("Fetching user from database...");
|
|
10
|
+
yield* Effect.annotateCurrentSpan("user.id", 1);
|
|
11
|
+
yield* Effect.annotateCurrentSpan("user.role", "admin");
|
|
12
|
+
yield* randomDelay(300, 600);
|
|
13
|
+
yield* Effect.log("User fetched successfully");
|
|
14
|
+
return {
|
|
15
|
+
id: 1,
|
|
16
|
+
name: "Alice"
|
|
17
|
+
};
|
|
18
|
+
}).pipe(withTracedSpan("fetchUser"));
|
|
19
|
+
const processData = Effect.gen(function* () {
|
|
20
|
+
yield* Effect.log("Starting data processing");
|
|
21
|
+
yield* randomDelay(200, 400);
|
|
22
|
+
yield* Effect.logDebug("Processed 42 records");
|
|
23
|
+
yield* Effect.log("Data processing complete");
|
|
24
|
+
}).pipe(withTracedSpan("processData"));
|
|
25
|
+
const connectToDatabase = Effect.gen(function* () {
|
|
26
|
+
yield* Effect.log("Establishing database connection");
|
|
27
|
+
yield* randomDelay(100, 200);
|
|
28
|
+
yield* Effect.log("Connected to PostgreSQL");
|
|
29
|
+
}).pipe(withTracedSpan("connectToDatabase"));
|
|
30
|
+
const queryUsers = Effect.gen(function* () {
|
|
31
|
+
yield* Effect.log("Executing query: SELECT * FROM users");
|
|
32
|
+
yield* Effect.annotateCurrentSpan("db.table", "users");
|
|
33
|
+
yield* Effect.annotateCurrentSpan("db.rows_returned", 42);
|
|
34
|
+
yield* randomDelay(250, 500);
|
|
35
|
+
yield* Effect.logDebug("Query returned 42 rows in 234ms");
|
|
36
|
+
}).pipe(withTracedSpan("queryUsers"));
|
|
37
|
+
const queryOrders = Effect.gen(function* () {
|
|
38
|
+
yield* Effect.log("Executing query: SELECT * FROM orders");
|
|
39
|
+
yield* Effect.annotateCurrentSpan("db.table", "orders");
|
|
40
|
+
yield* Effect.annotateCurrentSpan("db.rows_returned", 156);
|
|
41
|
+
yield* randomDelay(300, 600);
|
|
42
|
+
yield* Effect.logDebug("Query returned 156 rows in 412ms");
|
|
43
|
+
yield* Effect.logWarning("Large result set - consider pagination");
|
|
44
|
+
}).pipe(withTracedSpan("queryOrders"));
|
|
45
|
+
const databaseOperations = Effect.gen(function* () {
|
|
46
|
+
yield* Effect.log("Starting database operations");
|
|
47
|
+
yield* connectToDatabase;
|
|
48
|
+
yield* queryUsers;
|
|
49
|
+
yield* queryOrders;
|
|
50
|
+
yield* Effect.log("Database operations complete");
|
|
51
|
+
}).pipe(withTracedSpan("databaseOperations"));
|
|
52
|
+
const sendNotifications = Effect.gen(function* () {
|
|
53
|
+
yield* Effect.log("Sending notifications to 3 users");
|
|
54
|
+
yield* randomDelay(400, 800);
|
|
55
|
+
yield* Effect.logInfo("Email sent to user@example.com");
|
|
56
|
+
yield* Effect.logInfo("Push notification sent");
|
|
57
|
+
}).pipe(withTracedSpan("sendNotifications"));
|
|
58
|
+
const updateCache = Effect.gen(function* () {
|
|
59
|
+
yield* Effect.log("Invalidating cache entries");
|
|
60
|
+
yield* randomDelay(150, 300);
|
|
61
|
+
yield* Effect.logDebug("Cache key: user:1 invalidated");
|
|
62
|
+
}).pipe(withTracedSpan("updateCache"));
|
|
63
|
+
const trackAnalytics = Effect.gen(function* () {
|
|
64
|
+
yield* Effect.log("Recording analytics event");
|
|
65
|
+
yield* randomDelay(200, 500);
|
|
66
|
+
yield* Effect.logDebug("Event: page_view recorded");
|
|
67
|
+
}).pipe(withTracedSpan("trackAnalytics"));
|
|
68
|
+
const generateReport = Effect.gen(function* () {
|
|
69
|
+
yield* Effect.log("Generating monthly report");
|
|
70
|
+
yield* randomDelay(400, 700);
|
|
71
|
+
yield* Effect.logInfo("Report generated: report_2024_01.pdf");
|
|
72
|
+
yield* Effect.log("Report saved to S3");
|
|
73
|
+
}).pipe(withTracedSpan("generateReport"));
|
|
74
|
+
/** A simulated API request handler: sequential, nested and parallel spans. */
|
|
75
|
+
const demoProgram = Effect.gen(function* () {
|
|
76
|
+
yield* Effect.log("Starting API request handler");
|
|
77
|
+
const user = yield* fetchUser;
|
|
78
|
+
yield* processData;
|
|
79
|
+
yield* databaseOperations;
|
|
80
|
+
yield* Effect.all([
|
|
81
|
+
sendNotifications,
|
|
82
|
+
updateCache,
|
|
83
|
+
trackAnalytics
|
|
84
|
+
], { concurrency: "unbounded" }).pipe(withTracedSpan("sideEffects"));
|
|
85
|
+
yield* generateReport;
|
|
86
|
+
yield* Effect.sleep("200 millis").pipe(withTracedSpan("finalize"));
|
|
87
|
+
yield* Effect.log("Request completed successfully");
|
|
88
|
+
return user;
|
|
89
|
+
}).pipe(withTracedSpan("main"));
|
|
90
|
+
// src/cli.ts
|
|
91
|
+
/**
|
|
92
|
+
* Spanoptic CLI - TUI OTEL visualizer for Effect programs
|
|
93
|
+
*
|
|
94
|
+
* Usage:
|
|
95
|
+
* spanoptic demo [--output <file>] - Run a demo trace visualization
|
|
96
|
+
* spanoptic replay <file> - Replay a previously exported trace
|
|
97
|
+
*/
|
|
98
|
+
const writeTrace = Effect.fnUntraced(function* (data, output) {
|
|
99
|
+
const fs = yield* FileSystem.FileSystem;
|
|
100
|
+
const now = yield* Clock.currentTimeMillis;
|
|
101
|
+
const filename = Option.getOrElse(output, () => `trace-${now}.json`);
|
|
102
|
+
yield* fs.writeFileString(filename, yield* encodeTraceExport(data));
|
|
103
|
+
yield* Console.log(`\nTrace exported to ${filename}`);
|
|
104
|
+
});
|
|
105
|
+
const exportHandler = Effect.fnUntraced(function* (output) {
|
|
106
|
+
const context = yield* Effect.context();
|
|
107
|
+
return (data) => {
|
|
108
|
+
Effect.runForkWith(context)(writeTrace(data, output).pipe(Effect.catch((error) => Console.error(String(error)))));
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
const demoCommand = Command.make("demo", { output: Flag.File("output").pipe(Flag.withAlias("o"), Flag.withDescription("Where to write the trace when you press e"), Flag.optional) }, Effect.fnUntraced(function* ({ output }) {
|
|
112
|
+
yield* traceVisualize(demoProgram, {
|
|
113
|
+
serviceName: "spanoptic-demo",
|
|
114
|
+
onExport: yield* exportHandler(output)
|
|
115
|
+
});
|
|
116
|
+
})).pipe(Command.withDescription("Run a demo trace visualization"));
|
|
117
|
+
const replayCommand = Command.make("replay", { file: Argument.File("file", { mustExist: true }).pipe(Argument.withDescription("A trace exported with e")) }, Effect.fnUntraced(function* ({ file }) {
|
|
118
|
+
const fs = yield* FileSystem.FileSystem;
|
|
119
|
+
const data = yield* decodeTraceExport(yield* fs.readFileString(file));
|
|
120
|
+
yield* Console.log(`Replaying trace from ${file}`);
|
|
121
|
+
yield* Console.log(`Service: ${data.serviceName}`);
|
|
122
|
+
yield* Console.log(`Spans: ${data.spans.length}`);
|
|
123
|
+
yield* Console.log("");
|
|
124
|
+
yield* Effect.sleep("500 millis");
|
|
125
|
+
yield* replayVisualize(data, { onExport: yield* exportHandler(Option.none()) });
|
|
126
|
+
})).pipe(Command.withDescription("Replay a previously exported trace"));
|
|
127
|
+
const spanoptic = Command.make("spanoptic", {}, () => Console.log("Spanoptic - TUI OTEL visualizer for Effect programs\n\nUse --help to see available commands.")).pipe(Command.withDescription("TUI OTEL visualizer for Effect programs"), Command.withSubcommands([demoCommand, replayCommand]));
|
|
128
|
+
Command.run(spanoptic, { version: "0.0.1" }).pipe(Effect.provideService(References.MinimumLogLevel, "None"), Effect.provide(BunServices.layer), BunRuntime.runMain);
|
|
129
|
+
export {};
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/demo-program.ts","../src/cli.ts"],"sourcesContent":["import { Effect, Random } from 'effect';\n\nimport { withTracedSpan } from './index.ts';\n\nconst randomDelay = (min: number, max: number) =>\n Effect.flatMap(Random.nextIntBetween(min, max), (ms) => Effect.sleep(ms));\n\nconst fetchUser = Effect.gen(function* () {\n yield* Effect.log('Fetching user from database...');\n yield* Effect.annotateCurrentSpan('user.id', 1);\n yield* Effect.annotateCurrentSpan('user.role', 'admin');\n yield* randomDelay(300, 600);\n yield* Effect.log('User fetched successfully');\n return { id: 1, name: 'Alice' };\n}).pipe(withTracedSpan('fetchUser'));\n\nconst processData = Effect.gen(function* () {\n yield* Effect.log('Starting data processing');\n yield* randomDelay(200, 400);\n yield* Effect.logDebug('Processed 42 records');\n yield* Effect.log('Data processing complete');\n}).pipe(withTracedSpan('processData'));\n\nconst connectToDatabase = Effect.gen(function* () {\n yield* Effect.log('Establishing database connection');\n yield* randomDelay(100, 200);\n yield* Effect.log('Connected to PostgreSQL');\n}).pipe(withTracedSpan('connectToDatabase'));\n\nconst queryUsers = Effect.gen(function* () {\n yield* Effect.log('Executing query: SELECT * FROM users');\n yield* Effect.annotateCurrentSpan('db.table', 'users');\n yield* Effect.annotateCurrentSpan('db.rows_returned', 42);\n yield* randomDelay(250, 500);\n yield* Effect.logDebug('Query returned 42 rows in 234ms');\n}).pipe(withTracedSpan('queryUsers'));\n\nconst queryOrders = Effect.gen(function* () {\n yield* Effect.log('Executing query: SELECT * FROM orders');\n yield* Effect.annotateCurrentSpan('db.table', 'orders');\n yield* Effect.annotateCurrentSpan('db.rows_returned', 156);\n yield* randomDelay(300, 600);\n yield* Effect.logDebug('Query returned 156 rows in 412ms');\n yield* Effect.logWarning('Large result set - consider pagination');\n}).pipe(withTracedSpan('queryOrders'));\n\nconst databaseOperations = Effect.gen(function* () {\n yield* Effect.log('Starting database operations');\n yield* connectToDatabase;\n yield* queryUsers;\n yield* queryOrders;\n yield* Effect.log('Database operations complete');\n}).pipe(withTracedSpan('databaseOperations'));\n\nconst sendNotifications = Effect.gen(function* () {\n yield* Effect.log('Sending notifications to 3 users');\n yield* randomDelay(400, 800);\n yield* Effect.logInfo('Email sent to user@example.com');\n yield* Effect.logInfo('Push notification sent');\n}).pipe(withTracedSpan('sendNotifications'));\n\nconst updateCache = Effect.gen(function* () {\n yield* Effect.log('Invalidating cache entries');\n yield* randomDelay(150, 300);\n yield* Effect.logDebug('Cache key: user:1 invalidated');\n}).pipe(withTracedSpan('updateCache'));\n\nconst trackAnalytics = Effect.gen(function* () {\n yield* Effect.log('Recording analytics event');\n yield* randomDelay(200, 500);\n yield* Effect.logDebug('Event: page_view recorded');\n}).pipe(withTracedSpan('trackAnalytics'));\n\nconst generateReport = Effect.gen(function* () {\n yield* Effect.log('Generating monthly report');\n yield* randomDelay(400, 700);\n yield* Effect.logInfo('Report generated: report_2024_01.pdf');\n yield* Effect.log('Report saved to S3');\n}).pipe(withTracedSpan('generateReport'));\n\n/** A simulated API request handler: sequential, nested and parallel spans. */\nexport const demoProgram = Effect.gen(function* () {\n yield* Effect.log('Starting API request handler');\n const user = yield* fetchUser;\n yield* processData;\n yield* databaseOperations;\n yield* Effect.all([sendNotifications, updateCache, trackAnalytics], {\n concurrency: 'unbounded',\n }).pipe(withTracedSpan('sideEffects'));\n yield* generateReport;\n yield* Effect.sleep('200 millis').pipe(withTracedSpan('finalize'));\n yield* Effect.log('Request completed successfully');\n return user;\n}).pipe(withTracedSpan('main'));\n","#!/usr/bin/env bun\n/**\n * Spanoptic CLI - TUI OTEL visualizer for Effect programs\n *\n * Usage:\n * spanoptic demo [--output <file>] - Run a demo trace visualization\n * spanoptic replay <file> - Replay a previously exported trace\n */\n\nimport { BunRuntime, BunServices } from '@effect/platform-bun';\nimport { Clock, Console, Effect, FileSystem, Option, References } from 'effect';\nimport { Argument, Command, Flag } from 'effect/cli';\n\nimport { demoProgram } from './demo-program.ts';\nimport {\n decodeTraceExport,\n encodeTraceExport,\n replayVisualize,\n traceVisualize,\n} from './index.ts';\nimport type { TraceExport } from './index.ts';\n\nconst writeTrace = Effect.fnUntraced(function* (\n data: TraceExport,\n output: Option.Option<string>,\n) {\n const fs = yield* FileSystem.FileSystem;\n const now = yield* Clock.currentTimeMillis;\n const filename = Option.getOrElse(output, () => `trace-${now}.json`);\n yield* fs.writeFileString(filename, yield* encodeTraceExport(data));\n yield* Console.log(`\\nTrace exported to ${filename}`);\n});\n\n// onExport is a plain callback fired from a key press, so the write is\n// forked with the command's services rather than awaited by the TUI.\nconst exportHandler = Effect.fnUntraced(function* (\n output: Option.Option<string>,\n) {\n const context = yield* Effect.context<FileSystem.FileSystem>();\n return (data: TraceExport) => {\n Effect.runForkWith(context)(\n writeTrace(data, output).pipe(\n Effect.catch((error) => Console.error(String(error))),\n ),\n );\n };\n});\n\nconst demoCommand = Command.make(\n 'demo',\n {\n output: Flag.File('output').pipe(\n Flag.withAlias('o'),\n Flag.withDescription('Where to write the trace when you press e'),\n Flag.optional,\n ),\n },\n Effect.fnUntraced(function* ({ output }) {\n yield* traceVisualize(demoProgram, {\n serviceName: 'spanoptic-demo',\n onExport: yield* exportHandler(output),\n });\n }),\n).pipe(Command.withDescription('Run a demo trace visualization'));\n\nconst replayCommand = Command.make(\n 'replay',\n {\n file: Argument.File('file', { mustExist: true }).pipe(\n Argument.withDescription('A trace exported with e'),\n ),\n },\n Effect.fnUntraced(function* ({ file }) {\n const fs = yield* FileSystem.FileSystem;\n const data = yield* decodeTraceExport(yield* fs.readFileString(file));\n\n yield* Console.log(`Replaying trace from ${file}`);\n yield* Console.log(`Service: ${data.serviceName}`);\n yield* Console.log(`Spans: ${data.spans.length}`);\n yield* Console.log('');\n // Leave the summary on screen briefly before the TUI takes over.\n yield* Effect.sleep('500 millis');\n\n yield* replayVisualize(data, {\n onExport: yield* exportHandler(Option.none()),\n });\n }),\n).pipe(Command.withDescription('Replay a previously exported trace'));\n\nconst spanoptic = Command.make('spanoptic', {}, () =>\n Console.log(\n 'Spanoptic - TUI OTEL visualizer for Effect programs\\n\\nUse --help to see available commands.',\n ),\n).pipe(\n Command.withDescription('TUI OTEL visualizer for Effect programs'),\n Command.withSubcommands([demoCommand, replayCommand]),\n);\n\nCommand.run(spanoptic, { version: '0.0.1' }).pipe(\n // Effect logs would paint over the TUI; traceVisualize captures its own.\n Effect.provideService(References.MinimumLogLevel, 'None'),\n // oxlint-disable-next-line effecttsgo/strict-effect-provide -- this is the entry point\n Effect.provide(BunServices.layer),\n BunRuntime.runMain,\n);\n"],"mappings":"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"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import * as OtelTracer from "@effect/opentelemetry/OtelTracer";
|
|
2
|
+
import { Effect, Schema, Tracer } from "effect";
|
|
3
|
+
export declare const LogEntry: Schema.Struct<{
|
|
4
|
+
readonly timestamp: Schema.Finite;
|
|
5
|
+
readonly level: Schema.String;
|
|
6
|
+
readonly message: Schema.String;
|
|
7
|
+
readonly spanName: Schema.NullOr<Schema.String>;
|
|
8
|
+
}>;
|
|
9
|
+
export type LogEntry = typeof LogEntry.Type;
|
|
10
|
+
export declare const SpanInfo: Schema.Struct<{
|
|
11
|
+
readonly spanId: Schema.String;
|
|
12
|
+
readonly parentSpanId: Schema.optional<Schema.String>;
|
|
13
|
+
readonly name: Schema.String;
|
|
14
|
+
readonly startTime: Schema.Finite;
|
|
15
|
+
readonly endTime: Schema.NullOr<Schema.Finite>;
|
|
16
|
+
readonly status: Schema.Literals<readonly ["running", "ok", "error"]>;
|
|
17
|
+
readonly errorMessage: Schema.optional<Schema.String>;
|
|
18
|
+
readonly attributes: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
19
|
+
readonly logs: Schema.$Array<Schema.Struct<{
|
|
20
|
+
readonly timestamp: Schema.Finite;
|
|
21
|
+
readonly level: Schema.String;
|
|
22
|
+
readonly message: Schema.String;
|
|
23
|
+
readonly spanName: Schema.NullOr<Schema.String>;
|
|
24
|
+
}>>;
|
|
25
|
+
}>;
|
|
26
|
+
export type SpanInfo = typeof SpanInfo.Type;
|
|
27
|
+
/** Exported trace data for replay. */
|
|
28
|
+
export declare const TraceExport: Schema.Struct<{
|
|
29
|
+
readonly version: Schema.Literal<1>;
|
|
30
|
+
readonly serviceName: Schema.String;
|
|
31
|
+
readonly exportedAt: Schema.Finite;
|
|
32
|
+
readonly spans: Schema.$Array<Schema.Struct<{
|
|
33
|
+
readonly spanId: Schema.String;
|
|
34
|
+
readonly parentSpanId: Schema.optional<Schema.String>;
|
|
35
|
+
readonly name: Schema.String;
|
|
36
|
+
readonly startTime: Schema.Finite;
|
|
37
|
+
readonly endTime: Schema.NullOr<Schema.Finite>;
|
|
38
|
+
readonly status: Schema.Literals<readonly ["running", "ok", "error"]>;
|
|
39
|
+
readonly errorMessage: Schema.optional<Schema.String>;
|
|
40
|
+
readonly attributes: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
41
|
+
readonly logs: Schema.$Array<Schema.Struct<{
|
|
42
|
+
readonly timestamp: Schema.Finite;
|
|
43
|
+
readonly level: Schema.String;
|
|
44
|
+
readonly message: Schema.String;
|
|
45
|
+
readonly spanName: Schema.NullOr<Schema.String>;
|
|
46
|
+
}>>;
|
|
47
|
+
}>>;
|
|
48
|
+
}>;
|
|
49
|
+
export type TraceExport = typeof TraceExport.Type;
|
|
50
|
+
/** A trace export as the pretty-printed JSON file the CLI writes and replays. */
|
|
51
|
+
export declare const TraceExportJson: Schema.fromJsonString<Schema.Struct<{
|
|
52
|
+
readonly version: Schema.Literal<1>;
|
|
53
|
+
readonly serviceName: Schema.String;
|
|
54
|
+
readonly exportedAt: Schema.Finite;
|
|
55
|
+
readonly spans: Schema.$Array<Schema.Struct<{
|
|
56
|
+
readonly spanId: Schema.String;
|
|
57
|
+
readonly parentSpanId: Schema.optional<Schema.String>;
|
|
58
|
+
readonly name: Schema.String;
|
|
59
|
+
readonly startTime: Schema.Finite;
|
|
60
|
+
readonly endTime: Schema.NullOr<Schema.Finite>;
|
|
61
|
+
readonly status: Schema.Literals<readonly ["running", "ok", "error"]>;
|
|
62
|
+
readonly errorMessage: Schema.optional<Schema.String>;
|
|
63
|
+
readonly attributes: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
64
|
+
readonly logs: Schema.$Array<Schema.Struct<{
|
|
65
|
+
readonly timestamp: Schema.Finite;
|
|
66
|
+
readonly level: Schema.String;
|
|
67
|
+
readonly message: Schema.String;
|
|
68
|
+
readonly spanName: Schema.NullOr<Schema.String>;
|
|
69
|
+
}>>;
|
|
70
|
+
}>>;
|
|
71
|
+
}>>;
|
|
72
|
+
export declare const encodeTraceExport: (input: {
|
|
73
|
+
readonly version: 1;
|
|
74
|
+
readonly serviceName: string;
|
|
75
|
+
readonly exportedAt: number;
|
|
76
|
+
readonly spans: readonly {
|
|
77
|
+
readonly spanId: string;
|
|
78
|
+
readonly parentSpanId?: string | undefined;
|
|
79
|
+
readonly name: string;
|
|
80
|
+
readonly startTime: number;
|
|
81
|
+
readonly endTime: number | null;
|
|
82
|
+
readonly status: "error" | "ok" | "running";
|
|
83
|
+
readonly errorMessage?: string | undefined;
|
|
84
|
+
readonly attributes: {
|
|
85
|
+
readonly [x: string]: unknown;
|
|
86
|
+
};
|
|
87
|
+
readonly logs: readonly {
|
|
88
|
+
readonly timestamp: number;
|
|
89
|
+
readonly level: string;
|
|
90
|
+
readonly message: string;
|
|
91
|
+
readonly spanName: string | null;
|
|
92
|
+
}[];
|
|
93
|
+
}[];
|
|
94
|
+
}, options?: import("effect/SchemaAST").ParseOptions) => import("effect/Effect").Effect<string, Schema.SchemaError, never>;
|
|
95
|
+
export declare const decodeTraceExport: (input: string, options?: import("effect/SchemaAST").ParseOptions) => import("effect/Effect").Effect<{
|
|
96
|
+
readonly version: 1;
|
|
97
|
+
readonly serviceName: string;
|
|
98
|
+
readonly exportedAt: number;
|
|
99
|
+
readonly spans: readonly {
|
|
100
|
+
readonly spanId: string;
|
|
101
|
+
readonly parentSpanId?: string | undefined;
|
|
102
|
+
readonly name: string;
|
|
103
|
+
readonly startTime: number;
|
|
104
|
+
readonly endTime: number | null;
|
|
105
|
+
readonly status: "error" | "ok" | "running";
|
|
106
|
+
readonly errorMessage?: string | undefined;
|
|
107
|
+
readonly attributes: {
|
|
108
|
+
readonly [x: string]: unknown;
|
|
109
|
+
};
|
|
110
|
+
readonly logs: readonly {
|
|
111
|
+
readonly timestamp: number;
|
|
112
|
+
readonly level: string;
|
|
113
|
+
readonly message: string;
|
|
114
|
+
readonly spanName: string | null;
|
|
115
|
+
}[];
|
|
116
|
+
}[];
|
|
117
|
+
}, Schema.SchemaError, never>;
|
|
118
|
+
export type TraceVisualizeOptions = {
|
|
119
|
+
readonly serviceName: string;
|
|
120
|
+
/** Called when the user presses `e` to export the trace. */
|
|
121
|
+
readonly onExport?: (data: TraceExport) => void;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Wraps an Effect program with OTEL tracing and visualizes the trace
|
|
125
|
+
* in a real-time TUI with spinners for active spans.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* import { BunRuntime } from "@effect/platform-bun"
|
|
130
|
+
* import { Effect } from "effect"
|
|
131
|
+
* import { traceVisualize, withTracedSpan } from "shoe-box-spanoptic"
|
|
132
|
+
*
|
|
133
|
+
* const program = Effect.gen(function* () {
|
|
134
|
+
* yield* Effect.log("Starting...")
|
|
135
|
+
* yield* Effect.sleep("100 millis")
|
|
136
|
+
* yield* Effect.log("Done!")
|
|
137
|
+
* return "done"
|
|
138
|
+
* }).pipe(withTracedSpan("main"))
|
|
139
|
+
*
|
|
140
|
+
* BunRuntime.runMain(traceVisualize(program, { serviceName: "my-app" }))
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare const traceVisualize: <A, E, R>(effect: Effect.Effect<A, E, R>, options: TraceVisualizeOptions) => Effect.Effect<A, E, Exclude<Exclude<Exclude<R, OtelTracer.OtelTracer>, never>, import("effect/Scope").Scope>>;
|
|
144
|
+
/**
|
|
145
|
+
* Replays a previously exported trace in the TUI.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* import { BunRuntime, BunServices } from "@effect/platform-bun"
|
|
150
|
+
* import { Effect, FileSystem } from "effect"
|
|
151
|
+
* import { decodeTraceExport, replayVisualize } from "shoe-box-spanoptic"
|
|
152
|
+
*
|
|
153
|
+
* const program = Effect.gen(function* () {
|
|
154
|
+
* const fs = yield* FileSystem.FileSystem
|
|
155
|
+
* const data = yield* decodeTraceExport(yield* fs.readFileString("trace.json"))
|
|
156
|
+
* yield* replayVisualize(data)
|
|
157
|
+
* })
|
|
158
|
+
* BunRuntime.runMain(program.pipe(Effect.provide(BunServices.layer)))
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare const replayVisualize: (data: TraceExport, options?: Pick<TraceVisualizeOptions, 'onExport'>) => Effect.Effect<void, never, never>;
|
|
162
|
+
/**
|
|
163
|
+
* Wraps an effect in an Effect log span and a tracing span of the same name.
|
|
164
|
+
* The TUI attaches logs to the current tracing span, so plain
|
|
165
|
+
* `Effect.withSpan` works too; the log span adds the span name to console
|
|
166
|
+
* log output when the program runs outside the TUI.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* const myEffect = Effect.gen(function* () {
|
|
171
|
+
* yield* Effect.log("This log will be associated with mySpan");
|
|
172
|
+
* return "result";
|
|
173
|
+
* }).pipe(withTracedSpan("mySpan"));
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export declare const withTracedSpan: (name: string, options?: Tracer.SpanOptionsNoTrace) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Tracer.ParentSpan>>;
|
|
177
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/trace.ts","../src/index.ts"],"mappings":";;qBAEa,UAAQ,OAAA;;;;;;YAMT,kBAAkB,SAAS;qBAK1B,UAAQ,OAAA;;;;;;;;;;;;;;;;YAYT,kBAAkB,SAAS;;qBAG1B,aAAW,OAAA;;;;;;;;;;;;;;;;;;;;;YAMZ,qBAAqB,YAAY;;qBAGhC,iBAAe,OAAA,eAAA,OAAA;;;;;;;;;;;;;;;;;;;;;qBAEf,oBAAiB;;;;;;;;;;;;;;;;;;;;;;GAAuC,qCAAA,yCAAA,eAAA,OAAA;qBACxD,oBAAiB,eAAA,qCAAA,yCAAA;;;;;;;;;;;;;;;;;;;;;;GAAuC,OAAA;YCiBzD;WACD;;WAEA,YAAY,MAAM;;;;;;;;;;;;;;;;;;;;;;qBA2lBhB,iBAAkB,GAAG,GAAG,GAAC,QAC5B,OAAO,OAAO,GAAG,GAAG,IAAE,SACrB,0BAAqB,OAAA,OAAA,GAAA,GAAA,QAAA,QAAA,QAAA,GAAA,WAAA,4CAAA;;;;;;;;;;;;;;;;;;qBAyDnB,kBAAe,MACpB,aAAW,UACP,KAAK,uCAAkC,OAAA;;;;;;;;;;;;;;;qBA0BtC,iBAAc,cACZ,UAAY,OAAO,wBAC/B,GAAG,GAAG,GAAC,QAAU,OAAO,OAAO,GAAG,GAAG,OAAE,OAAA,OAAA,GAAA,GAAA,QAAA,GAAA,OAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { i as LogEntry, a as SpanInfo, o as TraceExport, s as TraceExportJson, c as decodeTraceExport, l as encodeTraceExport, t as replayVisualize, n as traceVisualize, r as withTracedSpan } from "./src-Cnnn7RBN.mjs";
|