@effect-app/infra 4.0.0-beta.209 → 4.0.0-beta.210

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 (47) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  3. package/dist/Model/Repository/internal/internal.js +28 -21
  4. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  5. package/dist/QueueMaker/SQLQueue.js +37 -14
  6. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  7. package/dist/QueueMaker/memQueue.js +37 -14
  8. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  9. package/dist/QueueMaker/sbqueue.js +28 -13
  10. package/dist/RequestContext.d.ts +6 -6
  11. package/dist/RequestContext.js +7 -7
  12. package/dist/Store/Cosmos.d.ts.map +1 -1
  13. package/dist/Store/Cosmos.js +80 -66
  14. package/dist/Store/Disk.d.ts.map +1 -1
  15. package/dist/Store/Disk.js +48 -14
  16. package/dist/Store/Memory.d.ts.map +1 -1
  17. package/dist/Store/Memory.js +60 -35
  18. package/dist/Store/SQL/Pg.d.ts.map +1 -1
  19. package/dist/Store/SQL/Pg.js +80 -38
  20. package/dist/Store/SQL.d.ts.map +1 -1
  21. package/dist/Store/SQL.js +160 -77
  22. package/dist/adapters/SQL/Model.d.ts +4 -1
  23. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  24. package/dist/adapters/SQL/Model.js +28 -37
  25. package/dist/api/internal/events.js +2 -2
  26. package/dist/api/routing/middleware/middleware.js +3 -3
  27. package/dist/api/routing.d.ts.map +1 -1
  28. package/dist/api/routing.js +12 -2
  29. package/dist/otel.d.ts +66 -0
  30. package/dist/otel.d.ts.map +1 -0
  31. package/dist/otel.js +56 -0
  32. package/package.json +6 -2
  33. package/src/Model/Repository/internal/internal.ts +82 -71
  34. package/src/QueueMaker/SQLQueue.ts +35 -13
  35. package/src/QueueMaker/memQueue.ts +35 -13
  36. package/src/QueueMaker/sbqueue.ts +26 -12
  37. package/src/RequestContext.ts +6 -6
  38. package/src/Store/Cosmos.ts +81 -66
  39. package/src/Store/Disk.ts +50 -16
  40. package/src/Store/Memory.ts +59 -34
  41. package/src/Store/SQL/Pg.ts +74 -40
  42. package/src/Store/SQL.ts +149 -83
  43. package/src/adapters/SQL/Model.ts +31 -36
  44. package/src/api/internal/events.ts +1 -1
  45. package/src/api/routing/middleware/middleware.ts +2 -2
  46. package/src/api/routing.ts +11 -1
  47. package/src/otel.ts +141 -0
@@ -8,6 +8,7 @@ import { OptimisticConcurrencyException } from "../errors.js"
8
8
  import { InfraLogger } from "../logger.js"
9
9
  import type { FieldValues } from "../Model/filter/types.js"
10
10
  import { type RawQuery } from "../Model/query.js"
11
+ import { annotateCosmosResponse, withDbSpan } from "../otel.js"
11
12
  import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
12
13
  import { storeId } from "./Memory.js"
13
14
  import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
@@ -35,19 +36,21 @@ const annotateFeed = (resp: {
35
36
  requestCharge?: number
36
37
  diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } }
37
38
  }) =>
38
- Effect.annotateCurrentSpan({
39
- "db.cosmos.request_charge": resp.requestCharge ?? 0,
40
- "db.cosmos.resource_count": resp.resources.length,
41
- "db.cosmos.response_bytes": respBytes(resp)
39
+ annotateCosmosResponse({
40
+ requestCharge: resp.requestCharge,
41
+ returnedRows: resp.resources.length,
42
+ responseBytes: respBytes(resp)
42
43
  })
43
44
 
44
45
  const annotateItem = (resp: {
45
46
  requestCharge?: number
47
+ statusCode?: number
46
48
  diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } }
47
49
  }) =>
48
- Effect.annotateCurrentSpan({
49
- "db.cosmos.request_charge": resp.requestCharge ?? 0,
50
- "db.cosmos.response_bytes": respBytes(resp)
50
+ annotateCosmosResponse({
51
+ requestCharge: resp.requestCharge,
52
+ statusCode: resp.statusCode,
53
+ responseBytes: respBytes(resp)
51
54
  })
52
55
 
53
56
  const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
@@ -137,7 +140,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
137
140
  )
138
141
  }),
