@pristine-ts/observability 2.0.16

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.
Files changed (179) hide show
  1. package/LICENSE +201 -0
  2. package/dist/lib/cjs/interfaces/interfaces.js +19 -0
  3. package/dist/lib/cjs/interfaces/interfaces.js.map +1 -0
  4. package/dist/lib/cjs/interfaces/serialized-span.interface.js +3 -0
  5. package/dist/lib/cjs/interfaces/serialized-span.interface.js.map +1 -0
  6. package/dist/lib/cjs/interfaces/serialized-trace.interface.js +3 -0
  7. package/dist/lib/cjs/interfaces/serialized-trace.interface.js.map +1 -0
  8. package/dist/lib/cjs/loggers/loggers.js +18 -0
  9. package/dist/lib/cjs/loggers/loggers.js.map +1 -0
  10. package/dist/lib/cjs/loggers/observability.logger.js +73 -0
  11. package/dist/lib/cjs/loggers/observability.logger.js.map +1 -0
  12. package/dist/lib/cjs/models/models.js +18 -0
  13. package/dist/lib/cjs/models/models.js.map +1 -0
  14. package/dist/lib/cjs/models/request-summary.model.js +19 -0
  15. package/dist/lib/cjs/models/request-summary.model.js.map +1 -0
  16. package/dist/lib/cjs/models/run-metadata.model.js +18 -0
  17. package/dist/lib/cjs/models/run-metadata.model.js.map +1 -0
  18. package/dist/lib/cjs/observability-configuration.js +49 -0
  19. package/dist/lib/cjs/observability-configuration.js.map +1 -0
  20. package/dist/lib/cjs/observability.configuration-keys.js +21 -0
  21. package/dist/lib/cjs/observability.configuration-keys.js.map +1 -0
  22. package/dist/lib/cjs/observability.module.js +98 -0
  23. package/dist/lib/cjs/observability.module.js.map +1 -0
  24. package/dist/lib/cjs/observability.module.keyname.js +5 -0
  25. package/dist/lib/cjs/observability.module.keyname.js.map +1 -0
  26. package/dist/lib/cjs/paths/observability-paths.js +78 -0
  27. package/dist/lib/cjs/paths/observability-paths.js.map +1 -0
  28. package/dist/lib/cjs/paths/paths.js +18 -0
  29. package/dist/lib/cjs/paths/paths.js.map +1 -0
  30. package/dist/lib/cjs/serializers/serialized-span.interface.js +3 -0
  31. package/dist/lib/cjs/serializers/serialized-span.interface.js.map +1 -0
  32. package/dist/lib/cjs/serializers/serialized-trace.interface.js +3 -0
  33. package/dist/lib/cjs/serializers/serialized-trace.interface.js.map +1 -0
  34. package/dist/lib/cjs/serializers/serializers.js +19 -0
  35. package/dist/lib/cjs/serializers/serializers.js.map +1 -0
  36. package/dist/lib/cjs/serializers/span-deserializer.js +26 -0
  37. package/dist/lib/cjs/serializers/span-deserializer.js.map +1 -0
  38. package/dist/lib/cjs/serializers/trace-deserializer.js +24 -0
  39. package/dist/lib/cjs/serializers/trace-deserializer.js.map +1 -0
  40. package/dist/lib/cjs/store/log-store.js +249 -0
  41. package/dist/lib/cjs/store/log-store.js.map +1 -0
  42. package/dist/lib/cjs/store/log-tailer.js +110 -0
  43. package/dist/lib/cjs/store/log-tailer.js.map +1 -0
  44. package/dist/lib/cjs/store/observability-paths.js +78 -0
  45. package/dist/lib/cjs/store/observability-paths.js.map +1 -0
  46. package/dist/lib/cjs/store/observability-run-manager.js +346 -0
  47. package/dist/lib/cjs/store/observability-run-manager.js.map +1 -0
  48. package/dist/lib/cjs/store/observability-store-reader.js +174 -0
  49. package/dist/lib/cjs/store/observability-store-reader.js.map +1 -0
  50. package/dist/lib/cjs/store/serializers/serialized-span.interface.js +3 -0
  51. package/dist/lib/cjs/store/serializers/serialized-span.interface.js.map +1 -0
  52. package/dist/lib/cjs/store/serializers/serialized-trace.interface.js +3 -0
  53. package/dist/lib/cjs/store/serializers/serialized-trace.interface.js.map +1 -0
  54. package/dist/lib/cjs/store/serializers/span-deserializer.js +26 -0
  55. package/dist/lib/cjs/store/serializers/span-deserializer.js.map +1 -0
  56. package/dist/lib/cjs/store/serializers/trace-deserializer.js +24 -0
  57. package/dist/lib/cjs/store/serializers/trace-deserializer.js.map +1 -0
  58. package/dist/lib/cjs/store/store.js +19 -0
  59. package/dist/lib/cjs/store/store.js.map +1 -0
  60. package/dist/lib/cjs/store/trace-deserializer.js +41 -0
  61. package/dist/lib/cjs/store/trace-deserializer.js.map +1 -0
  62. package/dist/lib/cjs/store/trace-store.js +281 -0
  63. package/dist/lib/cjs/store/trace-store.js.map +1 -0
  64. package/dist/lib/cjs/tailers/log-tailer.js +110 -0
  65. package/dist/lib/cjs/tailers/log-tailer.js.map +1 -0
  66. package/dist/lib/cjs/tailers/tailers.js +18 -0
  67. package/dist/lib/cjs/tailers/tailers.js.map +1 -0
  68. package/dist/lib/cjs/tracers/observability.tracer.js +65 -0
  69. package/dist/lib/cjs/tracers/observability.tracer.js.map +1 -0
  70. package/dist/lib/cjs/tracers/tracers.js +18 -0
  71. package/dist/lib/cjs/tracers/tracers.js.map +1 -0
  72. package/dist/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  73. package/dist/lib/esm/interfaces/interfaces.js +3 -0
  74. package/dist/lib/esm/interfaces/interfaces.js.map +1 -0
  75. package/dist/lib/esm/interfaces/serialized-span.interface.js +2 -0
  76. package/dist/lib/esm/interfaces/serialized-span.interface.js.map +1 -0
  77. package/dist/lib/esm/interfaces/serialized-trace.interface.js +2 -0
  78. package/dist/lib/esm/interfaces/serialized-trace.interface.js.map +1 -0
  79. package/dist/lib/esm/loggers/loggers.js +2 -0
  80. package/dist/lib/esm/loggers/loggers.js.map +1 -0
  81. package/dist/lib/esm/loggers/observability.logger.js +70 -0
  82. package/dist/lib/esm/loggers/observability.logger.js.map +1 -0
  83. package/dist/lib/esm/models/models.js +2 -0
  84. package/dist/lib/esm/models/models.js.map +1 -0
  85. package/dist/lib/esm/models/request-summary.model.js +15 -0
  86. package/dist/lib/esm/models/request-summary.model.js.map +1 -0
  87. package/dist/lib/esm/models/run-metadata.model.js +14 -0
  88. package/dist/lib/esm/models/run-metadata.model.js.map +1 -0
  89. package/dist/lib/esm/observability-configuration.js +46 -0
  90. package/dist/lib/esm/observability-configuration.js.map +1 -0
  91. package/dist/lib/esm/observability.configuration-keys.js +18 -0
  92. package/dist/lib/esm/observability.configuration-keys.js.map +1 -0
  93. package/dist/lib/esm/observability.module.js +81 -0
  94. package/dist/lib/esm/observability.module.js.map +1 -0
  95. package/dist/lib/esm/observability.module.keyname.js +2 -0
  96. package/dist/lib/esm/observability.module.keyname.js.map +1 -0
  97. package/dist/lib/esm/paths/observability-paths.js +41 -0
  98. package/dist/lib/esm/paths/observability-paths.js.map +1 -0
  99. package/dist/lib/esm/paths/paths.js +2 -0
  100. package/dist/lib/esm/paths/paths.js.map +1 -0
  101. package/dist/lib/esm/serializers/serialized-span.interface.js +2 -0
  102. package/dist/lib/esm/serializers/serialized-span.interface.js.map +1 -0
  103. package/dist/lib/esm/serializers/serialized-trace.interface.js +2 -0
  104. package/dist/lib/esm/serializers/serialized-trace.interface.js.map +1 -0
  105. package/dist/lib/esm/serializers/serializers.js +3 -0
  106. package/dist/lib/esm/serializers/serializers.js.map +1 -0
  107. package/dist/lib/esm/serializers/span-deserializer.js +22 -0
  108. package/dist/lib/esm/serializers/span-deserializer.js.map +1 -0
  109. package/dist/lib/esm/serializers/trace-deserializer.js +20 -0
  110. package/dist/lib/esm/serializers/trace-deserializer.js.map +1 -0
  111. package/dist/lib/esm/store/log-store.js +213 -0
  112. package/dist/lib/esm/store/log-store.js.map +1 -0
  113. package/dist/lib/esm/store/log-tailer.js +73 -0
  114. package/dist/lib/esm/store/log-tailer.js.map +1 -0
  115. package/dist/lib/esm/store/observability-paths.js +41 -0
  116. package/dist/lib/esm/store/observability-paths.js.map +1 -0
  117. package/dist/lib/esm/store/observability-run-manager.js +310 -0
  118. package/dist/lib/esm/store/observability-run-manager.js.map +1 -0
  119. package/dist/lib/esm/store/observability-store-reader.js +138 -0
  120. package/dist/lib/esm/store/observability-store-reader.js.map +1 -0
  121. package/dist/lib/esm/store/serializers/serialized-span.interface.js +2 -0
  122. package/dist/lib/esm/store/serializers/serialized-span.interface.js.map +1 -0
  123. package/dist/lib/esm/store/serializers/serialized-trace.interface.js +2 -0
  124. package/dist/lib/esm/store/serializers/serialized-trace.interface.js.map +1 -0
  125. package/dist/lib/esm/store/serializers/span-deserializer.js +22 -0
  126. package/dist/lib/esm/store/serializers/span-deserializer.js.map +1 -0
  127. package/dist/lib/esm/store/serializers/trace-deserializer.js +20 -0
  128. package/dist/lib/esm/store/serializers/trace-deserializer.js.map +1 -0
  129. package/dist/lib/esm/store/store.js +3 -0
  130. package/dist/lib/esm/store/store.js.map +1 -0
  131. package/dist/lib/esm/store/trace-deserializer.js +37 -0
  132. package/dist/lib/esm/store/trace-deserializer.js.map +1 -0
  133. package/dist/lib/esm/store/trace-store.js +245 -0
  134. package/dist/lib/esm/store/trace-store.js.map +1 -0
  135. package/dist/lib/esm/tailers/log-tailer.js +73 -0
  136. package/dist/lib/esm/tailers/log-tailer.js.map +1 -0
  137. package/dist/lib/esm/tailers/tailers.js +2 -0
  138. package/dist/lib/esm/tailers/tailers.js.map +1 -0
  139. package/dist/lib/esm/tracers/observability.tracer.js +62 -0
  140. package/dist/lib/esm/tracers/observability.tracer.js.map +1 -0
  141. package/dist/lib/esm/tracers/tracers.js +2 -0
  142. package/dist/lib/esm/tracers/tracers.js.map +1 -0
  143. package/dist/lib/esm/tsconfig.tsbuildinfo +1 -0
  144. package/dist/types/interfaces/interfaces.d.ts +2 -0
  145. package/dist/types/interfaces/serialized-span.interface.d.ts +14 -0
  146. package/dist/types/interfaces/serialized-trace.interface.d.ts +14 -0
  147. package/dist/types/loggers/loggers.d.ts +1 -0
  148. package/dist/types/loggers/observability.logger.d.ts +20 -0
  149. package/dist/types/models/models.d.ts +1 -0
  150. package/dist/types/models/request-summary.model.d.ts +52 -0
  151. package/dist/types/models/run-metadata.model.d.ts +28 -0
  152. package/dist/types/observability-configuration.d.ts +32 -0
  153. package/dist/types/observability.configuration-keys.d.ts +32 -0
  154. package/dist/types/observability.module.d.ts +28 -0
  155. package/dist/types/observability.module.keyname.d.ts +1 -0
  156. package/dist/types/paths/observability-paths.d.ts +30 -0
  157. package/dist/types/paths/paths.d.ts +1 -0
  158. package/dist/types/serializers/serialized-span.interface.d.ts +14 -0
  159. package/dist/types/serializers/serialized-trace.interface.d.ts +14 -0
  160. package/dist/types/serializers/serializers.d.ts +2 -0
  161. package/dist/types/serializers/span-deserializer.d.ts +11 -0
  162. package/dist/types/serializers/trace-deserializer.d.ts +9 -0
  163. package/dist/types/store/log-store.d.ts +79 -0
  164. package/dist/types/store/log-tailer.d.ts +27 -0
  165. package/dist/types/store/observability-paths.d.ts +30 -0
  166. package/dist/types/store/observability-run-manager.d.ts +96 -0
  167. package/dist/types/store/observability-store-reader.d.ts +55 -0
  168. package/dist/types/store/serializers/serialized-span.interface.d.ts +14 -0
  169. package/dist/types/store/serializers/serialized-trace.interface.d.ts +14 -0
  170. package/dist/types/store/serializers/span-deserializer.d.ts +11 -0
  171. package/dist/types/store/serializers/trace-deserializer.d.ts +9 -0
  172. package/dist/types/store/store.d.ts +2 -0
  173. package/dist/types/store/trace-deserializer.d.ts +41 -0
  174. package/dist/types/store/trace-store.d.ts +88 -0
  175. package/dist/types/tailers/log-tailer.d.ts +27 -0
  176. package/dist/types/tailers/tailers.d.ts +1 -0
  177. package/dist/types/tracers/observability.tracer.d.ts +16 -0
  178. package/dist/types/tracers/tracers.d.ts +1 -0
  179. package/package.json +60 -0
