@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.
- package/CHANGELOG.md +55 -0
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +28 -21
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +37 -14
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +37 -14
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +28 -13
- package/dist/RequestContext.d.ts +6 -6
- package/dist/RequestContext.js +7 -7
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +80 -66
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +48 -14
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +60 -35
- package/dist/Store/SQL/Pg.d.ts.map +1 -1
- package/dist/Store/SQL/Pg.js +80 -38
- package/dist/Store/SQL.d.ts.map +1 -1
- package/dist/Store/SQL.js +160 -77
- package/dist/adapters/SQL/Model.d.ts +4 -1
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +28 -37
- package/dist/api/internal/events.js +2 -2
- package/dist/api/routing/middleware/middleware.js +3 -3
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +12 -2
- package/dist/otel.d.ts +66 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +56 -0
- package/package.json +6 -2
- package/src/Model/Repository/internal/internal.ts +82 -71
- package/src/QueueMaker/SQLQueue.ts +35 -13
- package/src/QueueMaker/memQueue.ts +35 -13
- package/src/QueueMaker/sbqueue.ts +26 -12
- package/src/RequestContext.ts +6 -6
- package/src/Store/Cosmos.ts +81 -66
- package/src/Store/Disk.ts +50 -16
- package/src/Store/Memory.ts +59 -34
- package/src/Store/SQL/Pg.ts +74 -40
- package/src/Store/SQL.ts +149 -83
- package/src/adapters/SQL/Model.ts +31 -36
- package/src/api/internal/events.ts +1 -1
- package/src/api/routing/middleware/middleware.ts +2 -2
- package/src/api/routing.ts +11 -1
- package/src/otel.ts +141 -0
|
@@ -76,7 +76,7 @@ export function makeRepoInternal<
|
|
|
76
76
|
const encodeMany = flow(
|
|
77
77
|
S.encodeEffect(S.Array(schema)),
|
|
78
78
|
provideRctx,
|
|
79
|
-
Effect.withSpan("encodeMany", { attributes: {
|
|
79
|
+
Effect.withSpan("encodeMany", { attributes: { "app.entity": name } }, { captureStackTrace: false })
|
|
80
80
|
)
|
|
81
81
|
const decode = flow(S.decodeEffectConcurrently(schema), provideRctx)
|
|
82
82
|
const decodeMany = flow(
|
|
@@ -140,7 +140,7 @@ export function makeRepoInternal<
|
|
|
140
140
|
Effect.map((_: Record<string, unknown>) => _[idKey as string] as Encoded[IdKey])
|
|
141
141
|
)
|
|
142
142
|
const findEId = Effect.fnUntraced(function*(id: Encoded[IdKey]) {
|
|
143
|
-
yield* Effect.annotateCurrentSpan({
|
|
143
|
+
yield* Effect.annotateCurrentSpan({ "app.entity.id": id })
|
|
144
144
|
|
|
145
145
|
return yield* Effect.flatMap(
|
|
146
146
|
store.find(id),
|
|
@@ -153,7 +153,7 @@ export function makeRepoInternal<
|
|
|
153
153
|
})
|
|
154
154
|
// TODO: select the particular field, instead of as struct
|
|
155
155
|
const findE = Effect.fnUntraced(function*(id: T[IdKey]) {
|
|
156
|
-
yield* Effect.annotateCurrentSpan({
|
|
156
|
+
yield* Effect.annotateCurrentSpan({ "app.entity.id": id })
|
|
157
157
|
|
|
158
158
|
return yield* pipe(
|
|
159
159
|
encodeId({ [idKey]: id } as any),
|
|
@@ -163,8 +163,8 @@ export function makeRepoInternal<
|
|
|
163
163
|
)
|
|
164
164
|
})
|
|
165
165
|
|
|
166
|
-
const find = Effect.fn("find", { attributes: {
|
|
167
|
-
yield* Effect.annotateCurrentSpan({
|
|
166
|
+
const find = Effect.fn("find", { attributes: { "app.entity": name } })(function*(id: T[IdKey]) {
|
|
167
|
+
yield* Effect.annotateCurrentSpan({ "app.entity.id": id })
|
|
168
168
|
|
|
169
169
|
return yield* flatMapOption(findE(id), (_) => Effect.orDie(decode(_)))
|
|
170
170
|
})
|
|
@@ -190,11 +190,14 @@ export function makeRepoInternal<
|
|
|
190
190
|
Effect.andThen(saveAllE)
|
|
191
191
|
)
|
|
192
192
|
|
|
193
|
-
const saveAndPublish = Effect.fn("saveAndPublish", { attributes: {
|
|
193
|
+
const saveAndPublish = Effect.fn("saveAndPublish", { attributes: { "app.entity": name } })(
|
|
194
194
|
function*(items: Iterable<T>, events: Iterable<Evt> = []) {
|
|
195
195
|
const it = Chunk.fromIterable(items)
|
|
196
196
|
const evts = [...events]
|
|
197
|
-
yield* Effect.annotateCurrentSpan({
|
|
197
|
+
yield* Effect.annotateCurrentSpan({
|
|
198
|
+
"app.entity.ids": Chunk.map(it, (_) => _[idKey]),
|
|
199
|
+
"app.event.count": evts.length
|
|
200
|
+
})
|
|
198
201
|
return yield* saveAll(it)
|
|
199
202
|
.pipe(
|
|
200
203
|
Effect.andThen(Effect.sync(() => toNonEmptyArray(evts))),
|
|
@@ -206,12 +209,15 @@ export function makeRepoInternal<
|
|
|
206
209
|
}
|
|
207
210
|
)
|
|
208
211
|
|
|
209
|
-
const removeAndPublish = Effect.fn("removeAndPublish", { attributes: {
|
|
212
|
+
const removeAndPublish = Effect.fn("removeAndPublish", { attributes: { "app.entity": name } })(
|
|
210
213
|
function*(a: Iterable<T>, events: Iterable<Evt> = []) {
|
|
211
214
|
const { set } = yield* cms
|
|
212
215
|
const it = [...a]
|
|
213
216
|
const evts = [...events]
|
|
214
|
-
yield* Effect.annotateCurrentSpan({
|
|
217
|
+
yield* Effect.annotateCurrentSpan({
|
|
218
|
+
"app.entity.ids": it.map((_) => _[idKey]),
|
|
219
|
+
"app.event.count": evts.length
|
|
220
|
+
})
|
|
215
221
|
const items = yield* encodeMany(it).pipe(Effect.orDie)
|
|
216
222
|
if (Array.isReadonlyArrayNonEmpty(items)) {
|
|
217
223
|
yield* store.batchRemove(
|
|
@@ -231,7 +237,7 @@ export function makeRepoInternal<
|
|
|
231
237
|
}
|
|
232
238
|
)
|
|
233
239
|
|
|
234
|
-
const removeById = Effect.fn("removeById", { attributes: {
|
|
240
|
+
const removeById = Effect.fn("removeById", { attributes: { "app.entity": name } })(
|
|
235
241
|
function*(idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>) {
|
|
236
242
|
const ids = globalThis.Array.isArray(idOrIds)
|
|
237
243
|
? idOrIds as readonly T[IdKey][]
|
|
@@ -241,7 +247,7 @@ export function makeRepoInternal<
|
|
|
241
247
|
}
|
|
242
248
|
const { set } = yield* cms
|
|
243
249
|
const eids = yield* Effect.forEach(ids, (_) => encodeIdOnly(_ as any)).pipe(Effect.orDie)
|
|
244
|
-
yield* Effect.annotateCurrentSpan({
|
|
250
|
+
yield* Effect.annotateCurrentSpan({ "app.entity.ids": eids })
|
|
245
251
|
yield* store.batchRemove(eids)
|
|
246
252
|
for (const id of eids) {
|
|
247
253
|
set(id, undefined)
|
|
@@ -250,11 +256,13 @@ export function makeRepoInternal<
|
|
|
250
256
|
}
|
|
251
257
|
)
|
|
252
258
|
|
|
253
|
-
const parseMany = Effect.fn("parseMany", { attributes: {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
259
|
+
const parseMany = Effect.fn("parseMany", { attributes: { "app.entity": name } })(
|
|
260
|
+
function*(items: readonly PM[]) {
|
|
261
|
+
const cm = yield* cms
|
|
262
|
+
return yield* decodeMany(items.map((_) => mapReverse(_, cm.set))).pipe(Effect.orDie)
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
const parseMany2 = Effect.fn("parseMany2", { attributes: { "app.entity": name } })(
|
|
258
266
|
function*<A, R>(items: readonly PM[], schema: S.Codec<A, Encoded, R>) {
|
|
259
267
|
const cm = yield* cms
|
|
260
268
|
return yield* S.decodeEffectConcurrently(S.Array(schema))(items.map((_) => mapReverse(_, cm.set))).pipe(
|
|
@@ -332,73 +340,76 @@ export function makeRepoInternal<
|
|
|
332
340
|
.map(eff, (_) => NonNegativeInt(_.length))
|
|
333
341
|
.pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e)))
|
|
334
342
|
: eff,
|
|
335
|
-
Effect.withSpan(
|
|
343
|
+
Effect.withSpan(`query ${name}`, {
|
|
344
|
+
kind: "client",
|
|
336
345
|
attributes: {
|
|
337
|
-
|
|
338
|
-
"
|
|
339
|
-
|
|
346
|
+
"app.entity": name,
|
|
347
|
+
"db.operation.name": "query",
|
|
348
|
+
"db.collection.name": name
|
|
340
349
|
}
|
|
341
350
|
}, { captureStackTrace: false })
|
|
342
351
|
)
|
|
343
352
|
}) as any
|
|
344
353
|
|
|
345
|
-
const validateSample = Effect.fn("validateSample", { attributes: {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// 3. validate each item
|
|
367
|
-
const errors: ValidationError[] = []
|
|
354
|
+
const validateSample = Effect.fn("validateSample", { attributes: { "app.entity": name } })(
|
|
355
|
+
function*(options?: {
|
|
356
|
+
percentage?: number
|
|
357
|
+
maxItems?: number
|
|
358
|
+
}) {
|
|
359
|
+
const percentage = options?.percentage ?? 0.1 // default 10%
|
|
360
|
+
const maxItems = options?.maxItems
|
|
361
|
+
|
|
362
|
+
// 1. get all IDs with projection (bypasses main schema decode)
|
|
363
|
+
const allIds = yield* store.filter({
|
|
364
|
+
t: null as unknown as Encoded,
|
|
365
|
+
select: [idKey as keyof Encoded]
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// 2. random subset
|
|
369
|
+
const shuffled = [...allIds].sort(() => Math.random() - 0.5)
|
|
370
|
+
const sampleSize = Math.min(
|
|
371
|
+
maxItems ?? Infinity,
|
|
372
|
+
Math.ceil(allIds.length * percentage)
|
|
373
|
+
)
|
|
374
|
+
const sample = shuffled.slice(0, sampleSize)
|
|
368
375
|
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
const rawResult = yield* store.find(id)
|
|
376
|
+
// 3. validate each item
|
|
377
|
+
const errors: ValidationError[] = []
|
|
372
378
|
|
|
373
|
-
|
|
379
|
+
for (const item of sample) {
|
|
380
|
+
const id = item[idKey]
|
|
381
|
+
const rawResult = yield* store.find(id)
|
|
374
382
|
|
|
375
|
-
|
|
376
|
-
const jitMResult = mapFrom(rawData) // apply jitM
|
|
383
|
+
if (Option.isNone(rawResult)) continue
|
|
377
384
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
provideRctx
|
|
381
|
-
)
|
|
385
|
+
const rawData = rawResult.value as Encoded
|
|
386
|
+
const jitMResult = mapFrom(rawData) // apply jitM
|
|
382
387
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
id,
|
|
387
|
-
rawData,
|
|
388
|
-
jitMResult,
|
|
389
|
-
error: decodeResult.failure
|
|
390
|
-
})
|
|
388
|
+
const decodeResult = yield* S.decodeEffectConcurrently(schema)(jitMResult).pipe(
|
|
389
|
+
Effect.result,
|
|
390
|
+
provideRctx
|
|
391
391
|
)
|
|
392
|
+
|
|
393
|
+
if (Result.isFailure(decodeResult)) {
|
|
394
|
+
errors.push(
|
|
395
|
+
ValidationError.make({
|
|
396
|
+
id,
|
|
397
|
+
rawData,
|
|
398
|
+
jitMResult,
|
|
399
|
+
error: decodeResult.failure
|
|
400
|
+
})
|
|
401
|
+
)
|
|
402
|
+
}
|
|
392
403
|
}
|
|
393
|
-
}
|
|
394
404
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
405
|
+
return ValidationResult.make({
|
|
406
|
+
total: NonNegativeInt(allIds.length),
|
|
407
|
+
sampled: NonNegativeInt(sample.length),
|
|
408
|
+
valid: NonNegativeInt(sample.length - errors.length),
|
|
409
|
+
errors
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
)
|
|
402
413
|
|
|
403
414
|
const r = {
|
|
404
415
|
changeFeed,
|
|
@@ -450,7 +461,7 @@ export function makeRepoInternal<
|
|
|
450
461
|
// },
|
|
451
462
|
save: (...xes: any[]) =>
|
|
452
463
|
Effect.flatMap(encMany(xes), (_) => saveAllE(_)).pipe(
|
|
453
|
-
Effect.withSpan("mapped.save", { attributes: {
|
|
464
|
+
Effect.withSpan("mapped.save", { attributes: { "app.entity": name } }, { captureStackTrace: false })
|
|
454
465
|
)
|
|
455
466
|
}
|
|
456
467
|
}
|
|
@@ -522,7 +533,7 @@ export function makeStore<Encoded extends FieldValues>() {
|
|
|
522
533
|
.pipe(
|
|
523
534
|
Effect.flatMap(Effect.forEach(encodeToEncoded())),
|
|
524
535
|
setupRequestContextFromCurrent("Repository.makeInitial [effect-app/infra]", {
|
|
525
|
-
attributes: { "
|
|
536
|
+
attributes: { "app.entity": name }
|
|
526
537
|
})
|
|
527
538
|
)
|
|
528
539
|
: undefined,
|
|
@@ -8,6 +8,7 @@ import { pretty } from "effect-app/utils"
|
|
|
8
8
|
import { SqlClient } from "effect/unstable/sql"
|
|
9
9
|
import { SQLModel } from "../adapters/SQL.js"
|
|
10
10
|
import { InfraLogger } from "../logger.js"
|
|
11
|
+
import { messagingSpanArgs } from "../otel.js"
|
|
11
12
|
|
|
12
13
|
export const QueueId = S.Finite.pipe(S.brand("QueueId"))
|
|
13
14
|
export type QueueId = typeof QueueId.Type
|
|
@@ -101,10 +102,20 @@ export const makeSQLQueue = Effect.fnUntraced(function*<
|
|
|
101
102
|
})
|
|
102
103
|
}
|
|
103
104
|
const queue = {
|
|
104
|
-
publish: Effect.fn(
|
|
105
|
+
publish: Effect.fn(`publish ${queueName}`, {
|
|
106
|
+
kind: "producer",
|
|
107
|
+
attributes: {
|
|
108
|
+
"messaging.system": "sql",
|
|
109
|
+
"messaging.operation.name": "publish",
|
|
110
|
+
"messaging.destination.name": queueName
|
|
111
|
+
}
|
|
112
|
+
})(function*(
|
|
105
113
|
...messages: NonEmptyReadonlyArray<Evt>
|
|
106
114
|
) {
|
|
107
|
-
yield* Effect.annotateCurrentSpan({
|
|
115
|
+
yield* Effect.annotateCurrentSpan({
|
|
116
|
+
"messaging.batch.message_count": messages.length,
|
|
117
|
+
"messaging.message.types": messages.map((_) => _._tag)
|
|
118
|
+
})
|
|
108
119
|
const requestContext = yield* getRequestContext
|
|
109
120
|
yield* Effect.forEach(messages, (m) => q.offer(m, requestContext), { discard: true })
|
|
110
121
|
}),
|
|
@@ -120,21 +131,26 @@ export const makeSQLQueue = Effect.fnUntraced(function*<
|
|
|
120
131
|
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
121
132
|
Effect.andThen(handleEvent(body)),
|
|
122
133
|
silenceAndReportError,
|
|
123
|
-
(_) =>
|
|
124
|
-
|
|
134
|
+
(_) => {
|
|
135
|
+
const args = messagingSpanArgs({
|
|
136
|
+
operation: "process",
|
|
137
|
+
system: "sql",
|
|
138
|
+
destination: queueDrainName,
|
|
139
|
+
messageId: body.id,
|
|
140
|
+
conversationId: sessionId,
|
|
141
|
+
extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
|
|
142
|
+
}, "consumer")
|
|
143
|
+
return setupRequestContextWithCustomSpan(
|
|
125
144
|
_,
|
|
126
145
|
meta,
|
|
127
|
-
|
|
146
|
+
args.name,
|
|
128
147
|
{
|
|
129
148
|
captureStackTrace: false,
|
|
130
|
-
kind:
|
|
131
|
-
attributes:
|
|
132
|
-
"queue.name": queueDrainName,
|
|
133
|
-
"queue.sessionId": sessionId,
|
|
134
|
-
"queue.input": body
|
|
135
|
-
}
|
|
149
|
+
kind: args.kind,
|
|
150
|
+
attributes: args.attributes
|
|
136
151
|
}
|
|
137
152
|
)
|
|
153
|
+
}
|
|
138
154
|
)
|
|
139
155
|
if (meta.span) {
|
|
140
156
|
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
@@ -142,8 +158,14 @@ export const makeSQLQueue = Effect.fnUntraced(function*<
|
|
|
142
158
|
return yield* effect
|
|
143
159
|
})
|
|
144
160
|
|
|
145
|
-
return Effect.fn(`
|
|
146
|
-
|
|
161
|
+
return Effect.fn(`receive ${queueDrainName}`, {
|
|
162
|
+
kind: "consumer",
|
|
163
|
+
attributes: {
|
|
164
|
+
"messaging.system": "sql",
|
|
165
|
+
"messaging.operation.name": "receive",
|
|
166
|
+
"messaging.destination.name": queueDrainName,
|
|
167
|
+
...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
|
|
168
|
+
}
|
|
147
169
|
})(function*() {
|
|
148
170
|
const x = yield* q.take
|
|
149
171
|
yield* processMessage(x).pipe(
|
|
@@ -5,6 +5,7 @@ import * as Q from "effect/Queue"
|
|
|
5
5
|
import { MemQueue } from "../adapters/memQueue.js"
|
|
6
6
|
import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
|
|
7
7
|
import { InfraLogger } from "../logger.js"
|
|
8
|
+
import { messagingSpanArgs } from "../otel.js"
|
|
8
9
|
import { reportNonInterruptedFailure, reportNonInterruptedFailureCause } from "./errors.js"
|
|
9
10
|
import { QueueMeta } from "./service.js"
|
|
10
11
|
|
|
@@ -32,10 +33,20 @@ export const makeMemQueue = Effect.fnUntraced(function*<
|
|
|
32
33
|
const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
|
|
33
34
|
|
|
34
35
|
const queue = {
|
|
35
|
-
publish: Effect.fn(
|
|
36
|
+
publish: Effect.fn(`publish ${queueName}`, {
|
|
37
|
+
kind: "producer",
|
|
38
|
+
attributes: {
|
|
39
|
+
"messaging.system": "memory",
|
|
40
|
+
"messaging.operation.name": "publish",
|
|
41
|
+
"messaging.destination.name": queueName
|
|
42
|
+
}
|
|
43
|
+
})(function*(
|
|
36
44
|
...messages: NonEmptyReadonlyArray<Evt>
|
|
37
45
|
) {
|
|
38
|
-
yield* Effect.annotateCurrentSpan({
|
|
46
|
+
yield* Effect.annotateCurrentSpan({
|
|
47
|
+
"messaging.batch.message_count": messages.length,
|
|
48
|
+
"messaging.message.types": messages.map((_) => _._tag)
|
|
49
|
+
})
|
|
39
50
|
const requestContext = yield* getRequestContext
|
|
40
51
|
// we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
|
|
41
52
|
yield* Effect.forEach(
|
|
@@ -63,29 +74,40 @@ export const makeMemQueue = Effect.fnUntraced(function*<
|
|
|
63
74
|
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
64
75
|
Effect.andThen(handleEvent(body)),
|
|
65
76
|
silenceAndReportError,
|
|
66
|
-
(_) =>
|
|
67
|
-
|
|
77
|
+
(_) => {
|
|
78
|
+
const args = messagingSpanArgs({
|
|
79
|
+
operation: "process",
|
|
80
|
+
system: "memory",
|
|
81
|
+
destination: queueDrainName,
|
|
82
|
+
messageId: body.id,
|
|
83
|
+
conversationId: sessionId,
|
|
84
|
+
extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
|
|
85
|
+
}, "consumer")
|
|
86
|
+
return setupRequestContextWithCustomSpan(
|
|
68
87
|
_,
|
|
69
88
|
meta,
|
|
70
|
-
|
|
89
|
+
args.name,
|
|
71
90
|
{
|
|
72
91
|
captureStackTrace: false,
|
|
73
|
-
kind:
|
|
74
|
-
attributes:
|
|
75
|
-
"queue.name": queueDrainName,
|
|
76
|
-
"queue.sessionId": sessionId,
|
|
77
|
-
"queue.input": body
|
|
78
|
-
}
|
|
92
|
+
kind: args.kind,
|
|
93
|
+
attributes: args.attributes
|
|
79
94
|
}
|
|
80
95
|
)
|
|
96
|
+
}
|
|
81
97
|
)
|
|
82
98
|
if (meta.span) {
|
|
83
99
|
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
84
100
|
}
|
|
85
101
|
return yield* effect
|
|
86
102
|
})
|
|
87
|
-
return Effect.fn(`
|
|
88
|
-
|
|
103
|
+
return Effect.fn(`receive ${queueDrainName}`, {
|
|
104
|
+
kind: "consumer",
|
|
105
|
+
attributes: {
|
|
106
|
+
"messaging.system": "memory",
|
|
107
|
+
"messaging.operation.name": "receive",
|
|
108
|
+
"messaging.destination.name": queueDrainName,
|
|
109
|
+
...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
|
|
110
|
+
}
|
|
89
111
|
})(function*() {
|
|
90
112
|
const x = yield* Q.take(qDrain)
|
|
91
113
|
const exit = yield* processMessage(x).pipe(
|
|
@@ -5,6 +5,7 @@ import { pretty } from "effect-app/utils"
|
|
|
5
5
|
import { Receiver, Sender } from "../adapters/ServiceBus.js"
|
|
6
6
|
import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
|
|
7
7
|
import { InfraLogger } from "../logger.js"
|
|
8
|
+
import { messagingSpanArgs } from "../otel.js"
|
|
8
9
|
import { reportNonInterruptedFailure, reportNonInterruptedFailureCause, reportQueueError } from "./errors.js"
|
|
9
10
|
import { QueueMeta } from "./service.js"
|
|
10
11
|
|
|
@@ -52,21 +53,26 @@ export function makeServiceBusQueue<
|
|
|
52
53
|
Effect.orDie,
|
|
53
54
|
// we silenceAndReportError here, so that the error is reported, and moves into the Exit.
|
|
54
55
|
silenceAndReportError,
|
|
55
|
-
(_) =>
|
|
56
|
-
|
|
56
|
+
(_) => {
|
|
57
|
+
const args = messagingSpanArgs({
|
|
58
|
+
operation: "process",
|
|
59
|
+
system: "servicebus",
|
|
60
|
+
destination: receiver.name,
|
|
61
|
+
messageId: body.id,
|
|
62
|
+
conversationId: sessionId,
|
|
63
|
+
extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
|
|
64
|
+
}, "consumer")
|
|
65
|
+
return setupRequestContextWithCustomSpan(
|
|
57
66
|
_,
|
|
58
67
|
meta,
|
|
59
|
-
|
|
68
|
+
args.name,
|
|
60
69
|
{
|
|
61
70
|
captureStackTrace: false,
|
|
62
|
-
kind:
|
|
63
|
-
attributes:
|
|
64
|
-
"queue.name": receiver.name,
|
|
65
|
-
"queue.sessionId": sessionId,
|
|
66
|
-
"queue.input": body
|
|
67
|
-
}
|
|
71
|
+
kind: args.kind,
|
|
72
|
+
attributes: args.attributes
|
|
68
73
|
}
|
|
69
74
|
)
|
|
75
|
+
}
|
|
70
76
|
)
|
|
71
77
|
if (meta.span) {
|
|
72
78
|
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
@@ -84,10 +90,18 @@ export function makeServiceBusQueue<
|
|
|
84
90
|
.pipe(Effect.andThen(Effect.never))
|
|
85
91
|
},
|
|
86
92
|
|
|
87
|
-
publish: Effect.fn(
|
|
88
|
-
kind: "producer"
|
|
93
|
+
publish: Effect.fn(`publish ${sender.name}`, {
|
|
94
|
+
kind: "producer",
|
|
95
|
+
attributes: {
|
|
96
|
+
"messaging.system": "servicebus",
|
|
97
|
+
"messaging.operation.name": "publish",
|
|
98
|
+
"messaging.destination.name": sender.name
|
|
99
|
+
}
|
|
89
100
|
})(function*(...messages: NonEmptyReadonlyArray<Evt>) {
|
|
90
|
-
yield* Effect.annotateCurrentSpan({
|
|
101
|
+
yield* Effect.annotateCurrentSpan({
|
|
102
|
+
"messaging.batch.message_count": messages.length,
|
|
103
|
+
"messaging.message.types": messages.map((_) => _._tag)
|
|
104
|
+
})
|
|
91
105
|
const requestContext = yield* getRequestContext
|
|
92
106
|
const msgs = yield* Effect.forEach(messages, (m) =>
|
|
93
107
|
encodePublish({ body: m, meta: requestContext }).pipe(
|
package/src/RequestContext.ts
CHANGED
|
@@ -34,16 +34,16 @@ export class RequestContext extends S.Opaque<
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export const spanAttributes = (ctx: Pick<RequestContext, "locale" | "namespace"> & Partial<RequestContext>) => ({
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
...ctx.sourceId ? { "
|
|
37
|
+
"code.function.name": ctx.name,
|
|
38
|
+
"app.locale": ctx.locale,
|
|
39
|
+
"app.tenant.id": ctx.namespace,
|
|
40
|
+
...ctx.sourceId ? { "client.id": ctx.sourceId } : {},
|
|
41
41
|
...(ctx.userProfile?.sub
|
|
42
42
|
? {
|
|
43
|
-
"
|
|
43
|
+
"user.id": ctx
|
|
44
44
|
.userProfile
|
|
45
45
|
.sub,
|
|
46
|
-
"
|
|
46
|
+
"user.roles": "roles" in ctx
|
|
47
47
|
.userProfile
|
|
48
48
|
? ctx.userProfile.roles
|
|
49
49
|
: undefined
|