139
142
  Effect.withLogSpan(`Cosmos.seedCheck ${name} in ${ns} [effect-app/infra/Store]`),
140
- Effect.withSpan("Cosmos.seed [effect-app/infra/Store]", { attributes: { name, namespace: ns } })
143
+ withDbSpan({
144
+ operation: "seed",
145
+ system: "cosmosdb",
146
+ collection: containerId,
147
+ namespace: ns,
148
+ entity: name
149
+ })
141
150
  )
142
151
  }
143
152
  const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
@@ -251,9 +260,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
251
260
  return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
252
261
  })
253
262
  .pipe(
254
- Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
255
- attributes: { "repository.container_id": containerId, "repository.model_name": name, namespace: ns }
256
- }, { captureStackTrace: false })
263
+ withDbSpan({
264
+ operation: "bulkSet",
265
+ system: "cosmosdb",
266
+ collection: containerId,
267
+ namespace: ns,
268
+ entity: name
269
+ })
257
270
  )
258
271
 
259
272
  const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
@@ -321,14 +334,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
321
334
  })) as unknown as NonEmptyReadonlyArray<Encoded>
322
335
  })))
323
336
  })
324
- .pipe(Effect
325
- .withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
326
- attributes: {
327
- "repository.container_id": containerId,
328
- "repository.model_name": name,
329
- namespace: ns
330
- }
331
- }, { captureStackTrace: false }))
337
+ .pipe(withDbSpan({
338
+ operation: "batchSet",
339
+ system: "cosmosdb",
340
+ collection: containerId,
341
+ namespace: ns,
342
+ entity: name
343
+ }))
332
344
  ))
333
345
  }
334
346
 
@@ -352,13 +364,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
352
364
  )
353
365
  })
354
366
  .pipe(
355
- Effect.withSpan("Cosmos.queryRaw [effect-app/infra/Store]", {
356
- attributes: {
357
- "repository.container_id": containerId,
358
- "repository.model_name": name,
359
- namespace: ns
360
- }
361
- }, { captureStackTrace: false })
367
+ withDbSpan({
368
+ operation: "queryRaw",
369
+ system: "cosmosdb",
370
+ collection: containerId,
371
+ namespace: ns,
372
+ entity: name,
373
+ query: q.query
374
+ })
362
375
  )
363
376
  )
364
377
  ),
@@ -379,13 +392,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
379
392
  )
380
393
  )
381
394
  .pipe(
382
- Effect.withSpan("Cosmos.batchRemove [effect-app/infra/Store]", {
383
- attributes: {
384
- "repository.container_id": containerId,
385
- "repository.model_name": name,
386
- namespace: ns
387
- }
388
- }, { captureStackTrace: false })
395
+ withDbSpan({
396
+ operation: "batchRemove",
397
+ system: "cosmosdb",
398
+ collection: containerId,
399
+ namespace: ns,
400
+ entity: name
401
+ })
389
402
  )
390
403
  )),
391
404
  all: Effect
@@ -408,13 +421,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
408
421
  return response.resources.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
409
422
  })
410
423
  .pipe(
411
- Effect.withSpan("Cosmos.all [effect-app/infra/Store]", {
412
- attributes: {
413
- "repository.container_id": containerId,
414
- "repository.model_name": name,
415
- namespace: ns
416
- }
417
- }, { captureStackTrace: false })
424
+ withDbSpan({
425
+ operation: "all",
426
+ system: "cosmosdb",
427
+ collection: containerId,
428
+ namespace: ns,
429
+ entity: name,
430
+ query: q.query
431
+ })
418
432
  )
419
433
  )
420
434
  ),
@@ -472,13 +486,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
472
486
  return response.resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
473
487
  })
474
488
  .pipe(
475
- Effect.withSpan("Cosmos.filter [effect-app/infra/Store]", {
476
- attributes: {
477
- "repository.container_id": containerId,
478
- "repository.model_name": name,
479
- namespace: ns
480
- }
481
- }, { captureStackTrace: false })
489
+ withDbSpan({
490
+ operation: "filter",
491
+ system: "cosmosdb",
492
+ collection: containerId,
493
+ namespace: ns,
494
+ entity: name,
495
+ query: q.query
496
+ })
482
497
  )
483
498
  )
484
499
  )
@@ -497,16 +512,17 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
497
512
  Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
498
513
  )
499
514
  })