@@ -0,0 +1,79 @@
1
+ import { LogModel } from "@pristine-ts/logging";
2
+ /**
3
+ * The read/write layer for captured logs. The `ObservabilityLogger` (a `Logger`-tagged
4
+ * transport) calls `append` on every log it sees. The CLI's `logs` command calls `read`
5
+ * / `tail` — addressed by event/trace/request id, never by a partition selector.
6
+ *
7
+ * Internally, each pristine process writes to its own per-process directory (keyed by
8
+ * the kernel instantiation id) so concurrent processes never race on the same file.
9
+ * That partition is invisible to callers: `read` walks every directory newest-first and
10
+ * concatenates; `tail` follows the most-recently-written directory.
11
+ *
12
+ * Singleton so both the writer (logger) and any reader resolved during the same
13
+ * process see the same in-memory state.
14
+ */
15
+ export declare class LogStore {
16
+ private readonly enabled;
17
+ private readonly retainedInstances;
18
+ private readonly partitionId;
19
+ private readonly paths;
20
+ private directoryEnsured;
21
+ private pruned;
22
+ constructor(enabled: boolean, directory: string, retainedInstances: number, partitionId: string);
23
+ /**
24
+ * Whether capture is on. The `LoggerInterface` contract surfaces this through the
25
+ * adapter so `LogHandler` can skip dispatch entirely for disabled stores.
26
+ */
27
+ isCaptureEnabled(): boolean;
28
+ /**
29
+ * Appends one log entry to the current process's `logs.jsonl`. No-op when
30
+ * observability is disabled.
31
+ *
32
+ * The on-disk shape is the JSON-serialized `LogModel` itself — `severity` stays a
33
+ * numeric `SeverityEnum`, `date` becomes an ISO string — so the `logs` command can
34
+ * round-trip through `PrettyLogFormatter`. Stringification is cycle-safe because
35
+ * `log.extra` routinely holds `Span`/`Trace` objects whose `parentSpan` ↔ `children`
36
+ * back-references would otherwise blow up a naive serializer.
37
+ */
38
+ append(log: LogModel): void;
39
+ /**
40
+ * Every captured log entry across every partition, in write order within each
41
+ * partition and partitions concatenated newest-first. When `id` is provided, only
42
+ * entries whose `traceId` / `eventId` / `requestId` match are returned — useful for
43
+ * `pristine logs <id>`.
44
+ */
45
+ read(id?: string): Record<string, any>[];
46
+ /**
47
+ * Follows the most-recently-written partition's `logs.jsonl`, emitting each newly-
48
+ * appended line until the returned handle's `stop()` is called. When `id` is
49
+ * provided, only matching entries surface. Returns a no-op handle when the store
50
+ * has no partitions yet.
51
+ */
52
+ tail(id: string | undefined, onLine: (line: string) => void): {
53
+ stop(): void;
54
+ };
55
+ private ensurePartitionDirectory;
56
+ /**
57
+ * Drops partition directories beyond the retained limit, ordered by `mtime`. Once
58
+ * per process — additional appends are zero-cost. Best-effort: a failure to prune
59
+ * never blocks a write.
60
+ */
61
+ private pruneOldPartitions;
62
+ private partitionsNewestFirst;
63
+ private latestPartition;
64
+ private directoryMtime;
65
+ private readJsonl;
66
+ /**
67
+ * True when any of the entry's correlation fields match the requested id.
68
+ * `requestId` is checked too even though `LogModel` doesn't define it as a field
69
+ * today — JSON deserialization is permissive, so a custom mapper that does set it
70
+ * will round-trip and match.
71
+ */
72
+ private static entryMatchesId;
73
+ /**
74
+ * `JSON.stringify` with a cycle guard — an object seen earlier in the walk is rendered
75
+ * as `"[Circular]"` rather than recursed into. Faithful at any depth, and immune to the
76
+ * `Span.parentSpan` ↔ `Span.children` cycles common in `log.extra`.
77
+ */
78
+ private static safeStringify;
79
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Follows a growing `.jsonl` file and emits each newly-appended complete line. The
3
+ * `logs --follow` command and the REPL use this for a `tail -f`-style live view.
4
+ *
5
+ * Watches the file's parent directory (watching a not-yet-created file directly is
6
+ * unreliable across platforms) and reads the byte delta on each change. Partial trailing
7
+ * lines are buffered until their terminating newline arrives.
8
+ *
9
+ * Not a DI service — a plain utility. Instantiate, `follow()`, and `stop()` when done.
10
+ */
11
+ export declare class LogTailer {
12
+ private readonly filePath;
13
+ private watcher?;
14
+ private offset;
15
+ private buffer;
16
+ constructor(filePath: string);
17
+ /**
18
+ * Starts following. Existing content is skipped — only lines appended after this call
19
+ * are emitted. Call `stop()` to release the watch.
20
+ */
21
+ follow(onLine: (line: string) => void): void;
22
+ /**
23
+ * Stops following and releases the filesystem watch.
24
+ */
25
+ stop(): void;
26
+ private drain;
27
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Resolves every path inside the observability store from the configured store directory.
3
+ * Pure and stateless past construction — instantiate once with the configured directory
4
+ * and reuse.
5
+ *
6
+ * Layout:
7
+ * ```
8
+ * <root>/
9
+ * <instanceId>/logs.jsonl
10
+ * <instanceId>/requests.jsonl
11
+ * <instanceId>/traces/<traceId>.json
12
+ * ```
13
+ *
14
+ * Each `<instanceId>` is one pristine process lifetime (= the kernel instantiation id).
15
+ * No metadata sidecars, no `latest.json` pointer — directory `mtime` answers "which is
16
+ * most recent."
17
+ */
18
+ export declare class ObservabilityPaths {
19
+ /**
20
+ * The absolute store root. The configured directory is resolved against `process.cwd()`
21
+ * when it is not already absolute.
22
+ */
23
+ readonly root: string;
24
+ constructor(configuredDirectory: string);
25
+ instanceDirectory(instanceId: string): string;
26
+ logsFile(instanceId: string): string;
27
+ requestsFile(instanceId: string): string;
28
+ tracesDirectory(instanceId: string): string;
29
+ traceFile(instanceId: string, traceId: string): string;
30
+ }
@@ -0,0 +1,96 @@
1
+ import { ObservabilityConfiguration } from "../observability-configuration";
2
+ /**
3
+ * Owns the lifecycle of an observability run. A "run" is one `pristine start` lifetime;
4
+ * the run directory is keyed by the kernel instantiation id.
5
+ *
6
+ * The observability writers (`ObservabilityLogger`, `ObservabilityTracer`) stay dormant
7
+ * until `beginRun()` is called — so one-shot CLI commands (`build`, `logs`, `trace`) and
8
+ * the REPL never pollute the store. `StartCommand` is the only caller of `beginRun()`.
9
+ *
10
+ * A singleton so the logger, the tracer, and `StartCommand` all share the same run state.
11
+ */
12
+ export declare class ObservabilityRunManager {
13
+ private readonly configuration;
14
+ private readonly runId;
15
+ /**
16
+ * Once the reclaimed run exceeds the budget we trim back to this fraction of it, so the
17
+ * (relatively expensive) reclaim isn't re-triggered on the very next write.
18
+ */
19
+ private static readonly LOW_WATER_FRACTION;
20
+ private readonly paths;
21
+ private activeRunDirectory?;
22
+ /**
23
+ * Approximate running total of bytes written to the active run. Incremented cheaply by
24
+ * the writers via `recordBytesWritten`; resynced to the real on-disk total after each
25
+ * reclaim. Used to decide when to enforce the per-run size budget without stat-ing the
26
+ * whole run directory on every write.
27
+ */
28
+ private runBytes;
29
+ constructor(configuration: ObservabilityConfiguration, runId: string);
30
+ /**
31
+ * Begins a run: creates the run directory, writes `run.json`, repoints `latest.json`,
32
+ * and prunes old runs. No-op when observability is disabled. Safe to call once per
33
+ * process.
34
+ */
35
+ beginRun(command: string): void;
36
+ /**
37
+ * Reports bytes just written to the run by a writer. Cheap — an add and a compare. When
38
+ * the running total crosses the configured `maxRunSizeBytes`, triggers a reclaim that
39
+ * drops the run's oldest data. No-op when no run is active or the cap is disabled.
40
+ */
41
+ recordBytesWritten(bytes: number): void;
42
+ /**
43
+ * Ends the active run by stamping `endedAt` into `run.json`. No-op when no run is active.
44
+ */
45
+ endRun(): void;
46
+ /**
47
+ * Whether a run is currently active. The writers consult this on every write.
48
+ */
49
+ isRunActive(): boolean;
50
+ /**
51
+ * The absolute logs file for the active run, or undefined when no run is active.
52
+ */
53
+ logsFile(): string | undefined;
54
+ /**
55
+ * The absolute requests-index file for the active run, or undefined when no run is active.
56
+ */
57
+ requestsFile(): string | undefined;
58
+ /**
59
+ * The absolute path of the trace file for a given trace id in the active run, or
60
+ * undefined when no run is active.
61
+ */
62
+ traceFile(traceId: string): string | undefined;
63
+ /**
64
+ * Lazily begins a run when `autoBegin` is configured and none is active yet — so a
65
+ * server started outside the `pristine` CLI captures its logs/traces on the first
66
+ * write, with nothing calling `beginRun()` explicitly.
67
+ */
68
+ private ensureAutoBegun;
69
+ /**
70
+ * Drops the run's oldest data until it is back under the budget's low-water mark:
71
+ * oldest trace files first, then the head of `logs.jsonl` (keeping the newest tail).
72
+ * The `requests.jsonl` index is rewritten to drop entries for deleted traces.
73
+ *
74
+ * Best-effort and fully guarded — budget enforcement must never break or lose a write.
75
+ */
76
+ private reclaim;
77
+ /**
78
+ * Rewrites a file in place keeping only its last `keepBytes` bytes, aligned forward to
79
+ * the next newline so no partial line survives.
80
+ */
81
+ private trimFileHead;
82
+ /**
83
+ * Rewrites `requests.jsonl` keeping only entries whose trace file still exists — so the
84
+ * request index stays consistent after trace files are reclaimed.
85
+ */
86
+ private pruneRequestsIndex;
87
+ /**
88
+ * Sum of the sizes of every file directly inside `directory`.
89
+ */
90
+ private directoryTotalSize;
91
+ /**
92
+ * Removes run directories beyond the retained limit, ordered by their
93
+ * `run.json:startedAt`. Best-effort — a failure to prune never blocks a run.
94
+ */
95
+ private pruneOldRuns;
96
+ }
@@ -0,0 +1,55 @@
1
+ import { ObservabilityConfiguration } from "../observability-configuration";
2
+ import { RunMetadata } from "../models/run-metadata.model";
3
+ import { RequestSummary } from "../models/request-summary.model";
4
+ import { SerializedTrace } from "./trace-deserializer";
5
+ /**
6
+ * Read-only access to the observability store. Used by the `logs`, `trace` and `requests`
7
+ * CLI commands. Pure filesystem reads — no kernel, no running app required; it reads what
8
+ * a separate `pristine start` process wrote.
9
+ */
10
+ export declare class ObservabilityStoreReader {
11
+ private readonly paths;
12
+ constructor(configuration: ObservabilityConfiguration);
13
+ /**
14
+ * The run id the `latest.json` pointer references, or undefined when the store is empty.
15
+ */
16
+ latestRunId(): string | undefined;
17
+ /**
18
+ * Resolves an explicit run id, or falls back to the latest run.
19
+ */
20
+ resolveRunId(explicit?: string): string | undefined;
21
+ /**
22
+ * Every run's metadata, most-recent first.
23
+ */
24
+ listRuns(): RunMetadata[];
25
+ /**
26
+ * Reads a run's `run.json`, or undefined when absent/corrupt.
27
+ */
28
+ readRunMetadata(runId: string): RunMetadata | undefined;
29
+ /**
30
+ * The request summaries for a run, most-recent first, optionally capped to `limit`.
31
+ */
32
+ readRequests(runId: string, limit?: number): RequestSummary[];
33
+ /**
34
+ * The raw log entries for a run, in write order. Each is a parsed `logs.jsonl` line.
35
+ */
36
+ readLogs(runId: string): Record<string, any>[];
37
+ /**
38
+ * The absolute `logs.jsonl` path for a run — handed to `LogTailer` for `--follow`.
39
+ */
40
+ logsFilePath(runId: string): string;
41
+ /**
42
+ * Finds and reads a trace by id. Searches the preferred run first (when given), then
43
+ * every other run most-recent first. Returns the parsed trace and the run it was found
44
+ * in, or undefined when no run contains it.
45
+ */
46
+ findTrace(traceId: string, preferredRunId?: string): {
47
+ trace: SerializedTrace;
48
+ runId: string;
49
+ } | undefined;
50
+ /**
51
+ * The most recently seen trace ids across the latest run — used by the REPL completer.
52
+ */
53
+ recentTraceIds(limit: number): string[];
54
+ private readJsonl;
55
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The plain-object shape of a stored span — as produced by `TraceRenderer.serialize`
3
+ * and rehydrated by `SpanDeserializer`.
4
+ */
5
+ export interface SerializedSpan {
6
+ id?: string;
7
+ keyname: string;
8
+ startDate: number;
9
+ endDate?: number;
10
+ context?: {
11
+ [key: string]: string;
12
+ };
13
+ children?: SerializedSpan[];
14
+ }
@@ -0,0 +1,14 @@
1
+ import { SerializedSpan } from "./serialized-span.interface";
2
+ /**
3
+ * The plain-object shape of a stored trace — as written to disk by
4
+ * `ObservabilityTracer` and rehydrated by `TraceDeserializer`.
5
+ */
6
+ export interface SerializedTrace {
7
+ id: string;
8
+ startDate: number;
9
+ endDate?: number;
10
+ context?: {
11
+ [key: string]: string;
12
+ };
13
+ rootSpan?: SerializedSpan;
14
+ }
@@ -0,0 +1,11 @@
1
+ import { Span, Trace } from "@pristine-ts/common";
2
+ import { SerializedSpan } from "./serialized-span.interface";
3
+ /**
4
+ * Rebuilds a `Span` (with its full child tree) from the stored plain object. Used by
5
+ * `TraceDeserializer` for the trace's root span and recursively for every descendant —
6
+ * so `traceRenderer.renderTree`/`renderFlat`, which call instance methods like
7
+ * `getDuration()`, work unchanged on stored traces.
8
+ */
9
+ export declare class SpanDeserializer {
10
+ static deserialize(serialized: SerializedSpan, trace: Trace): Span;
11
+ }
@@ -0,0 +1,9 @@
1
+ import { Trace } from "@pristine-ts/common";
2
+ import { SerializedTrace } from "./serialized-trace.interface";
3
+ /**
4
+ * Rehydrates a stored trace JSON (the shape written by `ObservabilityTracer`) back into
5
+ * a `Trace` instance, with its full span tree rebuilt via `SpanDeserializer`.
6
+ */
7
+ export declare class TraceDeserializer {
8
+ static deserialize(serialized: SerializedTrace): Trace;
9
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./log-store";
2
+ export * from "./trace-store";
@@ -0,0 +1,41 @@
1
+ import { Trace } from "@pristine-ts/common";
2
+ /**
3
+ * The plain-object shape of a stored span (as produced by `TraceRenderer.serialize`).
4
+ */
5
+ interface SerializedSpan {
6
+ id?: string;
7
+ keyname: string;
8
+ startDate: number;
9
+ endDate?: number;
10
+ context?: {
11
+ [key: string]: string;
12
+ };
13
+ children?: SerializedSpan[];
14
+ }
15
+ /**
16
+ * The plain-object shape of a stored trace.
17
+ */
18
+ export interface SerializedTrace {
19
+ id: string;
20
+ startDate: number;
21
+ endDate?: number;
22
+ context?: {
23
+ [key: string]: string;
24
+ };
25
+ rootSpan?: SerializedSpan;
26
+ }
27
+ /**
28
+ * Rehydrates a stored trace JSON (the shape written by `ObservabilityTracer`) back into
29
+ * `Trace`/`Span` instances, so `traceRenderer.renderTree`/`renderFlat` — which call
30
+ * instance methods like `getDuration()` — work unchanged on stored traces.
31
+ *
32
+ * Stateless; instantiate once and reuse, or use the static `deserialize`.
33
+ */
34
+ export declare class TraceDeserializer {
35
+ /**
36
+ * Rebuilds a `Trace` (with its full span tree) from the stored plain object.
37
+ */
38
+ static deserialize(serialized: SerializedTrace): Trace;
39
+ private static deserializeSpan;
40
+ }
41
+ export {};
@@ -0,0 +1,88 @@
1
+ import { Trace } from "@pristine-ts/common";
2
+ import { RequestSummary } from "../models/request-summary.model";
3
+ import { SerializedTrace } from "../interfaces/serialized-trace.interface";
4
+ /**
5
+ * The read/write layer for captured traces. The `ObservabilityTracer` (a `Tracer`-
6
+ * tagged transport) calls `append` on every completed trace; the CLI's `trace` and
7
+ * `requests` commands call `find` / `recentRequests` / `recentTraceIds`. The REPL also
8
+ * reads `recentTraceIds` for its tab-completion.
9
+ *
10
+ * Writes both `traces/<eventId>.json` (the full tree) and an appended one-line
11
+ * `RequestSummary` in `requests.jsonl` (the fast index for `pristine requests`) as a
12
+ * single coupled operation. The summary additionally serves as the lookup table that
13
+ * resolves `requestId` / `traceId` back to the canonical `eventId` when they differ.
14
+ *
15
+ * Internally, each pristine process writes to its own per-process directory (keyed by
16
+ * the kernel instantiation id) so concurrent processes never race. That partition is
17
+ * invisible to callers — `find` and `recentRequests` walk every directory newest-first.
18
+ *
19
+ * Singleton so multiple appenders within the same process share the in-memory `pruned`
20
+ * latch.
21
+ */
22
+ export declare class TraceStore {
23
+ private readonly enabled;
24
+ private readonly retainedInstances;
25
+ private readonly partitionId;
26
+ private readonly paths;
27
+ private directoryEnsured;
28
+ private pruned;
29
+ constructor(enabled: boolean, directory: string, retainedInstances: number, partitionId: string);
30
+ /**
31
+ * Persists a completed trace: writes `traces/<eventId>.json` (the full tree) and
32
+ * appends a `RequestSummary` to `requests.jsonl`. No-op when observability is
33
+ * disabled.
34
+ *
35
+ * The summary's optional `traceId` / `requestId` fields are written only when they
36
+ * differ from the canonical `eventId` (typically only `requestId` for HTTP requests
37
+ * with an `x-pristine-request-id` header that disagreed with the mapper's event id,
38
+ * and `traceId` for distributed-tracing scenarios). The common case is a one-id
39
+ * line — `{eventId, startedAt, durationMs, rootKeyname, ...http}`.
40
+ */
41
+ append(trace: Trace): void;
42
+ /**
43
+ * Finds and rehydrates a trace by any of `eventId` / `traceId` / `requestId`, returning
44
+ * a `Trace` instance with its full span tree rebuilt (instance methods like
45
+ * `getDuration()` work directly). Searches partitions newest-first.
46
+ */
47
+ find(id: string): {
48
+ trace: Trace;
49
+ eventId: string;
50
+ } | undefined;
51
+ /**
52
+ * Same as `find`, but returns the raw stored JSON. The escape hatch for callers that
53
+ * want to render the on-disk shape verbatim — e.g. `pristine trace --format json`.
54
+ */
55
+ findSerialized(id: string): {
56
+ trace: SerializedTrace;
57
+ eventId: string;
58
+ } | undefined;
59
+ /**
60
+ * Recent request summaries across every partition, most-recent first, optionally
61
+ * capped to `limit`.
62
+ */
63
+ recentRequests(limit?: number): RequestSummary[];
64
+ /**
65
+ * The most recent event ids across all partitions — used by the REPL completer.
66
+ */
67
+ recentTraceIds(limit: number): string[];
68
+ private buildSummary;
69
+ /**
70
+ * The canonical id for a trace. Today the trace's own `id` is set from the kernel's
71
+ * event id at trace creation, so they're equal. A custom `context["event.id"]`
72
+ * override wins when present — that's the explicit hook for distributed-tracing setups
73
+ * where the trace id was overwritten from a propagated `traceparent`.
74
+ */
75
+ private eventIdOf;
76
+ private resolveEventIdFromSummaries;
77
+ private tryLoadTrace;
78
+ private readSummaries;
79
+ private ensurePartitionDirectory;
80
+ /**
81
+ * Drops partition directories beyond the retained limit, ordered by `mtime`. Once
82
+ * per process. Best-effort: a failure to prune never blocks a write.
83
+ */
84
+ private pruneOldPartitions;
85
+ private partitionsNewestFirst;
86
+ private directoryMtime;
87
+ private readJsonl;
88
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Follows a growing `.jsonl` file and emits each newly-appended complete line. The
3
+ * `logs --follow` command and the REPL use this for a `tail -f`-style live view.
4
+ *
5
+ * Watches the file's parent directory (watching a not-yet-created file directly is
6
+ * unreliable across platforms) and reads the byte delta on each change. Partial trailing
7
+ * lines are buffered until their terminating newline arrives.
8
+ *
9
+ * Not a DI service — a plain utility. Instantiate, `follow()`, and `stop()` when done.
10
+ */
11
+ export declare class LogTailer {
12
+ private readonly filePath;
13
+ private watcher?;
14
+ private offset;
15
+ private buffer;
16
+ constructor(filePath: string);
17
+ /**
18
+ * Starts following. Existing content is skipped — only lines appended after this call
19
+ * are emitted. Call `stop()` to release the watch.
20
+ */
21
+ follow(onLine: (line: string) => void): void;
22
+ /**
23
+ * Stops following and releases the filesystem watch.
24
+ */
25
+ stop(): void;
26
+ private drain;
27
+ }
@@ -0,0 +1 @@
1
+ export * from "./log-tailer";
@@ -0,0 +1,16 @@
1
+ import { Readable } from "stream";
2
+ import { TracerInterface } from "@pristine-ts/telemetry";
3
+ import { TraceStore } from "../store/trace-store";
4
+ /**
5
+ * A `Tracer` transport that forwards every completed trace to `TraceStore`. Thin adapter
6
+ * — all file I/O, request-summary computation, retention, and per-process partitioning
7
+ * live in `TraceStore`.
8
+ *
9
+ * Crash-isolated: a write failure becomes a stderr line rather than propagating.
10
+ */
11
+ export declare class ObservabilityTracer implements TracerInterface {
12
+ private readonly traceStore;
13
+ traceEndedStream: Readable;
14
+ constructor(traceStore: TraceStore);
15
+ private reportFailure;
16
+ }
@@ -0,0 +1 @@
1
+ export * from "./observability.tracer";
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@pristine-ts/observability",
3
+ "version": "2.0.16",
4
+ "description": "",
5
+ "module": "dist/lib/esm/observability.module.js",
6
+ "main": "dist/lib/cjs/observability.module.js",
7
+ "types": "dist/types/observability.module.d.ts",
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json",
10
+ "prepublish": "npm run build",
11
+ "test": "jest",
12
+ "test:cov": "jest --coverage"
13
+ },
14
+ "dependencies": {
15
+ "@pristine-ts/common": "^2.0.16",
16
+ "@pristine-ts/configuration": "^2.0.16",
17
+ "@pristine-ts/logging": "^2.0.16",
18
+ "@pristine-ts/telemetry": "^2.0.16"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "author": "",
24
+ "license": "ISC",
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "jest": {
29
+ "transform": {
30
+ ".(ts|tsx)": "ts-jest"
31
+ },
32
+ "globals": {
33
+ "ts-jest": {
34
+ "tsconfig": {
35
+ "strictNullChecks": false
36
+ }
37
+ }
38
+ },
39
+ "testEnvironment": "node",
40
+ "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
41
+ "moduleFileExtensions": [
42
+ "ts",
43
+ "tsx",
44
+ "js"
45
+ ],
46
+ "coveragePathIgnorePatterns": [
47
+ "/node_modules/",
48
+ "/test/"
49
+ ],
50
+ "collectCoverageFrom": [
51
+ "src/*.{js,ts}"
52
+ ]
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/magieno/pristine-ts.git",
57
+ "directory": "packages/observability"
58
+ },
59
+ "gitHead": "a55d7d59862672a8cbbca4088fed090779b37705"
60
+ }