@effect-app/infra 4.0.0-beta.301 → 4.0.0-beta.303
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/CHANGELOG.md +15 -0
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +21 -2
- package/package.json +2 -2
- package/src/otel.ts +26 -1
- package/test/repository-ext.test.ts +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @effect-app/infra
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.303
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 33b0544: Replace repository codec child spans with parent-span timing attributes and bounded metrics, and record database operation timing for instrumented stores.
|
|
8
|
+
- Updated dependencies [33b0544]
|
|
9
|
+
- effect-app@4.0.0-beta.303
|
|
10
|
+
|
|
11
|
+
## 4.0.0-beta.302
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [c0e5a1b]
|
|
16
|
+
- effect-app@4.0.0-beta.302
|
|
17
|
+
|
|
3
18
|
## 4.0.0-beta.301
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/otel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAG3C,MAAM,MAAM,QAAQ,GAChB,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,SAAS,GACT,OAAO,GACP,WAAW,GACX,QAAQ,GACR,MAAM,CAAA;AAEV,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAA;IACzB,mDAAmD;IACnD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;CACrD;AAoCD;;;;GAIG;AACH,eAAO,MAAM,UAAU,MAAO,aAAa,4IAKxC,CAAA;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,MAAO,aAAa,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CACZ,CAAA;AAElG,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,MAAO;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5C,sCAIG,CAAA;AAEJ,4CAA4C;AAC5C,eAAO,MAAM,sBAAsB,MAAO;IACxC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACzC,sCAMG,CAAA;AAEJ,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,UAAU,GACV,OAAO,GACP,QAAQ,GACR,KAAK,CAAA;AAET,MAAM,MAAM,kBAAkB,GAC1B,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,QAAQ,CAAA;AAEZ,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;IAChC,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;CACrD;AAYD,0DAA0D;AAC1D,eAAO,MAAM,iBAAiB,MACzB,oBAAoB,QACjB,UAAU,GAAG,UAAU,4IAM5B,CAAA;AAEH,yGAAyG;AACzG,eAAO,MAAM,iBAAiB,MACzB,oBAAoB,QACjB,UAAU,GAAG,UAAU;;;;CAMlB,CAAA"}
|
package/dist/otel.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - Cosmos DB: https://opentelemetry.io/docs/specs/semconv/database/cosmosdb/
|
|
10
10
|
*/
|
|
11
11
|
import * as Effect from "effect-app/Effect";
|
|
12
|
+
import * as Metric from "effect/Metric";
|
|
12
13
|
const dbAttributes = (a) => ({
|
|
13
14
|
"db.system.name": a.system,
|
|
14
15
|
"db.operation.name": a.operation,
|
|
@@ -18,6 +19,24 @@ const dbAttributes = (a) => ({
|
|
|
18
19
|
...(a.entity !== undefined && { "app.entity": a.entity }),
|
|
19
20
|
...a.extra
|
|
20
21
|
});
|
|
22
|
+
const dbOperationDuration = Metric.histogram("db.client.operation.duration", {
|
|
23
|
+
boundaries: [0.1, 0.5, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000],
|
|
24
|
+
description: "Caller-observed database operation duration in milliseconds"
|
|
25
|
+
});
|
|
26
|
+
const timeDbOperation = (a) => (self) => Effect.clockWith((clock) => {
|
|
27
|
+
const startedAt = clock.currentTimeNanosUnsafe();
|
|
28
|
+
return Effect.onExit(self, () => {
|
|
29
|
+
const durationMs = Number(clock.currentTimeNanosUnsafe() - startedAt) / 1_000_000;
|
|
30
|
+
return Effect.all([
|
|
31
|
+
Effect.annotateCurrentSpan({ "db.operation.duration_ms": durationMs }),
|
|
32
|
+
Metric.update(Metric.withAttributes(dbOperationDuration, {
|
|
33
|
+
"db.system.name": a.system,
|
|
34
|
+
"db.operation.name": a.operation,
|
|
35
|
+
...(a.entity !== undefined && { "app.entity": a.entity })
|
|
36
|
+
}), durationMs)
|
|
37
|
+
], { discard: true });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
21
40
|
/**
|
|
22
41
|
* Wrap an effect with an OTel-semconv database span.
|
|
23
42
|
*
|
|
@@ -32,7 +51,7 @@ export const withDbSpan = (a) => Effect.withSpan(`${a.operation} ${a.collection}
|
|
|
32
51
|
* Annotates before running so attrs persist even on failure.
|
|
33
52
|
* No-op if there is no current span.
|
|
34
53
|
*/
|
|
35
|
-
export const annotateDb = (a) => (self) => Effect.flatMap(Effect.annotateCurrentSpan(dbAttributes(a)), () => self);
|
|
54
|
+
export const annotateDb = (a) => (self) => Effect.flatMap(Effect.annotateCurrentSpan(dbAttributes(a)), () => self.pipe(timeDbOperation(a)));
|
|
36
55
|
/** Annotate the current span with response metrics from a DB call. */
|
|
37
56
|
export const annotateDbResponse = (m) => Effect.annotateCurrentSpan({
|
|
38
57
|
...(m.returnedRows !== undefined && { "db.response.returned_rows": m.returnedRows }),
|
|
@@ -62,4 +81,4 @@ export const messagingSpanArgs = (a, kind) => ({
|
|
|
62
81
|
kind,
|
|
63
82
|
attributes: messagingAttributes(a)
|
|
64
83
|
});
|
|
65
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
84
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3RlbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9vdGVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7R0FTRztBQUVILE9BQU8sS0FBSyxNQUFNLE1BQU0sbUJBQW1CLENBQUE7QUFDM0MsT0FBTyxLQUFLLE1BQU0sTUFBTSxlQUFlLENBQUE7QUE0QnZDLE1BQU0sWUFBWSxHQUFHLENBQUMsQ0FBZ0IsRUFBMkIsRUFBRSxDQUFDLENBQUM7SUFDbkUsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDLE1BQU07SUFDMUIsbUJBQW1CLEVBQUUsQ0FBQyxDQUFDLFNBQVM7SUFDaEMsb0JBQW9CLEVBQUUsQ0FBQyxDQUFDLFVBQVU7SUFDbEMsR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLEtBQUssU0FBUyxJQUFJLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztJQUNqRSxHQUFHLENBQUMsQ0FBQyxDQUFDLEtBQUssS0FBSyxTQUFTLElBQUksRUFBRSxlQUFlLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzFELEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxLQUFLLFNBQVMsSUFBSSxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDekQsR0FBRyxDQUFDLENBQUMsS0FBSztDQUNYLENBQUMsQ0FBQTtBQUVGLE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyw4QkFBOEIsRUFBRTtJQUMzRSxVQUFVLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQztJQUN2RixXQUFXLEVBQUUsNkRBQTZEO0NBQzNFLENBQUMs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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/infra",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.303",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"proper-lockfile": "^4.1.2",
|
|
16
16
|
"pure-rand": "8.4.0",
|
|
17
17
|
"query-string": "^9.4.0",
|
|
18
|
-
"effect-app": "4.0.0-beta.
|
|
18
|
+
"effect-app": "4.0.0-beta.303"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@azure/cosmos": "^4.9.3",
|
package/src/otel.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import * as Effect from "effect-app/Effect"
|
|
13
|
+
import * as Metric from "effect/Metric"
|
|
13
14
|
|
|
14
15
|
export type DbSystem =
|
|
15
16
|
| "postgresql"
|
|
@@ -47,6 +48,30 @@ const dbAttributes = (a: DbSpanOptions): Record<string, unknown> => ({
|
|
|
47
48
|
...a.extra
|
|
48
49
|
})
|
|
49
50
|
|
|
51
|
+
const dbOperationDuration = Metric.histogram("db.client.operation.duration", {
|
|
52
|
+
boundaries: [0.1, 0.5, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000],
|
|
53
|
+
description: "Caller-observed database operation duration in milliseconds"
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const timeDbOperation = (a: DbSpanOptions) => <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
57
|
+
Effect.clockWith((clock) => {
|
|
58
|
+
const startedAt = clock.currentTimeNanosUnsafe()
|
|
59
|
+
return Effect.onExit(self, () => {
|
|
60
|
+
const durationMs = Number(clock.currentTimeNanosUnsafe() - startedAt) / 1_000_000
|
|
61
|
+
return Effect.all([
|
|
62
|
+
Effect.annotateCurrentSpan({ "db.operation.duration_ms": durationMs }),
|
|
63
|
+
Metric.update(
|
|
64
|
+
Metric.withAttributes(dbOperationDuration, {
|
|
65
|
+
"db.system.name": a.system,
|
|
66
|
+
"db.operation.name": a.operation,
|
|
67
|
+
...(a.entity !== undefined && { "app.entity": a.entity })
|
|
68
|
+
}),
|
|
69
|
+
durationMs
|
|
70
|
+
)
|
|
71
|
+
], { discard: true })
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
50
75
|
/**
|
|
51
76
|
* Wrap an effect with an OTel-semconv database span.
|
|
52
77
|
*
|
|
@@ -68,7 +93,7 @@ export const withDbSpan = (a: DbSpanOptions) =>
|
|
|
68
93
|
* No-op if there is no current span.
|
|
69
94
|
*/
|
|
70
95
|
export const annotateDb = (a: DbSpanOptions) => <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
71
|
-
Effect.flatMap(Effect.annotateCurrentSpan(dbAttributes(a)), () => self)
|
|
96
|
+
Effect.flatMap(Effect.annotateCurrentSpan(dbAttributes(a)), () => self.pipe(timeDbOperation(a)))
|
|
72
97
|
|
|
73
98
|
/** Annotate the current span with response metrics from a DB call. */
|
|
74
99
|
export const annotateDbResponse = (m: {
|
|
@@ -8,6 +8,7 @@ import { RepositoryRegistryLive } from "effect-app/Model/Repository/Registry"
|
|
|
8
8
|
import * as S from "effect-app/Schema"
|
|
9
9
|
import { setupRequestContextFromCurrent } from "effect-app/setupRequest"
|
|
10
10
|
import * as Ref from "effect/Ref"
|
|
11
|
+
import * as Tracer from "effect/Tracer"
|
|
11
12
|
import { MemoryStoreLive } from "../src/Store/Memory.js"
|
|
12
13
|
|
|
13
14
|
class BatchItem extends S.Class<BatchItem>("BatchItem")({
|
|
@@ -175,4 +176,41 @@ describe("repository ext save/remove batching", () => {
|
|
|
175
176
|
setupRequestContextFromCurrent(),
|
|
176
177
|
Effect.provide(TestStoreLive)
|
|
177
178
|
))
|
|
179
|
+
|
|
180
|
+
it.effect("records schema timing on repository spans without codec child spans", () =>
|
|
181
|
+
Effect
|
|
182
|
+
.gen(function*() {
|
|
183
|
+
const spans: Tracer.NativeSpan[] = []
|
|
184
|
+
const tracer = Tracer.make({
|
|
185
|
+
span(options) {
|
|
186
|
+
const span = new Tracer.NativeSpan(options)
|
|
187
|
+
spans.push(span)
|
|
188
|
+
return span
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
yield* Effect
|
|
193
|
+
.gen(function*() {
|
|
194
|
+
const repo = yield* makeRepo("TelemetryItem", BatchItem, {})
|
|
195
|
+
yield* repo.save(new BatchItem({ id: "1", label: "one" }))
|
|
196
|
+
yield* repo.all
|
|
197
|
+
})
|
|
198
|
+
.pipe(Effect.provideService(Tracer.Tracer, tracer))
|
|
199
|
+
|
|
200
|
+
expect(spans.map((_) => _.name)).not.toContain("parseMany")
|
|
201
|
+
expect(spans.map((_) => _.name)).not.toContain("encodeMany")
|
|
202
|
+
|
|
203
|
+
const saveSpan = spans.find((_) => _.name === "Repository.saveAndPublish")
|
|
204
|
+
const allSpan = spans.find((_) => _.name === "Repository.all")
|
|
205
|
+
expect(saveSpan?.attributes.get("app.schema.encode.duration_ms")).toEqual(expect.any(Number))
|
|
206
|
+
expect(saveSpan?.attributes.get("app.schema.item_count")).toBe(1)
|
|
207
|
+
expect(saveSpan?.attributes.get("db.operation.duration_ms")).toEqual(expect.any(Number))
|
|
208
|
+
expect(allSpan?.attributes.get("app.schema.decode.duration_ms")).toEqual(expect.any(Number))
|
|
209
|
+
expect(allSpan?.attributes.get("app.schema.item_count")).toBe(1)
|
|
210
|
+
expect(allSpan?.attributes.get("db.operation.duration_ms")).toEqual(expect.any(Number))
|
|
211
|
+
})
|
|
212
|
+
.pipe(
|
|
213
|
+
setupRequestContextFromCurrent(),
|
|
214
|
+
Effect.provide(TestStoreLive)
|
|
215
|
+
))
|
|
178
216
|
})
|