500
- .pipe(Effect
501
- .withSpan("Cosmos.find [effect-app/infra/Store]", {
502
- attributes: {
503
- "repository.container_id": containerId,
504
- "repository.model_name": name,
505
- partitionValue: nsPartitionValue(ns, { [idKey]: id } as Encoded),
506
- namespace: ns,
507
- id
508
- }
509
- }, { captureStackTrace: false }))
515
+ .pipe(withDbSpan({
516
+ operation: "find",
517
+ system: "cosmosdb",
518
+ collection: containerId,
519
+ namespace: ns,
520
+ entity: name,
521
+ extra: {
522
+ "azure.cosmosdb.operation.partition_key": nsPartitionValue(ns, { [idKey]: id } as Encoded),
523
+ "app.entity.id": id
524
+ }
525
+ }))
510
526
  )),
511
527
  set: (e) =>
512
528
  resolveNamespace.pipe(Effect.flatMap((ns) =>
@@ -556,15 +572,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
556
572
  _etag: x.etag
557
573
  }))
558
574
  }),
559
- Effect
560
- .withSpan("Cosmos.set [effect-app/infra/Store]", {
561
- attributes: {
562
- "repository.container_id": containerId,
563
- "repository.model_name": name,
564
- namespace: ns,
565
- id: e[idKey]
566
- }
567
- }, { captureStackTrace: false })
575
+ withDbSpan({
576
+ operation: "set",
577
+ system: "cosmosdb",
578
+ collection: containerId,
579
+ namespace: ns,
580
+ entity: name,
581
+ extra: { "app.entity.id": e[idKey] }
582
+ })
568
583
  )
569
584
  )),
570
585
  batchSet,
package/src/Store/Disk.ts CHANGED
@@ -5,6 +5,7 @@ import fs from "fs"
5
5
 
6
6
  import { Console, Effect, flow, Semaphore } from "effect-app"
7
7
  import type { FieldValues } from "../Model/filter/types.js"
8
+ import { withDbSpan } from "../otel.js"
8
9
  import { makeMemoryStoreInt, storeId } from "./Memory.js"
9
10
  import { type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
10
11
 
@@ -26,42 +27,75 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
26
27
  }
27
28
  }
28
29
  const file = dir + "/" + prefix + name + ".json"
30
+ const fileExtra = { "disk.file.path": file }
29
31
  const fsStore = {
30
32
  get: fu
31
33
  .readTextFile(file)
32
34
  .pipe(
33
- Effect.withSpan("Disk.read.readFile [effect-app/infra/Store]", {}, { captureStackTrace: false }),
35
+ withDbSpan({
36
+ operation: "read.readFile",
37
+ system: "disk",
38
+ collection: name,
39
+ namespace,
40
+ entity: name,
41
+ extra: fileExtra
42
+ }),
34
43
  Effect.flatMap((x) =>
35
44
  Effect.sync(() => JSON.parse(x) as PM[]).pipe(
36
- Effect.withSpan("Disk.read.parse [effect-app/infra/Store]", {}, { captureStackTrace: false })
45
+ withDbSpan({
46
+ operation: "read.parse",
47
+ system: "disk",
48
+ collection: name,
49
+ namespace,
50
+ entity: name,
51
+ extra: fileExtra
52
+ })
37
53
  )
38
54
  ),
39
55
  Effect.orDie,
40
- Effect.withSpan("Disk.read [effect-app/infra/Store]", {
41
- attributes: { "disk.file": file }
42
- }, { captureStackTrace: false })
56
+ withDbSpan({
57
+ operation: "read",
58
+ system: "disk",
59
+ collection: name,
60
+ namespace,
61
+ entity: name,
62
+ extra: fileExtra
63
+ })
43
64
  ),
44
65
  setRaw: (v: Iterable<PM>) =>
45
66
  Effect
46
67
  .sync(() => JSON.stringify([...v], undefined, 2))
47
68
  .pipe(
48
- Effect.withSpan("Disk.stringify [effect-app/infra/Store]", {
49
- attributes: { "disk.file": file }
50
- }, { captureStackTrace: false }),
69
+ withDbSpan({
70
+ operation: "stringify",
71
+ system: "disk",
72
+ collection: name,
73
+ namespace,
74
+ entity: name,
75
+ extra: fileExtra
76
+ }),
51
77
  Effect
52
78
  .flatMap(
53
79
  (json) =>
54
80
  fu
55
81
  .writeTextFile(file, json)
56
- .pipe(Effect
57
- .withSpan("Disk.write.writeFile [effect-app/infra/Store]", {
58
- attributes: { "disk.file_size": json.length }
59
- }, { captureStackTrace: false }))
82
+ .pipe(withDbSpan({
83
+ operation: "write.writeFile",
84
+ system: "disk",
85
+ collection: name,
86
+ namespace,
87
+ entity: name,
88
+ extra: { ...fileExtra, "disk.file.size": json.length }
89
+ }))
60
90
  ),
61
- Effect
62
- .withSpan("Disk.write [effect-app/infra/Store]", {
63
- attributes: { "disk.file": file }
64
- }, { captureStackTrace: false })
91
+ withDbSpan({
92
+ operation: "write",
93
+ system: "disk",
94
+ collection: name,
95
+ namespace,
96
+ entity: name,
97
+ extra: fileExtra
98
+ })
65
99
  )
66
100
  }
