@storyshelf/observability 0.7.0 → 0.9.0-next-d2cc082
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 +1 -3
- package/dist/config.d.mts +1 -5
- package/dist/config.mjs +3 -9
- package/dist/config.mjs.map +1 -1
- package/dist/index.mjs +0 -12
- package/dist/instrument-db.d.mts +0 -2
- package/dist/instrument-db.mjs +10 -2
- package/dist/instrument-db.mjs.map +1 -1
- package/dist/instrument-storage.d.mts +0 -2
- package/dist/instrument-storage.mjs +1 -2
- package/dist/logs.d.mts +0 -2
- package/dist/logs.mjs +1 -2
- package/dist/metrics.d.mts +0 -2
- package/dist/metrics.mjs +1 -2
- package/dist/middleware.d.mts +0 -2
- package/dist/middleware.mjs +1 -2
- package/dist/node.d.mts +1 -3
- package/dist/node.mjs +3 -5
- package/dist/node.mjs.map +1 -1
- package/dist/propagate.d.mts +0 -2
- package/dist/propagate.mjs +1 -2
- package/dist/tracing.d.mts +0 -2
- package/dist/tracing.mjs +20 -5
- package/dist/tracing.mjs.map +1 -1
- package/dist/types.d.mts +1 -3
- package/package.json +4 -13
- package/dist/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -48,9 +48,7 @@ await app.lifecycle.setup();
|
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
`storyshelf server init` scaffolds exactly this preamble. Two entrypoints by design: the root is `@opentelemetry/api`
|
|
51
|
-
only (
|
|
52
|
-
must never be imported under Deno (`OTEL_DENO=true` uses Deno's native OTEL
|
|
53
|
-
instead).
|
|
51
|
+
only (runtime-agnostic); `./node` holds the Node SDK lifecycle.
|
|
54
52
|
|
|
55
53
|
## What it exports
|
|
56
54
|
|
package/dist/config.d.mts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
//#region src/config.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Environment-driven observability configuration.
|
|
6
5
|
*
|
|
7
6
|
* Standard `OTEL_*` variables win; `STORYSHELF_OTEL_*` covers StoryShelf
|
|
8
7
|
* specifics. When `OTEL_EXPORTER_OTLP_ENDPOINT` is unset the SDK stays a
|
|
9
|
-
* noop (zero overhead, hermetic tests).
|
|
10
|
-
* Deno's built-in OTEL (`OTEL_DENO=true`) bridges `npm:@opentelemetry/api`
|
|
11
|
-
* natively, so a NodeSDK must never start under Deno.
|
|
8
|
+
* noop (zero overhead, hermetic tests).
|
|
12
9
|
*/
|
|
13
10
|
/** Resolved observability configuration for the Node SDK entrypoint. */
|
|
14
11
|
interface ObservabilityConfig {
|
|
@@ -33,6 +30,5 @@ interface ObservabilityConfig {
|
|
|
33
30
|
* @returns The resolved config; `enabled` is false without an endpoint.
|
|
34
31
|
*/
|
|
35
32
|
declare function resolveObservabilityConfig(env?: NodeJS.ProcessEnv, serviceVersion?: string): ObservabilityConfig;
|
|
36
|
-
//#endregion
|
|
37
33
|
export { ObservabilityConfig, resolveObservabilityConfig };
|
|
38
34
|
//# sourceMappingURL=config.d.mts.map
|
package/dist/config.mjs
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/config.ts
|
|
5
5
|
const DEFAULT_SERVICE_NAME = "storyshelf";
|
|
6
6
|
const DEFAULT_METRICS_INTERVAL_MS = 6e4;
|
|
7
|
-
/** Whether the current runtime is Deno (native OTEL owns the SDK there). */
|
|
8
|
-
function isDenoRuntime(env) {
|
|
9
|
-
if (env["OTEL_DENO"] === "true") return true;
|
|
10
|
-
return globalThis.Deno !== void 0;
|
|
11
|
-
}
|
|
12
7
|
/** Parse one `k=v` pair into the headers record. */
|
|
13
8
|
function parseHeaderPair(pair, headers) {
|
|
14
9
|
const separator = pair.indexOf("=");
|
|
@@ -42,7 +37,7 @@ function resolveObservabilityConfig(env = process.env, serviceVersion) {
|
|
|
42
37
|
const sdkDisabled = env["OTEL_SDK_DISABLED"] === "true";
|
|
43
38
|
const explicit = env["STORYSHELF_OTEL_ENABLED"];
|
|
44
39
|
return {
|
|
45
|
-
enabled: !sdkDisabled &&
|
|
40
|
+
enabled: !sdkDisabled && (explicit ? explicit === "true" : Boolean(endpoint)),
|
|
46
41
|
endpoint,
|
|
47
42
|
headers: parseHeaders(env["OTEL_EXPORTER_OTLP_HEADERS"]),
|
|
48
43
|
serviceName: env["OTEL_SERVICE_NAME"] ?? DEFAULT_SERVICE_NAME,
|
|
@@ -56,9 +51,8 @@ function resolveObservabilityConfig(env = process.env, serviceVersion) {
|
|
|
56
51
|
* runs, where the identifier is undeclared, from throwing.
|
|
57
52
|
*/
|
|
58
53
|
function packageVersion() {
|
|
59
|
-
return "0.
|
|
54
|
+
return "0.9.0-next-d2cc082";
|
|
60
55
|
}
|
|
61
|
-
//#endregion
|
|
62
56
|
export { resolveObservabilityConfig };
|
|
63
57
|
|
|
64
58
|
//# sourceMappingURL=config.mjs.map
|
package/dist/config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Environment-driven observability configuration.\n *\n * Standard `OTEL_*` variables win; `STORYSHELF_OTEL_*` covers StoryShelf\n * specifics. When `OTEL_EXPORTER_OTLP_ENDPOINT` is unset the SDK stays a\n * noop (zero overhead, hermetic tests)
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Environment-driven observability configuration.\n *\n * Standard `OTEL_*` variables win; `STORYSHELF_OTEL_*` covers StoryShelf\n * specifics. When `OTEL_EXPORTER_OTLP_ENDPOINT` is unset the SDK stays a\n * noop (zero overhead, hermetic tests).\n */\n\n/** Resolved observability configuration for the Node SDK entrypoint. */\nexport interface ObservabilityConfig {\n /** Whether to start the SDK (false = noop handle). */\n enabled: boolean;\n /** OTLP/HTTP endpoint, e.g. `http://collector:4318`. */\n endpoint?: string;\n /** Extra OTLP headers (`OTEL_EXPORTER_OTLP_HEADERS`, `k=v,k2=v2`). */\n headers?: Record<string, string>;\n /** Resource `service.name`. */\n serviceName: string;\n /** Resource `service.version`. */\n serviceVersion?: string;\n /** Metric export interval in milliseconds. */\n metricsIntervalMs: number;\n}\n\nconst DEFAULT_SERVICE_NAME = \"storyshelf\";\nconst DEFAULT_METRICS_INTERVAL_MS = 60_000;\n\n/** Parse one `k=v` pair into the headers record. */\nfunction parseHeaderPair(pair: string, headers: Record<string, string>): void {\n const separator = pair.indexOf(\"=\");\n if (separator === -1) {\n return;\n }\n const key = pair.slice(0, separator).trim();\n const value = pair.slice(separator + 1).trim();\n if (key) {\n headers[key] = value;\n }\n}\n\n/** Parse `k=v,k2=v2` header syntax into a record. */\nfunction parseHeaders(raw: string | undefined): Record<string, string> | undefined {\n if (!raw) {\n return undefined;\n }\n const headers: Record<string, string> = {};\n for (const pair of raw.split(\",\")) {\n parseHeaderPair(pair, headers);\n }\n return Object.keys(headers).length === 0 ? undefined : headers;\n}\n\n/** Parse a positive integer env value with a fallback. */\nfunction parseInterval(raw: string | undefined, fallback: number): number {\n const parsed = Number(raw);\n if (!raw || !Number.isFinite(parsed) || parsed <= 0) {\n return fallback;\n }\n return Math.trunc(parsed);\n}\n\n/**\n * Resolve the observability configuration from the environment.\n *\n * @param env - Environment record (defaults to `process.env`).\n * @param serviceVersion - Version for the resource (defaults to `__PKG_VERSION__`).\n * @returns The resolved config; `enabled` is false without an endpoint.\n */\nexport function resolveObservabilityConfig(\n env: NodeJS.ProcessEnv = process.env,\n serviceVersion?: string,\n): ObservabilityConfig {\n const endpoint = env[\"OTEL_EXPORTER_OTLP_ENDPOINT\"];\n const sdkDisabled = env[\"OTEL_SDK_DISABLED\"] === \"true\";\n const explicit = env[\"STORYSHELF_OTEL_ENABLED\"];\n const enabled = !sdkDisabled && (explicit ? explicit === \"true\" : Boolean(endpoint));\n return {\n enabled,\n endpoint,\n headers: parseHeaders(env[\"OTEL_EXPORTER_OTLP_HEADERS\"]),\n serviceName: env[\"OTEL_SERVICE_NAME\"] ?? DEFAULT_SERVICE_NAME,\n serviceVersion: serviceVersion ?? packageVersion(),\n metricsIntervalMs: parseInterval(\n env[\"STORYSHELF_OTEL_METRICS_INTERVAL_MS\"],\n DEFAULT_METRICS_INTERVAL_MS,\n ),\n };\n}\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/**\n * Package version injected at build via the `__PKG_VERSION__` define. Read as a bare identifier\n * (defines don't replace `globalThis` member reads); the `typeof` guard keeps source-condition\n * runs, where the identifier is undeclared, from throwing.\n */\nfunction packageVersion(): string | undefined {\n return typeof __PKG_VERSION__ === \"string\" ? __PKG_VERSION__ : undefined;\n}\n"],"mappings":";;;;AAwBA,MAAM,uBAAuB;AAC7B,MAAM,8BAA8B;;AAGpC,SAAS,gBAAgB,MAAc,SAAuC;CAC5E,MAAM,YAAY,KAAK,QAAQ,GAAG;CAClC,IAAI,cAAc,IAChB;CAEF,MAAM,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC,KAAK;CAC1C,MAAM,QAAQ,KAAK,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK;CAC7C,IAAI,KACF,QAAQ,OAAO;AAEnB;;AAGA,SAAS,aAAa,KAA6D;CACjF,IAAI,CAAC,KACH;CAEF,MAAM,UAAkC,CAAC;CACzC,KAAK,MAAM,QAAQ,IAAI,MAAM,GAAG,GAC9B,gBAAgB,MAAM,OAAO;CAE/B,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,IAAI,KAAA,IAAY;AACzD;;AAGA,SAAS,cAAc,KAAyB,UAA0B;CACxE,MAAM,SAAS,OAAO,GAAG;CACzB,IAAI,CAAC,OAAO,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAChD,OAAO;CAET,OAAO,KAAK,MAAM,MAAM;AAC1B;;;;;;;;AASA,SAAgB,2BACd,MAAyB,QAAQ,KACjC,gBACqB;CACrB,MAAM,WAAW,IAAI;CACrB,MAAM,cAAc,IAAI,yBAAyB;CACjD,MAAM,WAAW,IAAI;CAErB,OAAO;EACL,SAFc,CAAC,gBAAgB,WAAW,aAAa,SAAS,QAAQ,QAAQ;EAGhF;EACA,SAAS,aAAa,IAAI,6BAA6B;EACvD,aAAa,IAAI,wBAAwB;EACzC,gBAAgB,kBAAkB,eAAe;EACjD,mBAAmB,cACjB,IAAI,wCACJ,2BACF;CACF;AACF;;;;;;AASA,SAAS,iBAAqC;CAC5C,OAAA;AACF"}
|
package/dist/index.mjs
CHANGED
|
@@ -9,16 +9,4 @@ import { createHttpMiddleware } from "./middleware.mjs";
|
|
|
9
9
|
import { otelLogMixin } from "./logs.mjs";
|
|
10
10
|
import { TRACEPARENT_HEADER, currentTraceparent, injectTraceContext, parentContext } from "./propagate.mjs";
|
|
11
11
|
import { resolveObservabilityConfig } from "./config.mjs";
|
|
12
|
-
//#region src/index.ts
|
|
13
|
-
/**
|
|
14
|
-
* StoryShelf observability: runtime-agnostic OTEL instrumentation.
|
|
15
|
-
*
|
|
16
|
-
* This entrypoint is cross-runtime (`@opentelemetry/api` only — Node SDK or
|
|
17
|
-
* Deno's native bridge supplies the provider). The Node SDK lifecycle
|
|
18
|
-
* (`initObservabilityFromEnv`) lives under `@storyshelf/observability/node`
|
|
19
|
-
* and must never be imported here.
|
|
20
|
-
*/
|
|
21
|
-
//#endregion
|
|
22
12
|
export { TRACEPARENT_HEADER, captureMetrics, createHttpMiddleware, createInstrumentedDatabase, createInstrumentedStorage, currentTraceparent, dbMetrics, injectTraceContext, otelLogMixin, parentContext, resolveObservabilityConfig, storageMetrics, withSpan };
|
|
23
|
-
|
|
24
|
-
//# sourceMappingURL=index.mjs.map
|
package/dist/instrument-db.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
4
|
-
//#region src/instrument-db.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Wrap a database adapter with per-operation spans and metrics.
|
|
7
6
|
*
|
|
@@ -9,6 +8,5 @@ import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
|
9
8
|
* @returns A transparent adapter emitting `db.*` telemetry.
|
|
10
9
|
*/
|
|
11
10
|
declare function createInstrumentedDatabase(db: DatabaseAdapter): DatabaseAdapter;
|
|
12
|
-
//#endregion
|
|
13
11
|
export { createInstrumentedDatabase };
|
|
14
12
|
//# sourceMappingURL=instrument-db.d.mts.map
|
package/dist/instrument-db.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { dbMetrics } from "./metrics.mjs";
|
|
|
5
5
|
import { withSpan } from "./tracing.mjs";
|
|
6
6
|
import { addTiming } from "@storyshelf/core/utils";
|
|
7
7
|
import { getTableName } from "drizzle-orm";
|
|
8
|
-
|
|
8
|
+
// src/instrument-db.ts
|
|
9
9
|
/**
|
|
10
10
|
* Wrap a database adapter with per-operation spans and metrics.
|
|
11
11
|
*
|
|
@@ -17,8 +17,11 @@ function createInstrumentedDatabase(db) {
|
|
|
17
17
|
}
|
|
18
18
|
var InstrumentedDatabase = class {
|
|
19
19
|
inner;
|
|
20
|
+
transact;
|
|
20
21
|
constructor(inner) {
|
|
21
22
|
this.inner = inner;
|
|
23
|
+
const transact = inner.transact?.bind(inner);
|
|
24
|
+
this.transact = transact ? async (fn) => await trackQuery("db.transact", async () => await transact(fn)) : void 0;
|
|
22
25
|
}
|
|
23
26
|
get metadata() {
|
|
24
27
|
return this.inner.metadata;
|
|
@@ -26,6 +29,9 @@ var InstrumentedDatabase = class {
|
|
|
26
29
|
get lifecycle() {
|
|
27
30
|
return this.inner.lifecycle;
|
|
28
31
|
}
|
|
32
|
+
get buildReferenceLifecycle() {
|
|
33
|
+
return this.inner.buildReferenceLifecycle;
|
|
34
|
+
}
|
|
29
35
|
get tables() {
|
|
30
36
|
return this.inner.tables;
|
|
31
37
|
}
|
|
@@ -38,6 +44,9 @@ var InstrumentedDatabase = class {
|
|
|
38
44
|
async update(table, id, values) {
|
|
39
45
|
return await track("db.update", table, async () => await this.inner.update(table, id, values));
|
|
40
46
|
}
|
|
47
|
+
async updateWhere(table, id, values, where) {
|
|
48
|
+
return await track("db.updateWhere", table, async () => await this.inner.updateWhere(table, id, values, where));
|
|
49
|
+
}
|
|
41
50
|
async get(table, id) {
|
|
42
51
|
return await track("db.get", table, async () => await this.inner.get(table, id));
|
|
43
52
|
}
|
|
@@ -82,7 +91,6 @@ async function trackQuery(operation, fn) {
|
|
|
82
91
|
dbMetrics().operationDuration.record(durationMs, { "db.operation": operation });
|
|
83
92
|
}
|
|
84
93
|
}
|
|
85
|
-
//#endregion
|
|
86
94
|
export { createInstrumentedDatabase };
|
|
87
95
|
|
|
88
96
|
//# sourceMappingURL=instrument-db.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-db.mjs","names":[],"sources":["../src/instrument-db.ts"],"sourcesContent":["/**\n * Database adapter wrapper: one span + duration metric per operation.\n *\n * Span names are `db.<operation>` with the table as an attribute (never in\n * the name). Adapter identity (`metadata`, `lifecycle`, `tables`) and the\n * host-bound logger delegate straight through, so health/setup behave\n * identically with or without observability.\n */\nimport type {
|
|
1
|
+
{"version":3,"file":"instrument-db.mjs","names":[],"sources":["../src/instrument-db.ts"],"sourcesContent":["/**\n * Database adapter wrapper: one span + duration metric per operation.\n *\n * Span names are `db.<operation>` with the table as an attribute (never in\n * the name). Adapter identity (`metadata`, `lifecycle`, `tables`) and the\n * host-bound logger delegate straight through, so health/setup behave\n * identically with or without observability.\n */\nimport type {\n DatabaseAdapter,\n ListOptions,\n Tables,\n TxStore,\n} from \"@storyshelf/core/adapter/database\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { addTiming } from \"@storyshelf/core/utils\";\nimport { getTableName, type SQL, type Table } from \"drizzle-orm\";\nimport { dbMetrics } from \"./metrics.ts\";\nimport { withSpan } from \"./tracing.ts\";\n\n/**\n * Wrap a database adapter with per-operation spans and metrics.\n *\n * @param db - The adapter to wrap (never mutated).\n * @returns A transparent adapter emitting `db.*` telemetry.\n */\nexport function createInstrumentedDatabase(db: DatabaseAdapter): DatabaseAdapter {\n return new InstrumentedDatabase(db);\n}\n\nclass InstrumentedDatabase implements DatabaseAdapter {\n private readonly inner: DatabaseAdapter;\n readonly transact: DatabaseAdapter[\"transact\"];\n\n constructor(inner: DatabaseAdapter) {\n this.inner = inner;\n const transact = inner.transact?.bind(inner);\n this.transact = transact\n ? async <Result>(fn: (tx: TxStore) => Promise<Result>): Promise<Result> =>\n await trackQuery(\"db.transact\", async () => await transact(fn))\n : undefined;\n }\n\n get metadata(): DatabaseAdapter[\"metadata\"] {\n return this.inner.metadata;\n }\n\n get lifecycle(): DatabaseAdapter[\"lifecycle\"] {\n return this.inner.lifecycle;\n }\n\n get buildReferenceLifecycle(): DatabaseAdapter[\"buildReferenceLifecycle\"] {\n return this.inner.buildReferenceLifecycle;\n }\n\n get tables(): Tables {\n return this.inner.tables;\n }\n\n setLogger(logger: Logger): void {\n this.inner.setLogger?.(logger);\n }\n\n async insert<T extends Table>(table: T, values: T[\"$inferInsert\"]): Promise<T[\"$inferSelect\"]> {\n return await track(\"db.insert\", table, async () => await this.inner.insert(table, values));\n }\n\n async update<T extends Table>(\n table: T,\n id: string,\n values: Partial<T[\"$inferInsert\"]>,\n ): Promise<T[\"$inferSelect\"]> {\n return await track(\"db.update\", table, async () => await this.inner.update(table, id, values));\n }\n\n async updateWhere<T extends Table>(\n table: T,\n id: string,\n values: Partial<T[\"$inferInsert\"]>,\n where: SQL,\n ): Promise<T[\"$inferSelect\"] | null> {\n return await track(\n \"db.updateWhere\",\n table,\n async () => await this.inner.updateWhere(table, id, values, where),\n );\n }\n\n async get<T extends Table>(table: T, id: string): Promise<T[\"$inferSelect\"] | null> {\n return await track(\"db.get\", table, async () => await this.inner.get(table, id));\n }\n\n async remove(table: Table, id: string): Promise<void> {\n await track(\"db.remove\", table, async () => {\n await this.inner.remove(table, id);\n });\n }\n\n async list<T extends Table>(table: T, opts?: ListOptions): Promise<T[\"$inferSelect\"][]> {\n return await track(\"db.list\", table, async () => await this.inner.list(table, opts));\n }\n\n async count(table: Table, where?: SQL): Promise<number> {\n return await track(\"db.count\", table, async () => await this.inner.count(table, where));\n }\n\n async all<T>(query: SQL): Promise<T[]> {\n return await trackQuery(\"db.all\", async () => await this.inner.all<T>(query));\n }\n}\n\n/** Run a table operation inside a span with a duration measurement. */\nasync function track<T>(operation: string, table: Table, fn: () => Promise<T>): Promise<T> {\n const tableName = getTableName(table);\n const start = performance.now();\n try {\n return await withSpan(operation, fn, { \"db.table\": tableName });\n } finally {\n const durationMs = performance.now() - start;\n addTiming(\"db\", durationMs);\n dbMetrics().operationDuration.record(durationMs, {\n \"db.operation\": operation,\n \"db.table\": tableName,\n });\n }\n}\n\n/** Run a table-less query inside a span with a duration measurement. */\nasync function trackQuery<T>(operation: string, fn: () => Promise<T>): Promise<T> {\n const start = performance.now();\n try {\n return await withSpan(operation, fn);\n } finally {\n const durationMs = performance.now() - start;\n addTiming(\"db\", durationMs);\n dbMetrics().operationDuration.record(durationMs, {\n \"db.operation\": operation,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA0BA,SAAgB,2BAA2B,IAAsC;CAC/E,OAAO,IAAI,qBAAqB,EAAE;AACpC;AAEA,IAAM,uBAAN,MAAsD;CACpD;CACA;CAEA,YAAY,OAAwB;EAClC,KAAK,QAAQ;EACb,MAAM,WAAW,MAAM,UAAU,KAAK,KAAK;EAC3C,KAAK,WAAW,WACZ,OAAe,OACb,MAAM,WAAW,eAAe,YAAY,MAAM,SAAS,EAAE,CAAC,IAChE,KAAA;CACN;CAEA,IAAI,WAAwC;EAC1C,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,YAA0C;EAC5C,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,0BAAsE;EACxE,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,SAAiB;EACnB,OAAO,KAAK,MAAM;CACpB;CAEA,UAAU,QAAsB;EAC9B,KAAK,MAAM,YAAY,MAAM;CAC/B;CAEA,MAAM,OAAwB,OAAU,QAAuD;EAC7F,OAAO,MAAM,MAAM,aAAa,OAAO,YAAY,MAAM,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC;CAC3F;CAEA,MAAM,OACJ,OACA,IACA,QAC4B;EAC5B,OAAO,MAAM,MAAM,aAAa,OAAO,YAAY,MAAM,KAAK,MAAM,OAAO,OAAO,IAAI,MAAM,CAAC;CAC/F;CAEA,MAAM,YACJ,OACA,IACA,QACA,OACmC;EACnC,OAAO,MAAM,MACX,kBACA,OACA,YAAY,MAAM,KAAK,MAAM,YAAY,OAAO,IAAI,QAAQ,KAAK,CACnE;CACF;CAEA,MAAM,IAAqB,OAAU,IAA+C;EAClF,OAAO,MAAM,MAAM,UAAU,OAAO,YAAY,MAAM,KAAK,MAAM,IAAI,OAAO,EAAE,CAAC;CACjF;CAEA,MAAM,OAAO,OAAc,IAA2B;EACpD,MAAM,MAAM,aAAa,OAAO,YAAY;GAC1C,MAAM,KAAK,MAAM,OAAO,OAAO,EAAE;EACnC,CAAC;CACH;CAEA,MAAM,KAAsB,OAAU,MAAkD;EACtF,OAAO,MAAM,MAAM,WAAW,OAAO,YAAY,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC;CACrF;CAEA,MAAM,MAAM,OAAc,OAA8B;EACtD,OAAO,MAAM,MAAM,YAAY,OAAO,YAAY,MAAM,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC;CACxF;CAEA,MAAM,IAAO,OAA0B;EACrC,OAAO,MAAM,WAAW,UAAU,YAAY,MAAM,KAAK,MAAM,IAAO,KAAK,CAAC;CAC9E;AACF;;AAGA,eAAe,MAAS,WAAmB,OAAc,IAAkC;CACzF,MAAM,YAAY,aAAa,KAAK;CACpC,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI;EACF,OAAO,MAAM,SAAS,WAAW,IAAI,EAAE,YAAY,UAAU,CAAC;CAChE,UAAU;EACR,MAAM,aAAa,YAAY,IAAI,IAAI;EACvC,UAAU,MAAM,UAAU;EAC1B,UAAU,CAAC,CAAC,kBAAkB,OAAO,YAAY;GAC/C,gBAAgB;GAChB,YAAY;EACd,CAAC;CACH;AACF;;AAGA,eAAe,WAAc,WAAmB,IAAkC;CAChF,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI;EACF,OAAO,MAAM,SAAS,WAAW,EAAE;CACrC,UAAU;EACR,MAAM,aAAa,YAAY,IAAI,IAAI;EACvC,UAAU,MAAM,UAAU;EAC1B,UAAU,CAAC,CAAC,kBAAkB,OAAO,YAAY,EAC/C,gBAAgB,UAClB,CAAC;CACH;AACF"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { StorageAdapter } from "@storyshelf/core/adapter/storage";
|
|
4
|
-
//#region src/instrument-storage.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Wrap a storage adapter with per-operation spans and metrics.
|
|
7
6
|
*
|
|
@@ -9,6 +8,5 @@ import { StorageAdapter } from "@storyshelf/core/adapter/storage";
|
|
|
9
8
|
* @returns A transparent adapter emitting `storage.*` telemetry.
|
|
10
9
|
*/
|
|
11
10
|
declare function createInstrumentedStorage(storage: StorageAdapter): StorageAdapter;
|
|
12
|
-
//#endregion
|
|
13
11
|
export { createInstrumentedStorage };
|
|
14
12
|
//# sourceMappingURL=instrument-storage.d.mts.map
|
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { storageMetrics } from "./metrics.mjs";
|
|
5
5
|
import { withSpan } from "./tracing.mjs";
|
|
6
6
|
import { addTiming } from "@storyshelf/core/utils";
|
|
7
|
-
|
|
7
|
+
// src/instrument-storage.ts
|
|
8
8
|
/**
|
|
9
9
|
* Wrap a storage adapter with per-operation spans and metrics.
|
|
10
10
|
*
|
|
@@ -67,7 +67,6 @@ async function track(operation, path, fn) {
|
|
|
67
67
|
storageMetrics().operationDuration.record(durationMs, { "storage.operation": operation });
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
//#endregion
|
|
71
70
|
export { createInstrumentedStorage };
|
|
72
71
|
|
|
73
72
|
//# sourceMappingURL=instrument-storage.mjs.map
|
package/dist/logs.d.mts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
//#region src/logs.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Build the trace-correlation fields for one log line.
|
|
6
5
|
*
|
|
7
6
|
* @returns `{ trace_id, span_id }` when a valid span is active, else `{}`.
|
|
8
7
|
*/
|
|
9
8
|
declare function otelLogMixin(): Record<string, string>;
|
|
10
|
-
//#endregion
|
|
11
9
|
export { otelLogMixin };
|
|
12
10
|
//# sourceMappingURL=logs.d.mts.map
|
package/dist/logs.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { context, trace } from "@opentelemetry/api";
|
|
5
|
-
|
|
5
|
+
// src/logs.ts
|
|
6
6
|
/**
|
|
7
7
|
* Pino log correlation: inject the active trace context into every log line.
|
|
8
8
|
*
|
|
@@ -24,7 +24,6 @@ function otelLogMixin() {
|
|
|
24
24
|
span_id: spanContext.spanId
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
//#endregion
|
|
28
27
|
export { otelLogMixin };
|
|
29
28
|
|
|
30
29
|
//# sourceMappingURL=logs.mjs.map
|
package/dist/metrics.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { Counter, Histogram } from "@opentelemetry/api";
|
|
4
|
-
//#region src/metrics.d.ts
|
|
5
4
|
/** Capture-pipeline instruments. */
|
|
6
5
|
interface CaptureInstruments {
|
|
7
6
|
/** Capture job wall duration in milliseconds (`capture.job.status`). */
|
|
@@ -27,6 +26,5 @@ declare function captureMetrics(): CaptureInstruments;
|
|
|
27
26
|
declare function dbMetrics(): DbInstruments;
|
|
28
27
|
/** Storage-operation instruments (created once). */
|
|
29
28
|
declare function storageMetrics(): StorageInstruments;
|
|
30
|
-
//#endregion
|
|
31
29
|
export { CaptureInstruments, DbInstruments, StorageInstruments, captureMetrics, dbMetrics, storageMetrics };
|
|
32
30
|
//# sourceMappingURL=metrics.d.mts.map
|
package/dist/metrics.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { metrics } from "@opentelemetry/api";
|
|
5
|
-
|
|
5
|
+
// src/metrics.ts
|
|
6
6
|
/**
|
|
7
7
|
* Memoized OTEL instruments (global meter, `storyshelf` scope).
|
|
8
8
|
*
|
|
@@ -38,7 +38,6 @@ function storageMetrics() {
|
|
|
38
38
|
if (!storage) storage = { operationDuration: metrics.getMeter(METER_NAME).createHistogram("storage.operation.duration", { unit: "ms" }) };
|
|
39
39
|
return storage;
|
|
40
40
|
}
|
|
41
|
-
//#endregion
|
|
42
41
|
export { captureMetrics, dbMetrics, storageMetrics };
|
|
43
42
|
|
|
44
43
|
//# sourceMappingURL=metrics.mjs.map
|
package/dist/middleware.d.mts
CHANGED
|
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
|
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { HttpMiddlewareOptions } from "./types.mjs";
|
|
4
4
|
import { MiddlewareHandler } from "hono";
|
|
5
|
-
//#region src/middleware.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Create the instrumenting middleware.
|
|
8
7
|
*
|
|
@@ -13,6 +12,5 @@ import { MiddlewareHandler } from "hono";
|
|
|
13
12
|
* @returns Hono middleware producing one `http.server` span per request.
|
|
14
13
|
*/
|
|
15
14
|
declare function createHttpMiddleware(options?: HttpMiddlewareOptions): MiddlewareHandler;
|
|
16
|
-
//#endregion
|
|
17
15
|
export { createHttpMiddleware };
|
|
18
16
|
//# sourceMappingURL=middleware.d.mts.map
|
package/dist/middleware.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { redactSecrets } from "@storyshelf/core/utils";
|
|
5
5
|
import { context, trace } from "@opentelemetry/api";
|
|
6
6
|
import { httpInstrumentationMiddleware } from "@hono/otel";
|
|
7
|
-
|
|
7
|
+
// src/middleware.ts
|
|
8
8
|
/**
|
|
9
9
|
* Hono HTTP middleware: `@hono/otel` server spans plus StoryShelf context.
|
|
10
10
|
*
|
|
@@ -49,7 +49,6 @@ function createHttpMiddleware(options = {}) {
|
|
|
49
49
|
});
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
-
//#endregion
|
|
53
52
|
export { createHttpMiddleware };
|
|
54
53
|
|
|
55
54
|
//# sourceMappingURL=middleware.mjs.map
|
package/dist/node.d.mts
CHANGED
|
@@ -2,19 +2,17 @@ import __tsdown_shims_path from 'node:path';
|
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { ObservabilityHandle } from "./types.mjs";
|
|
4
4
|
import { ObservabilityConfig, resolveObservabilityConfig } from "./config.mjs";
|
|
5
|
-
//#region src/node.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Initialize observability from the environment.
|
|
8
7
|
*
|
|
9
8
|
* Starts the Node SDK when `OTEL_EXPORTER_OTLP_ENDPOINT` (or
|
|
10
9
|
* `STORYSHELF_OTEL_ENABLED=true`) is set; otherwise returns a noop handle
|
|
11
|
-
* (also
|
|
10
|
+
* (also when `OTEL_SDK_DISABLED=true`). Call before
|
|
12
11
|
* `createShelfApp` and flush via the handle next to `app.lifecycle.teardown`.
|
|
13
12
|
*
|
|
14
13
|
* @param env - Environment record (defaults to `process.env`).
|
|
15
14
|
* @returns The SDK handle (check `enabled`, always call `shutdown`).
|
|
16
15
|
*/
|
|
17
16
|
declare function initObservabilityFromEnv(env?: NodeJS.ProcessEnv): Promise<ObservabilityHandle>;
|
|
18
|
-
//#endregion
|
|
19
17
|
export { type ObservabilityConfig, type ObservabilityHandle, initObservabilityFromEnv, resolveObservabilityConfig };
|
|
20
18
|
//# sourceMappingURL=node.d.mts.map
|
package/dist/node.mjs
CHANGED
|
@@ -8,13 +8,12 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
|
8
8
|
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
9
9
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
10
10
|
import { ATTR_DEPLOYMENT_ENVIRONMENT_NAME, ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
11
|
-
|
|
11
|
+
// src/node.ts
|
|
12
12
|
/**
|
|
13
13
|
* Node-only OTEL SDK lifecycle.
|
|
14
14
|
*
|
|
15
15
|
* Separate entrypoint (`@storyshelf/observability/node`) so the cross-runtime
|
|
16
|
-
* root never pulls Node SDK modules.
|
|
17
|
-
* `initObservabilityFromEnv` noops there and Deno's native OTEL owns export.
|
|
16
|
+
* root never pulls Node SDK modules.
|
|
18
17
|
*/
|
|
19
18
|
/** Noop handle: SDK disabled, nothing to flush. */
|
|
20
19
|
function noopHandle() {
|
|
@@ -66,7 +65,7 @@ function startSdk(config) {
|
|
|
66
65
|
*
|
|
67
66
|
* Starts the Node SDK when `OTEL_EXPORTER_OTLP_ENDPOINT` (or
|
|
68
67
|
* `STORYSHELF_OTEL_ENABLED=true`) is set; otherwise returns a noop handle
|
|
69
|
-
* (also
|
|
68
|
+
* (also when `OTEL_SDK_DISABLED=true`). Call before
|
|
70
69
|
* `createShelfApp` and flush via the handle next to `app.lifecycle.teardown`.
|
|
71
70
|
*
|
|
72
71
|
* @param env - Environment record (defaults to `process.env`).
|
|
@@ -77,7 +76,6 @@ async function initObservabilityFromEnv(env = process.env) {
|
|
|
77
76
|
if (!config.enabled) return noopHandle();
|
|
78
77
|
return startSdk(config);
|
|
79
78
|
}
|
|
80
|
-
//#endregion
|
|
81
79
|
export { initObservabilityFromEnv, resolveObservabilityConfig };
|
|
82
80
|
|
|
83
81
|
//# sourceMappingURL=node.mjs.map
|
package/dist/node.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.mjs","names":[],"sources":["../src/node.ts"],"sourcesContent":["/**\n * Node-only OTEL SDK lifecycle.\n *\n * Separate entrypoint (`@storyshelf/observability/node`) so the cross-runtime\n * root never pulls Node SDK modules
|
|
1
|
+
{"version":3,"file":"node.mjs","names":[],"sources":["../src/node.ts"],"sourcesContent":["/**\n * Node-only OTEL SDK lifecycle.\n *\n * Separate entrypoint (`@storyshelf/observability/node`) so the cross-runtime\n * root never pulls Node SDK modules.\n */\nimport { OTLPMetricExporter } from \"@opentelemetry/exporter-metrics-otlp-http\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { PeriodicExportingMetricReader } from \"@opentelemetry/sdk-metrics\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport {\n ATTR_DEPLOYMENT_ENVIRONMENT_NAME,\n ATTR_SERVICE_NAME,\n ATTR_SERVICE_VERSION,\n} from \"@opentelemetry/semantic-conventions\";\nimport { resolveObservabilityConfig, type ObservabilityConfig } from \"./config.ts\";\nimport type { ObservabilityHandle } from \"./types.ts\";\n\n/** Noop handle: SDK disabled, nothing to flush. */\nfunction noopHandle(): ObservabilityHandle {\n return {\n enabled: false,\n shutdown: async (): Promise<void> => {\n await Promise.resolve();\n },\n };\n}\n\n/** OTLP/HTTP exporter options shared by trace and metric exporters. */\nfunction exporterOptions(config: ObservabilityConfig): {\n url?: string;\n headers?: Record<string, string>;\n} {\n return {\n url: config.endpoint ? `${config.endpoint}/v1/traces` : undefined,\n headers: config.headers,\n };\n}\n\n/** Start the Node SDK wired to the OTLP/HTTP endpoint. */\nfunction startSdk(config: ObservabilityConfig): ObservabilityHandle {\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: config.serviceName,\n ...(config.serviceVersion ? { [ATTR_SERVICE_VERSION]: config.serviceVersion } : {}),\n ...(process.env[\"NODE_ENV\"]\n ? { [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: process.env[\"NODE_ENV\"] }\n : {}),\n });\n const sdk = new NodeSDK({\n resource,\n traceExporter: new OTLPTraceExporter(exporterOptions(config)),\n metricReaders: [\n new PeriodicExportingMetricReader({\n exporter: new OTLPMetricExporter({\n url: config.endpoint ? `${config.endpoint}/v1/metrics` : undefined,\n headers: config.headers,\n }),\n exportIntervalMillis: config.metricsIntervalMs,\n }),\n ],\n });\n sdk.start();\n let shut = false;\n return {\n enabled: true,\n shutdown: async (): Promise<void> => {\n if (shut) {\n return;\n }\n shut = true;\n await sdk.shutdown();\n },\n };\n}\n\n/**\n * Initialize observability from the environment.\n *\n * Starts the Node SDK when `OTEL_EXPORTER_OTLP_ENDPOINT` (or\n * `STORYSHELF_OTEL_ENABLED=true`) is set; otherwise returns a noop handle\n * (also when `OTEL_SDK_DISABLED=true`). Call before\n * `createShelfApp` and flush via the handle next to `app.lifecycle.teardown`.\n *\n * @param env - Environment record (defaults to `process.env`).\n * @returns The SDK handle (check `enabled`, always call `shutdown`).\n */\n// oxlint-disable-next-line typescript/require-await -- async signature; the disabled fast-path is synchronous\nexport async function initObservabilityFromEnv(\n env: NodeJS.ProcessEnv = process.env,\n): Promise<ObservabilityHandle> {\n const config = resolveObservabilityConfig(env);\n if (!config.enabled) {\n return noopHandle();\n }\n return startSdk(config);\n}\n\nexport { resolveObservabilityConfig } from \"./config.ts\";\nexport type { ObservabilityConfig } from \"./config.ts\";\nexport type { ObservabilityHandle } from \"./types.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoBA,SAAS,aAAkC;CACzC,OAAO;EACL,SAAS;EACT,UAAU,YAA2B;GACnC,MAAM,QAAQ,QAAQ;EACxB;CACF;AACF;;AAGA,SAAS,gBAAgB,QAGvB;CACA,OAAO;EACL,KAAK,OAAO,WAAW,GAAG,OAAO,SAAS,cAAc,KAAA;EACxD,SAAS,OAAO;CAClB;AACF;;AAGA,SAAS,SAAS,QAAkD;CAClE,MAAM,WAAW,uBAAuB;GACrC,oBAAoB,OAAO;EAC5B,GAAI,OAAO,iBAAiB,GAAG,uBAAuB,OAAO,eAAe,IAAI,CAAC;EACjF,GAAI,QAAQ,IAAI,cACZ,GAAG,mCAAmC,QAAQ,IAAI,YAAY,IAC9D,CAAC;CACP,CAAC;CACD,MAAM,MAAM,IAAI,QAAQ;EACtB;EACA,eAAe,IAAI,kBAAkB,gBAAgB,MAAM,CAAC;EAC5D,eAAe,CACb,IAAI,8BAA8B;GAChC,UAAU,IAAI,mBAAmB;IAC/B,KAAK,OAAO,WAAW,GAAG,OAAO,SAAS,eAAe,KAAA;IACzD,SAAS,OAAO;GAClB,CAAC;GACD,sBAAsB,OAAO;EAC/B,CAAC,CACH;CACF,CAAC;CACD,IAAI,MAAM;CACV,IAAI,OAAO;CACX,OAAO;EACL,SAAS;EACT,UAAU,YAA2B;GACnC,IAAI,MACF;GAEF,OAAO;GACP,MAAM,IAAI,SAAS;EACrB;CACF;AACF;;;;;;;;;;;;AAcA,eAAsB,yBACpB,MAAyB,QAAQ,KACH;CAC9B,MAAM,SAAS,2BAA2B,GAAG;CAC7C,IAAI,CAAC,OAAO,SACV,OAAO,WAAW;CAEpB,OAAO,SAAS,MAAM;AACxB"}
|
package/dist/propagate.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { Context } from "@opentelemetry/api";
|
|
4
|
-
//#region src/propagate.d.ts
|
|
5
4
|
/** W3C traceparent header name. */
|
|
6
5
|
declare const TRACEPARENT_HEADER = "traceparent";
|
|
7
6
|
/**
|
|
@@ -19,6 +18,5 @@ declare function injectTraceContext(headers: Record<string, string>): void;
|
|
|
19
18
|
* @returns The header value, or undefined when no span is active.
|
|
20
19
|
*/
|
|
21
20
|
declare function currentTraceparent(): string | undefined;
|
|
22
|
-
//#endregion
|
|
23
21
|
export { TRACEPARENT_HEADER, currentTraceparent, injectTraceContext, parentContext };
|
|
24
22
|
//# sourceMappingURL=propagate.d.mts.map
|
package/dist/propagate.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { context, propagation } from "@opentelemetry/api";
|
|
5
|
-
|
|
5
|
+
// src/propagate.ts
|
|
6
6
|
/**
|
|
7
7
|
* W3C trace-context propagation helpers.
|
|
8
8
|
*
|
|
@@ -36,7 +36,6 @@ function currentTraceparent() {
|
|
|
36
36
|
propagation.inject(context.active(), carrier);
|
|
37
37
|
return carrier[TRACEPARENT_HEADER];
|
|
38
38
|
}
|
|
39
|
-
//#endregion
|
|
40
39
|
export { TRACEPARENT_HEADER, currentTraceparent, injectTraceContext, parentContext };
|
|
41
40
|
|
|
42
41
|
//# sourceMappingURL=propagate.mjs.map
|
package/dist/tracing.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { Attributes, Context, Span } from "@opentelemetry/api";
|
|
4
|
-
//#region src/tracing.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Run `fn` inside an internal span.
|
|
7
6
|
*
|
|
@@ -15,6 +14,5 @@ import { Attributes, Context, Span } from "@opentelemetry/api";
|
|
|
15
14
|
* @returns Whatever `fn` returns.
|
|
16
15
|
*/
|
|
17
16
|
declare function withSpan<T>(name: string, fn: (span: Span) => Promise<T>, attributes?: Attributes, parent?: Context): Promise<T>;
|
|
18
|
-
//#endregion
|
|
19
17
|
export { withSpan };
|
|
20
18
|
//# sourceMappingURL=tracing.d.mts.map
|
package/dist/tracing.mjs
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { HttpError, redactSecrets } from "@storyshelf/core/utils";
|
|
4
5
|
import { SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
5
|
-
|
|
6
|
+
// src/tracing.ts
|
|
6
7
|
/**
|
|
7
|
-
* Span helpers over `@opentelemetry/api` (runtime-agnostic
|
|
8
|
-
* Deno's native bridge). Without a configured provider these are cheap
|
|
8
|
+
* Span helpers over `@opentelemetry/api` (runtime-agnostic). Without a configured provider these are cheap
|
|
9
9
|
* noops, so call sites need no enabled-checks.
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* What a span records of a caught error: a copy with the same `name` and a `redactSecrets` message
|
|
13
|
+
* (connection strings, tokens and keys stay out of exported spans), and for an `HttpError` its
|
|
14
|
+
* status only, never the response body. The caller still gets the original error rethrown.
|
|
15
|
+
* Intentional copy of core's `spanException` (ADR 0015 §5 no-cycle rule: do not deduplicate).
|
|
16
|
+
*/
|
|
17
|
+
function recordedError(error) {
|
|
18
|
+
if (!(error instanceof Error)) return redactSecrets(String(error));
|
|
19
|
+
const message = error instanceof HttpError ? `HTTP ${String(error.status)}` : redactSecrets(error.message);
|
|
20
|
+
const copy = new Error(message);
|
|
21
|
+
copy.name = error.name;
|
|
22
|
+
const header = `${error.name}: ${error.message}`;
|
|
23
|
+
const frames = error.stack?.startsWith(header) ? error.stack.slice(header.length) : "";
|
|
24
|
+
copy.stack = `${error.name}: ${message}${redactSecrets(frames)}`;
|
|
25
|
+
return copy;
|
|
26
|
+
}
|
|
11
27
|
/** Instrumentation scope for observability-package spans. */
|
|
12
28
|
const SCOPE = "storyshelf.observability";
|
|
13
29
|
/**
|
|
@@ -30,7 +46,7 @@ async function withSpan(name, fn, attributes, parent) {
|
|
|
30
46
|
try {
|
|
31
47
|
return await fn(span);
|
|
32
48
|
} catch (error) {
|
|
33
|
-
span.recordException(
|
|
49
|
+
span.recordException(recordedError(error));
|
|
34
50
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
35
51
|
throw error;
|
|
36
52
|
} finally {
|
|
@@ -38,7 +54,6 @@ async function withSpan(name, fn, attributes, parent) {
|
|
|
38
54
|
}
|
|
39
55
|
});
|
|
40
56
|
}
|
|
41
|
-
//#endregion
|
|
42
57
|
export { withSpan };
|
|
43
58
|
|
|
44
59
|
//# sourceMappingURL=tracing.mjs.map
|
package/dist/tracing.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.mjs","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["/**\n * Span helpers over `@opentelemetry/api` (runtime-agnostic
|
|
1
|
+
{"version":3,"file":"tracing.mjs","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["/**\n * Span helpers over `@opentelemetry/api` (runtime-agnostic). Without a configured provider these are cheap\n * noops, so call sites need no enabled-checks.\n */\nimport {\n SpanKind,\n SpanStatusCode,\n context,\n trace,\n type Attributes,\n type Context,\n type Span,\n} from \"@opentelemetry/api\";\nimport { HttpError, redactSecrets } from \"@storyshelf/core/utils\";\n\n/**\n * What a span records of a caught error: a copy with the same `name` and a `redactSecrets` message\n * (connection strings, tokens and keys stay out of exported spans), and for an `HttpError` its\n * status only, never the response body. The caller still gets the original error rethrown.\n * Intentional copy of core's `spanException` (ADR 0015 §5 no-cycle rule: do not deduplicate).\n */\nfunction recordedError(error: unknown): Error | string {\n if (!(error instanceof Error)) {\n return redactSecrets(String(error));\n }\n const message =\n error instanceof HttpError ? `HTTP ${String(error.status)}` : redactSecrets(error.message);\n const copy = new Error(message);\n copy.name = error.name;\n const header = `${error.name}: ${error.message}`;\n const frames = error.stack?.startsWith(header) ? error.stack.slice(header.length) : \"\";\n copy.stack = `${error.name}: ${message}${redactSecrets(frames)}`;\n return copy;\n}\n\n/** Instrumentation scope for observability-package spans. */\nconst SCOPE = \"storyshelf.observability\";\n\n/**\n * Run `fn` inside an internal span.\n *\n * On throw the span records the exception, is marked ERROR, and the error\n * is rethrown — mirroring the pino `{ err }` convention.\n *\n * @param name - Span name (`domain.operation`, never a high-cardinality value).\n * @param fn - Work to run with the span active.\n * @param attributes - Fixed span attributes (no high-cardinality values in names).\n * @param parent - Parent context (defaults to the active context).\n * @returns Whatever `fn` returns.\n */\nexport async function withSpan<T>(\n name: string,\n fn: (span: Span) => Promise<T>,\n attributes?: Attributes,\n parent?: Context,\n): Promise<T> {\n const tracer = trace.getTracer(SCOPE);\n return await tracer.startActiveSpan(\n name,\n { kind: SpanKind.INTERNAL, attributes },\n parent ?? context.active(),\n async (span) => {\n try {\n return await fn(span);\n } catch (error) {\n span.recordException(recordedError(error));\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,SAAS,cAAc,OAAgC;CACrD,IAAI,EAAE,iBAAiB,QACrB,OAAO,cAAc,OAAO,KAAK,CAAC;CAEpC,MAAM,UACJ,iBAAiB,YAAY,QAAQ,OAAO,MAAM,MAAM,MAAM,cAAc,MAAM,OAAO;CAC3F,MAAM,OAAO,IAAI,MAAM,OAAO;CAC9B,KAAK,OAAO,MAAM;CAClB,MAAM,SAAS,GAAG,MAAM,KAAK,IAAI,MAAM;CACvC,MAAM,SAAS,MAAM,OAAO,WAAW,MAAM,IAAI,MAAM,MAAM,MAAM,OAAO,MAAM,IAAI;CACpF,KAAK,QAAQ,GAAG,MAAM,KAAK,IAAI,UAAU,cAAc,MAAM;CAC7D,OAAO;AACT;;AAGA,MAAM,QAAQ;;;;;;;;;;;;;AAcd,eAAsB,SACpB,MACA,IACA,YACA,QACY;CAEZ,OAAO,MADQ,MAAM,UAAU,KACb,CAAC,CAAC,gBAClB,MACA;EAAE,MAAM,SAAS;EAAU;CAAW,GACtC,UAAU,QAAQ,OAAO,GACzB,OAAO,SAAS;EACd,IAAI;GACF,OAAO,MAAM,GAAG,IAAI;EACtB,SAAS,OAAO;GACd,KAAK,gBAAgB,cAAc,KAAK,CAAC;GACzC,KAAK,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;GAC7C,MAAM;EACR,UAAU;GACR,KAAK,IAAI;EACX;CACF,CACF;AACF"}
|
package/dist/types.d.mts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { Attributes } from "@opentelemetry/api";
|
|
4
|
-
//#region src/types.d.ts
|
|
5
4
|
/** Attributes attached to a span or metric measurement. */
|
|
6
5
|
type SpanAttributes = Attributes;
|
|
7
6
|
/** Shutdown handle for the observability SDK (see `node.ts`). */
|
|
8
7
|
interface ObservabilityHandle {
|
|
9
|
-
/** Whether the SDK is active (false = noop: no endpoint configured
|
|
8
|
+
/** Whether the SDK is active (false = noop: no endpoint configured). */
|
|
10
9
|
readonly enabled: boolean;
|
|
11
10
|
/** Flush exporters and release SDK resources. Idempotent. */
|
|
12
11
|
shutdown(): Promise<void>;
|
|
@@ -18,6 +17,5 @@ interface HttpMiddlewareOptions {
|
|
|
18
17
|
/** Service version in spans/metrics. Defaults to the package version. */
|
|
19
18
|
serviceVersion?: string;
|
|
20
19
|
}
|
|
21
|
-
//#endregion
|
|
22
20
|
export { HttpMiddlewareOptions, ObservabilityHandle, SpanAttributes };
|
|
23
21
|
//# sourceMappingURL=types.d.mts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyshelf/observability",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-next-d2cc082",
|
|
4
4
|
"description": "StoryShelf observability: OpenTelemetry tracing, metrics, and log correlation over @storyshelf/core adapters.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"metrics",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"@opentelemetry/sdk-metrics": "^2.11.0",
|
|
66
66
|
"@opentelemetry/sdk-node": "^0.222.0",
|
|
67
67
|
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
68
|
-
"@storyshelf/core": "0.
|
|
69
|
-
"drizzle-orm": "^0.
|
|
68
|
+
"@storyshelf/core": "0.9.0-next-d2cc082",
|
|
69
|
+
"drizzle-orm": "^0.45.4",
|
|
70
70
|
"hono": "^4.11.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
@@ -82,18 +82,9 @@
|
|
|
82
82
|
"oxlint-tsgolint": "^7.0.2001",
|
|
83
83
|
"tsdown": "^0.22.14",
|
|
84
84
|
"typescript": "^7.0.2",
|
|
85
|
-
"vitest": "^5.0.
|
|
85
|
+
"vitest": "^5.0.5"
|
|
86
86
|
},
|
|
87
87
|
"engines": {
|
|
88
88
|
"node": ">=24"
|
|
89
|
-
},
|
|
90
|
-
"jsr": {
|
|
91
|
-
"runtimeCompat": {
|
|
92
|
-
"node": true,
|
|
93
|
-
"deno": true,
|
|
94
|
-
"bun": true,
|
|
95
|
-
"workerd": false,
|
|
96
|
-
"browser": false
|
|
97
|
-
}
|
|
98
89
|
}
|
|
99
90
|
}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * StoryShelf observability: runtime-agnostic OTEL instrumentation.\n *\n * This entrypoint is cross-runtime (`@opentelemetry/api` only — Node SDK or\n * Deno's native bridge supplies the provider). The Node SDK lifecycle\n * (`initObservabilityFromEnv`) lives under `@storyshelf/observability/node`\n * and must never be imported here.\n */\nimport { createInstrumentedDatabase } from \"./instrument-db.ts\";\nimport { createInstrumentedStorage } from \"./instrument-storage.ts\";\nimport { createHttpMiddleware } from \"./middleware.ts\";\n\n/** Wrap a database adapter with `db.*` spans and metrics. */\nexport { createInstrumentedDatabase };\n/** Wrap a storage adapter with `storage.*` spans and metrics. */\nexport { createInstrumentedStorage };\n/** Hono middleware emitting one `http.server` span per request. */\nexport { createHttpMiddleware };\nexport { captureMetrics, dbMetrics, storageMetrics } from \"./metrics.ts\";\nexport type { CaptureInstruments, DbInstruments, StorageInstruments } from \"./metrics.ts\";\nexport { otelLogMixin } from \"./logs.ts\";\nexport {\n TRACEPARENT_HEADER,\n currentTraceparent,\n injectTraceContext,\n parentContext,\n} from \"./propagate.ts\";\nexport { resolveObservabilityConfig } from \"./config.ts\";\nexport type { ObservabilityConfig } from \"./config.ts\";\nexport { withSpan } from \"./tracing.ts\";\nexport type { HttpMiddlewareOptions, ObservabilityHandle, SpanAttributes } from \"./types.ts\";\n"],"mappings":""}
|