azify-logger 1.0.35 → 1.0.37
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/middleware-express.js +140 -30
- package/package.json +1 -1
- package/queue/redisQueue.js +54 -22
- package/register.js +629 -125
- package/scripts/redis-worker.js +30 -8
- package/store.js +17 -1
- package/streams/httpQueue.js +43 -10
package/middleware-express.js
CHANGED
|
@@ -144,14 +144,29 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
144
144
|
return worker
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
function sendLog(level, message, meta = {}) {
|
|
148
|
-
if (!transport || typeof transport.enqueue !== 'function')
|
|
147
|
+
async function sendLog(level, message, meta = {}) {
|
|
148
|
+
if (!transport || typeof transport.enqueue !== 'function') {
|
|
149
|
+
try {
|
|
150
|
+
console.log(`[AZIFY-LOGGER-FALLBACK] ${message}`, JSON.stringify(meta, null, 2))
|
|
151
|
+
} catch (_) {
|
|
152
|
+
}
|
|
153
|
+
return
|
|
154
|
+
}
|
|
149
155
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
try {
|
|
157
|
+
// AGUARDAR o enqueue para garantir que o log seja adicionado ao Redis antes de continuar
|
|
158
|
+
// transport.enqueue agora é assíncrono e sempre retorna uma Promise
|
|
159
|
+
await transport.enqueue({
|
|
160
|
+
level,
|
|
161
|
+
message,
|
|
162
|
+
meta
|
|
163
|
+
}, { 'content-type': 'application/json' })
|
|
164
|
+
} catch (err) {
|
|
165
|
+
try {
|
|
166
|
+
console.log(`[AZIFY-LOGGER-FALLBACK] ${message}`, JSON.stringify(meta, null, 2))
|
|
167
|
+
} catch (_) {
|
|
168
|
+
}
|
|
169
|
+
}
|
|
155
170
|
}
|
|
156
171
|
|
|
157
172
|
return function azifyExpressLoggingMiddleware(req, res, next) {
|
|
@@ -210,14 +225,14 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
210
225
|
|
|
211
226
|
const ctx = ensureRequestContext()
|
|
212
227
|
|
|
213
|
-
const logWithContext = (level, message, meta) => {
|
|
228
|
+
const logWithContext = async (level, message, meta) => {
|
|
214
229
|
const otelCtx = getOtelTraceContext()
|
|
215
230
|
const ctx = getRequestContext() || ensureRequestContext()
|
|
216
231
|
|
|
217
232
|
meta.traceId = otelCtx?.traceId || meta.traceId || ctx.traceId
|
|
218
233
|
meta.spanId = otelCtx?.spanId || meta.spanId || ctx.spanId
|
|
219
234
|
meta.parentSpanId = otelCtx?.parentSpanId || meta.parentSpanId || ctx.parentSpanId
|
|
220
|
-
sendLog(level, message, meta)
|
|
235
|
+
await sendLog(level, message, meta)
|
|
221
236
|
}
|
|
222
237
|
|
|
223
238
|
function ensureIds() {
|
|
@@ -255,19 +270,20 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
255
270
|
}
|
|
256
271
|
}
|
|
257
272
|
|
|
258
|
-
function emitResponseLog(meta, chunk) {
|
|
273
|
+
async function emitResponseLog(meta, chunk) {
|
|
259
274
|
if (!config.captureResponseBody || chunk == null) {
|
|
260
|
-
logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
275
|
+
await logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
261
276
|
return
|
|
262
277
|
}
|
|
263
278
|
|
|
264
279
|
if (!meta.response) meta.response = {}
|
|
265
280
|
meta.response.body = safeSerializeBody(chunk)
|
|
266
|
-
logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
281
|
+
await logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
267
282
|
}
|
|
268
283
|
|
|
269
284
|
if (config.logRequest) {
|
|
270
|
-
|
|
285
|
+
let requestLogEmitted = false
|
|
286
|
+
try {
|
|
271
287
|
ensureIds()
|
|
272
288
|
ensureHeaders()
|
|
273
289
|
|
|
@@ -300,25 +316,55 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
300
316
|
if (config.environment) meta.environment = config.environment
|
|
301
317
|
|
|
302
318
|
logWithContext('info', `[REQUEST] ${method} ${url}`, meta)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
319
|
+
requestLogEmitted = true
|
|
320
|
+
} catch (err) {
|
|
321
|
+
try {
|
|
322
|
+
ensureIds()
|
|
323
|
+
const minimalMeta = {
|
|
324
|
+
traceId: reqCtx?.traceId || traceId,
|
|
325
|
+
spanId: reqCtx?.spanId || spanId,
|
|
326
|
+
parentSpanId: reqCtx?.parentSpanId || parentSpanId || null,
|
|
327
|
+
requestId: requestId || fastUUID(),
|
|
328
|
+
method,
|
|
329
|
+
url,
|
|
330
|
+
path,
|
|
331
|
+
timestamp: Date.now(),
|
|
332
|
+
hostname
|
|
333
|
+
}
|
|
334
|
+
if (serviceObj) minimalMeta.service = serviceObj
|
|
335
|
+
if (config.environment) minimalMeta.environment = config.environment
|
|
336
|
+
logWithContext('info', `[REQUEST] ${method} ${url}`, minimalMeta)
|
|
337
|
+
requestLogEmitted = true
|
|
338
|
+
} catch (_) {
|
|
339
|
+
try {
|
|
340
|
+
sendLog('info', `[REQUEST] ${method} ${url}`, {
|
|
341
|
+
method,
|
|
342
|
+
url,
|
|
343
|
+
path
|
|
344
|
+
})
|
|
345
|
+
requestLogEmitted = true
|
|
346
|
+
} catch (_) {
|
|
347
|
+
try {
|
|
348
|
+
console.log(`[AZIFY-LOGGER-EMERGENCY] [REQUEST] ${method} ${url}`)
|
|
349
|
+
} catch (_) {
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
311
353
|
}
|
|
312
|
-
logSent = true
|
|
313
354
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
355
|
+
if (!requestLogEmitted) {
|
|
356
|
+
try {
|
|
357
|
+
console.log(`[AZIFY-LOGGER-EMERGENCY] [REQUEST] ${method} ${url}`)
|
|
358
|
+
} catch (_) {
|
|
359
|
+
}
|
|
319
360
|
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function emitResponse() {
|
|
364
|
+
if (logSent) return
|
|
365
|
+
logSent = true
|
|
320
366
|
|
|
321
|
-
|
|
367
|
+
try {
|
|
322
368
|
ensureIds()
|
|
323
369
|
|
|
324
370
|
const statusCode = res.statusCode || 200
|
|
@@ -353,12 +399,76 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
353
399
|
if (config.environment) meta.environment = config.environment
|
|
354
400
|
|
|
355
401
|
const chunkToProcess = (responseChunk !== null && responseChunkCaptured) ? responseChunk : null
|
|
356
|
-
emitResponseLog(meta, chunkToProcess)
|
|
357
|
-
})
|
|
402
|
+
await emitResponseLog(meta, chunkToProcess)
|
|
403
|
+
} catch (err) {
|
|
404
|
+
try {
|
|
405
|
+
ensureIds()
|
|
406
|
+
const statusCode = res.statusCode || 200
|
|
407
|
+
const duration = Date.now() - startTime
|
|
408
|
+
const response = { statusCode, durationMs: duration }
|
|
409
|
+
const requestObj = {
|
|
410
|
+
id: requestId,
|
|
411
|
+
method,
|
|
412
|
+
url,
|
|
413
|
+
path,
|
|
414
|
+
ip: clientIp || 'unknown'
|
|
415
|
+
}
|
|
416
|
+
const meta = {
|
|
417
|
+
traceId: reqCtx?.traceId || traceId,
|
|
418
|
+
spanId: reqCtx?.spanId || spanId,
|
|
419
|
+
parentSpanId: reqCtx?.parentSpanId || parentSpanId || null,
|
|
420
|
+
requestId: requestId || fastUUID(),
|
|
421
|
+
request: requestObj,
|
|
422
|
+
response,
|
|
423
|
+
timestamp: Date.now(),
|
|
424
|
+
hostname
|
|
425
|
+
}
|
|
426
|
+
if (serviceObj) meta.service = serviceObj
|
|
427
|
+
if (config.environment) meta.environment = config.environment
|
|
428
|
+
await emitResponseLog(meta, null)
|
|
429
|
+
} catch (fallbackErr) {
|
|
430
|
+
try {
|
|
431
|
+
ensureIds()
|
|
432
|
+
const statusCode = res.statusCode || 200
|
|
433
|
+
const duration = Date.now() - startTime
|
|
434
|
+
await sendLog('info', `[RESPONSE] ${method} ${url}`, {
|
|
435
|
+
traceId: reqCtx?.traceId || traceId,
|
|
436
|
+
spanId: reqCtx?.spanId || spanId,
|
|
437
|
+
parentSpanId: reqCtx?.parentSpanId || parentSpanId || null,
|
|
438
|
+
requestId: requestId || fastUUID(),
|
|
439
|
+
method,
|
|
440
|
+
url,
|
|
441
|
+
path,
|
|
442
|
+
statusCode,
|
|
443
|
+
durationMs: duration,
|
|
444
|
+
timestamp: Date.now(),
|
|
445
|
+
hostname
|
|
446
|
+
})
|
|
447
|
+
} catch (_) {
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const originalEnd = res.end.bind(res)
|
|
454
|
+
|
|
455
|
+
res.end = (chunk, encoding) => {
|
|
456
|
+
if (chunk != null && config.captureResponseBody && !responseChunkCaptured) {
|
|
457
|
+
responseChunk = chunk
|
|
458
|
+
responseChunkCaptured = true
|
|
459
|
+
}
|
|
358
460
|
|
|
461
|
+
const result = originalEnd(chunk, encoding)
|
|
462
|
+
void emitResponse()
|
|
359
463
|
return result
|
|
360
464
|
}
|
|
361
465
|
|
|
466
|
+
// Também capturar o evento 'finish' para garantir que o RESPONSE seja logado
|
|
467
|
+
// mesmo quando res.json() ou res.send() são usados sem chamar res.end() explicitamente
|
|
468
|
+
res.once('finish', () => {
|
|
469
|
+
void emitResponse()
|
|
470
|
+
})
|
|
471
|
+
|
|
362
472
|
runWithRequestContext(ctx, () => {
|
|
363
473
|
next()
|
|
364
474
|
})
|
package/package.json
CHANGED
package/queue/redisQueue.js
CHANGED
|
@@ -29,6 +29,7 @@ function createRedisProducer(config = {}) {
|
|
|
29
29
|
const streamKey = config.streamKey || DEFAULT_STREAM_KEY
|
|
30
30
|
const maxLen = Number.isFinite(config.maxLen) ? config.maxLen : DEFAULT_MAXLEN
|
|
31
31
|
const redisOptions = { ...defaultRedisOptions, ...(config.redisOptions || {}) }
|
|
32
|
+
const spool = config.spool || null
|
|
32
33
|
|
|
33
34
|
const client = new Redis(url, redisOptions)
|
|
34
35
|
let lastConnectionErrorLog = 0
|
|
@@ -69,8 +70,9 @@ function createRedisProducer(config = {}) {
|
|
|
69
70
|
const BATCH_SIZE = 1
|
|
70
71
|
const BATCH_TIMEOUT = 0
|
|
71
72
|
let flushing = false
|
|
73
|
+
let currentSpool = spool
|
|
72
74
|
|
|
73
|
-
function flushBatch() {
|
|
75
|
+
async function flushBatch() {
|
|
74
76
|
if (flushing || batch.length === 0) return
|
|
75
77
|
flushing = true
|
|
76
78
|
|
|
@@ -80,12 +82,19 @@ function createRedisProducer(config = {}) {
|
|
|
80
82
|
return
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
try {
|
|
84
86
|
if (entriesToFlush.length === 1) {
|
|
85
87
|
const entry = entriesToFlush[0]
|
|
86
88
|
if (entry) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
try {
|
|
90
|
+
const payload = JSON.stringify(entry)
|
|
91
|
+
// AGUARDAR o xadd para garantir que o log seja adicionado ao Redis antes de continuar
|
|
92
|
+
await client.xadd(streamKey, 'MAXLEN', '~', maxLen, '*', 'entry', payload)
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (currentSpool) {
|
|
95
|
+
currentSpool.append(entry).catch(() => {})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
89
98
|
}
|
|
90
99
|
} else {
|
|
91
100
|
const pipeline = client.pipeline()
|
|
@@ -94,40 +103,58 @@ function createRedisProducer(config = {}) {
|
|
|
94
103
|
for (let i = 0; i < entriesToFlush.length; i++) {
|
|
95
104
|
const entry = entriesToFlush[i]
|
|
96
105
|
if (!entry) continue
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
try {
|
|
107
|
+
const payload = JSON.stringify(entry)
|
|
108
|
+
pipeline.xadd(streamKey, 'MAXLEN', '~', maxLen, '*', 'entry', payload)
|
|
109
|
+
validCount++
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (currentSpool) {
|
|
112
|
+
currentSpool.append(entry).catch(() => {})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
if (validCount > 0) {
|
|
103
|
-
pipeline.exec().catch(() => {
|
|
118
|
+
await pipeline.exec().catch((err) => {
|
|
119
|
+
for (const entry of entriesToFlush) {
|
|
120
|
+
if (currentSpool) {
|
|
121
|
+
currentSpool.append(entry).catch(() => {})
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
})
|
|
104
125
|
}
|
|
105
126
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
scheduleFlush()
|
|
127
|
+
} catch (err) {
|
|
128
|
+
for (const entry of entriesToFlush) {
|
|
129
|
+
if (currentSpool) {
|
|
130
|
+
currentSpool.append(entry).catch(() => {})
|
|
131
|
+
}
|
|
112
132
|
}
|
|
113
|
-
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
flushing = false
|
|
136
|
+
if (batch.length >= BATCH_SIZE) {
|
|
137
|
+
setImmediate(() => { void flushBatch() })
|
|
138
|
+
} else if (batch.length > 0) {
|
|
139
|
+
scheduleFlush()
|
|
140
|
+
}
|
|
114
141
|
}
|
|
115
142
|
|
|
116
143
|
function scheduleFlush() {
|
|
117
144
|
if (batchTimer || flushing) return
|
|
118
145
|
|
|
119
146
|
if (batch.length >= BATCH_SIZE) {
|
|
120
|
-
flushBatch()
|
|
147
|
+
void flushBatch()
|
|
121
148
|
} else if (BATCH_TIMEOUT === 0) {
|
|
122
149
|
setImmediate(() => {
|
|
123
150
|
if (!flushing) {
|
|
124
|
-
flushBatch()
|
|
151
|
+
void flushBatch()
|
|
125
152
|
}
|
|
126
153
|
})
|
|
127
154
|
} else {
|
|
128
155
|
batchTimer = setTimeout(() => {
|
|
129
156
|
batchTimer = null
|
|
130
|
-
flushBatch()
|
|
157
|
+
void flushBatch()
|
|
131
158
|
}, BATCH_TIMEOUT)
|
|
132
159
|
|
|
133
160
|
if (typeof batchTimer.unref === 'function') {
|
|
@@ -136,7 +163,7 @@ function createRedisProducer(config = {}) {
|
|
|
136
163
|
}
|
|
137
164
|
}
|
|
138
165
|
|
|
139
|
-
function enqueue(entry) {
|
|
166
|
+
async function enqueue(entry) {
|
|
140
167
|
batch.push(entry)
|
|
141
168
|
|
|
142
169
|
if (batch.length >= BATCH_SIZE) {
|
|
@@ -144,7 +171,8 @@ function createRedisProducer(config = {}) {
|
|
|
144
171
|
clearTimeout(batchTimer)
|
|
145
172
|
batchTimer = null
|
|
146
173
|
}
|
|
147
|
-
flushBatch
|
|
174
|
+
// AGUARDAR o flushBatch quando o batch está cheio para garantir que o log seja adicionado ao Redis
|
|
175
|
+
await flushBatch()
|
|
148
176
|
} else if (batch.length === 1) {
|
|
149
177
|
scheduleFlush()
|
|
150
178
|
}
|
|
@@ -156,7 +184,7 @@ function createRedisProducer(config = {}) {
|
|
|
156
184
|
batchTimer = null
|
|
157
185
|
}
|
|
158
186
|
while (batch.length > 0) {
|
|
159
|
-
flushBatch()
|
|
187
|
+
await flushBatch()
|
|
160
188
|
await new Promise(resolve => setTimeout(resolve, 10))
|
|
161
189
|
}
|
|
162
190
|
await client.quit().catch(() => {})
|
|
@@ -167,7 +195,11 @@ function createRedisProducer(config = {}) {
|
|
|
167
195
|
client,
|
|
168
196
|
streamKey,
|
|
169
197
|
close,
|
|
170
|
-
flush: flushBatch
|
|
198
|
+
flush: flushBatch,
|
|
199
|
+
setSpool(newSpool) {
|
|
200
|
+
currentSpool = newSpool
|
|
201
|
+
},
|
|
202
|
+
_spool: null
|
|
171
203
|
}
|
|
172
204
|
}
|
|
173
205
|
|