67
101
 
@@ -4,6 +4,7 @@ import { Array, Context, Effect, flow, type NonEmptyReadonlyArray, Option, Order
4
4
  import { NonEmptyString255 } from "effect-app/Schema"
5
5
  import { InfraLogger } from "../logger.js"
6
6
  import type { FieldValues } from "../Model/filter/types.js"
7
+ import { withDbSpan } from "../otel.js"
7
8
  import { codeFilter, codeFilter3_ } from "./codeFilter.js"
8
9
  import { type FilterArgs, type PersistenceModelType, type Store, type StoreConfig, StoreMaker } from "./service.js"
9
10
  import { makeUpdateETag } from "./utils.js"
@@ -162,38 +163,48 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
162
163
  .pipe(
163
164
  // Effect.tap(() => logQuery(query, defaultValues)),
164
165
  Effect.map(query.memory),
165
- Effect.withSpan("Memory.queryRaw [effect-app/infra/Store]", {
166
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
167
- }, { captureStackTrace: false })
166
+ withDbSpan({
167
+ operation: "queryRaw",
168
+ system: "memory",
169
+ collection: modelName,
170
+ namespace,
171
+ entity: modelName
172
+ })
168
173
  ),
169
174
 
170
- all: all.pipe(Effect.withSpan("Memory.all [effect-app/infra/Store]", {
171
- attributes: {
172
- modelName,
173
- namespace
174
- }
175
- }, { captureStackTrace: false })),
175
+ all: all.pipe(withDbSpan({
176
+ operation: "all",
177
+ system: "memory",
178
+ collection: modelName,
179
+ namespace,
180
+ entity: modelName
181
+ })),
176
182
  find: (id) =>
177
183
  Ref
178
184
  .get(store)
179
185
  .pipe(
180
186
  Effect.map((_) => Option.fromNullishOr(_.get(id))),
181
- Effect
182
- .withSpan("Memory.find [effect-app/infra/Store]", {
183
- attributes: {
184
- modelName,
185
- namespace
186
- }
187
- }, { captureStackTrace: false })
187
+ withDbSpan({
188
+ operation: "find",
189
+ system: "memory",
190
+ collection: modelName,
191
+ namespace,
192
+ entity: modelName,
193
+ extra: { "app.entity.id": id as unknown }
194
+ })
188
195
  ),
189
196
  filter: (f) =>
190
197
  all
191
198
  .pipe(
192
199
  Effect.tap(() => logQuery(f, defaultValues)),
193
200
  Effect.map(memFilter(f)),
194
- Effect.withSpan("Memory.filter [effect-app/infra/Store]", {
195
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
196
- }, { captureStackTrace: false })
201
+ withDbSpan({
202
+ operation: "filter",
203
+ system: "memory",
204
+ collection: modelName,
205
+ namespace,
206
+ entity: modelName
207
+ })
197
208
  ),
198
209
  set: (e) =>
199
210
  s
@@ -208,10 +219,14 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
208
219
  )
209
220
  ),
210
221
  withPermit,
211
- Effect
212
- .withSpan("Memory.set [effect-app/infra/Store]", {
213
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
214
- }, { captureStackTrace: false })
222
+ withDbSpan({
223
+ operation: "set",
224
+ system: "memory",
225
+ collection: modelName,
226
+ namespace,
227
+ entity: modelName,
228
+ extra: { "app.entity.id": e[idKey] as unknown }
229
+ })
215
230
  ),
216
231
  batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
217
232
  pipe(
@@ -222,10 +237,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
222
237
  Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
223
238
  Effect.orDie,
224
239
  Effect.andThen(batchRemove),
225
- Effect
226
- .withSpan("Memory.batchRemove [effect-app/infra/Store]", {
227
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
228
- }, { captureStackTrace: false })
240
+ withDbSpan({
241
+ operation: "batchRemove",
242
+ system: "memory",
243
+ collection: modelName,
244
+ namespace,
245
+ entity: modelName
246
+ })
229
247
  )
230
248
  ),
231
249
  batchSet: (items: readonly [PM, ...PM[]]) =>
@@ -237,18 +255,25 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
237
255
  Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
238
256
  Effect.orDie,
239
257
  Effect.andThen(batchSet),
240
- Effect
241
- .withSpan("Memory.batchSet [effect-app/infra/Store]", {
242
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
243
- }, { captureStackTrace: false })
258
+ withDbSpan({
259
+ operation: "batchSet",
260
+ system: "memory",
261
+ collection: modelName,
262
+ namespace,
263
+ entity: modelName
264
+ })
244
265
  )
245
266
  ),
246
267
  bulkSet: flow(
247
268
  batchSet,
248
269
  (_) =>
249
- _.pipe(Effect.withSpan("Memory.bulkSet [effect-app/infra/Store]", {
250
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
251
- }, { captureStackTrace: false }))
270
+ _.pipe(withDbSpan({
271
+ operation: "bulkSet",
272
+ system: "memory",
273
+ collection: modelName,
274
+ namespace,
275
+ entity: modelName
276
+ }))
252
277
  )
253
278
  }
254
279
  return s
@@ -6,6 +6,7 @@ import { SqlClient } from "effect/unstable/sql"
6
6
  import { OptimisticConcurrencyException } from "../../errors.js"
7
7
  import { InfraLogger } from "../../logger.js"
8
8
  import type { FieldValues } from "../../Model/filter/types.js"
9
+ import { withDbSpan } from "../../otel.js"
9
10
  import { storeId } from "../Memory.js"
10
11
  import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "../service.js"
11
12
  import { makeETag } from "../utils.js"
@@ -161,25 +162,28 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
161
162
  seedNamespace: (ns) => seedNamespace(ns),
162
163
 
163
164
  all: resolveNamespace.pipe(
164
- Effect.flatMap((ns) =>
165
- exec(`SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`, [ns])
165
+ Effect.flatMap((ns) => {
166
+ const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`
167
+ return exec(sqlText, [ns])
166
168
  .pipe(
167
169
  Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
168
- Effect.withSpan("PgSQL.all [effect-app/infra/Store]", {
169
- attributes: {
170
- "repository.table_name": tableName,
171
- "repository.model_name": name,
172
- "repository.namespace": ns
173
- }
174
- }, { captureStackTrace: false })
170
+ withDbSpan({
171
+ operation: "all",
172
+ system: "postgresql",
173
+ collection: tableName,
174
+ namespace: ns,
175
+ entity: name,
176
+ query: sqlText
177
+ })
175
178
  )
176
- )
179
+ })
177
180
  ),
178
181
 
179
182
  find: (id) =>
180
183
  resolveNamespace.pipe(Effect
181
- .flatMap((ns) =>
182
- exec(`SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`, [id, ns])
184
+ .flatMap((ns) => {
185
+ const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`
186
+ return exec(sqlText, [id, ns])
183
187
  .pipe(
184
188
  Effect.map((rows) => {
185
189
  const row = (rows as any[])[0]
@@ -187,11 +191,17 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
187
191
  ? Option.some(parseRow<Encoded>(row, idKey, defaultValues))
188
192
  : Option.none()
189
193
  }),
190
- Effect.withSpan("PgSQL.find [effect-app/infra/Store]", {
191
- attributes: { "repository.table_name": tableName, "repository.model_name": name, id }
192
- }, { captureStackTrace: false })
194
+ withDbSpan({
195
+ operation: "find",
196
+ system: "postgresql",
197
+ collection: tableName,
198
+ namespace: ns,
199
+ entity: name,
200
+ query: sqlText,
201
+ extra: { "app.entity.id": id }
202
+ })
193
203
  )
194
- )),
204
+ })),
195
205
 
196
206
  filter: <U extends keyof Encoded = never>(f: FilterArgs<Encoded, U>) => {
197
207
  const filter = f
@@ -225,6 +235,7 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
225
235
  })
226
236
  .pipe(
227
237
  Effect.tap((q) => logQuery(q)),
238
+ Effect.tap((q) => Effect.annotateCurrentSpan({ "db.query.text": q.sql })),
228
239
  Effect.flatMap((q) =>
229
240
  exec(q.sql, q.params).pipe(
230
241
  Effect.map((rows) => {
@@ -244,9 +255,13 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
244
255
  })
245
256
  )
246
257
  ),
247
- Effect.withSpan("PgSQL.filter [effect-app/infra/Store]", {
248
- attributes: { "repository.table_name": tableName, "repository.model_name": name }
249
- }, { captureStackTrace: false })
258
+ withDbSpan({
259
+ operation: "filter",
260
+ system: "postgresql",
261
+ collection: tableName,
262
+ namespace: ns,
263
+ entity: name
264
+ })
250
265
  )
251
266
  ))
252
267
  },
@@ -254,53 +269,72 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
254
269
  set: (e) =>
255
270
  resolveNamespace.pipe(Effect.flatMap((ns) =>
256
271
  setInternal(e, ns).pipe(
257
- Effect.withSpan("PgSQL.set [effect-app/infra/Store]", {
258
- attributes: { "repository.table_name": tableName, "repository.model_name": name, id: e[idKey] }
259
- }, { captureStackTrace: false })
272
+ withDbSpan({
273
+ operation: "set",
274
+ system: "postgresql",
275
+ collection: tableName,
276
+ namespace: ns,
277
+ entity: name,
278
+ extra: { "app.entity.id": e[idKey] }
279
+ })
260
280
  )
261
281
  )),
262
282
 
263
283
  batchSet: (items) =>
264
284
  resolveNamespace.pipe(Effect.flatMap((ns) =>
265
285
  bulkSetInternal(items, ns).pipe(
266
- Effect.withSpan("PgSQL.batchSet [effect-app/infra/Store]", {
267
- attributes: { "repository.table_name": tableName, "repository.model_name": name }
268
- }, { captureStackTrace: false })
286
+ withDbSpan({
287
+ operation: "batchSet",
288
+ system: "postgresql",
289
+ collection: tableName,
290
+ namespace: ns,
291
+ entity: name
292
+ })
269
293
  )
270
294
  )),
271
295
 
272
296
  bulkSet: (items) =>
273
297
  resolveNamespace.pipe(Effect.flatMap((ns) =>
274
298
  bulkSetInternal(items, ns).pipe(
275
- Effect.withSpan("PgSQL.bulkSet [effect-app/infra/Store]", {
276
- attributes: { "repository.table_name": tableName, "repository.model_name": name }
277
- }, { captureStackTrace: false })
299
+ withDbSpan({
300
+ operation: "bulkSet",
301
+ system: "postgresql",
302
+ collection: tableName,
303
+ namespace: ns,
304
+ entity: name
305
+ })
278
306
  )
279
307
  )),
280
308
 
281
309
  batchRemove: (ids) => {
282
310
  const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ")
283
311
  const nsPlaceholder = `$${ids.length + 1}`
284
- return resolveNamespace.pipe(Effect.flatMap((ns) =>
285
- exec(
286
- `DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ${nsPlaceholder}`,
287
- [...ids, ns]
288
- )
312
+ return resolveNamespace.pipe(Effect.flatMap((ns) => {
313
+ const sqlText = `DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ${nsPlaceholder}`
314
+ return exec(sqlText, [...ids, ns])
289
315
  .pipe(
290
316
  Effect.asVoid,
291
- Effect.withSpan("PgSQL.batchRemove [effect-app/infra/Store]", {
292
- attributes: { "repository.table_name": tableName, "repository.model_name": name }
293
- }, { captureStackTrace: false })
317
+ withDbSpan({
318
+ operation: "batchRemove",
319
+ system: "postgresql",
320
+ collection: tableName,
321
+ namespace: ns,
322
+ entity: name,
323
+ query: sqlText
324
+ })
294
325
  )
295
- ))
326
+ }))
296
327
  },
297
328
 
298
329
  queryRaw: (query) =>
299
330
  s.all.pipe(
300
331
  Effect.map(query.memory),
301
- Effect.withSpan("PgSQL.queryRaw [effect-app/infra/Store]", {
302
- attributes: { "repository.table_name": tableName, "repository.model_name": name }
303
- }, { captureStackTrace: false })
332
+ withDbSpan({
333
+ operation: "queryRaw",
334
+ system: "postgresql",
335
+ collection: tableName,
336
+ entity: name
337
+ })
304
338
  )
305
339
  }
306
340