azify-logger 1.0.28 → 1.0.30
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 +50 -14
- package/index.js +2 -1
- package/middleware-express.js +271 -161
- package/middleware-fastify.js +348 -0
- package/middleware-restify.js +74 -59
- package/package.json +16 -18
- package/queue/redisQueue.js +42 -44
- package/register-otel.js +110 -2
- package/register.js +54 -36
- package/sampling.js +1 -1
- package/scripts/redis-worker.js +25 -53
- package/server.js +128 -47
- package/store.js +10 -4
- package/streams/httpQueue.js +65 -80
package/README.md
CHANGED
|
@@ -10,15 +10,15 @@ Sistema de logging centralizado com OpenTelemetry e OpenSearch para múltiplas a
|
|
|
10
10
|
|----------|-----|
|
|
11
11
|
| **Development** | `http://localhost:3001/log` |
|
|
12
12
|
| **Staging** | `https://logsdashboard.azify.dev/send` |
|
|
13
|
-
| **Production** | `https://
|
|
13
|
+
| **Production** | `https://cadence.aztech.host/send` |
|
|
14
14
|
|
|
15
15
|
### URLs para acessar os logs:
|
|
16
16
|
|
|
17
|
-
| Ambiente | URL
|
|
18
|
-
|
|
19
|
-
| **Development** |
|
|
17
|
+
| Ambiente | URL |
|
|
18
|
+
|----------|-----------------------------------|
|
|
19
|
+
| **Development** | `http://localhost:3002` |
|
|
20
20
|
| **Staging** | `https://logsdashboard.azify.dev` |
|
|
21
|
-
| **Production** | `https://
|
|
21
|
+
| **Production** | `https://cadence.aztech.host` |
|
|
22
22
|
|
|
23
23
|
## 📦 Instalação
|
|
24
24
|
|
|
@@ -49,16 +49,31 @@ APP_NAME=nome-app
|
|
|
49
49
|
require('azify-logger')
|
|
50
50
|
const express = require('express')
|
|
51
51
|
const app = express()
|
|
52
|
-
|
|
52
|
+
const azifyMiddleware = require('azify-logger/middleware-express')
|
|
53
|
+
app.use(azifyMiddleware())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Para aplicações Fastify:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const fastify = require('fastify')()
|
|
60
|
+
const azifyPlugin = require('azify-logger/middleware-fastify')
|
|
61
|
+
|
|
62
|
+
await fastify.register(azifyPlugin, {
|
|
63
|
+
serviceName: 'minha-app'
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
await fastify.listen({ port: 3000 })
|
|
53
67
|
```
|
|
54
68
|
|
|
55
69
|
## ⚙️ Variáveis de Ambiente
|
|
56
70
|
|
|
57
|
-
| Variável | Padrão
|
|
58
|
-
|
|
59
|
-
| `APP_NAME` | -
|
|
60
|
-
| `AZIFY_LOGGER_URL` | `http://localhost:3001/log`
|
|
61
|
-
| `
|
|
71
|
+
| Variável | Padrão | Descrição |
|
|
72
|
+
|----------|------------------------------------|-----------|
|
|
73
|
+
| `APP_NAME` | - | Nome da aplicação |
|
|
74
|
+
| `AZIFY_LOGGER_URL` | `http://localhost:3001/log` | URL do logger |
|
|
75
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318/v1/traces` | Endpoint OTLP para traces (opcional) |
|
|
76
|
+
| `NODE_ENV` | `development` | Ambiente |
|
|
62
77
|
|
|
63
78
|
## 🎯 O Que Você Ganha
|
|
64
79
|
|
|
@@ -67,6 +82,27 @@ const app = express()
|
|
|
67
82
|
- ✅ **Trace Consistente**: REQUEST e RESPONSE com mesmo traceId/spanId
|
|
68
83
|
- ✅ **Genérico**: Funciona com Bunyan, Pino, console.log ou qualquer logger
|
|
69
84
|
- ✅ **Centralizado**: Todos os logs no OpenSearch
|
|
85
|
+
- ✅ **Tracing Completo**: Traces exportados via OTLP para Grafana Tempo (opcional)
|
|
86
|
+
|
|
87
|
+
## 📡 OpenTelemetry Collector
|
|
88
|
+
|
|
89
|
+
O `azify-logger` inclui um OpenTelemetry Collector configurado para receber traces via OTLP e exportá-los para Grafana Tempo.
|
|
90
|
+
|
|
91
|
+
### Configuração
|
|
92
|
+
|
|
93
|
+
Para habilitar o envio de traces, configure a variável de ambiente na sua aplicação:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### URLs
|
|
100
|
+
|
|
101
|
+
| Ambiente | URL |
|
|
102
|
+
|----------|-----|
|
|
103
|
+
| **Development** | `http://localhost:4318/v1/traces` |
|
|
104
|
+
| **Staging** | `https://logsdashboard.azify.dev/v1/traces` |
|
|
105
|
+
| **Production** | `https://cadence.aztech.host/v1/traces` |
|
|
70
106
|
|
|
71
107
|
## 🔍 Como Funciona
|
|
72
108
|
|
|
@@ -192,11 +228,11 @@ O deploy é feito automaticamente via GitHub Actions.
|
|
|
192
228
|
|
|
193
229
|
**Comandos seguros** (preservam dados):
|
|
194
230
|
```bash
|
|
195
|
-
docker
|
|
196
|
-
docker
|
|
231
|
+
docker compose stop
|
|
232
|
+
docker compose restart
|
|
197
233
|
```
|
|
198
234
|
|
|
199
235
|
**Comandos destrutivos** (APAGAM logs):
|
|
200
236
|
```bash
|
|
201
|
-
docker
|
|
237
|
+
docker compose down -v # ⚠️ APAGA VOLUMES!
|
|
202
238
|
```
|
package/index.js
CHANGED
|
@@ -341,7 +341,8 @@ module.exports.streams = {
|
|
|
341
341
|
}
|
|
342
342
|
module.exports.middleware = {
|
|
343
343
|
restify: require('./middleware-restify'),
|
|
344
|
-
express: require('./middleware-express')
|
|
344
|
+
express: require('./middleware-express'),
|
|
345
|
+
fastify: require('./middleware-fastify')
|
|
345
346
|
}
|
|
346
347
|
module.exports.startLoggerWorker = require('./queue/workerManager').startLoggerWorker
|
|
347
348
|
module.exports.stopLoggerWorker = require('./queue/workerManager').stopLoggerWorker
|
package/middleware-express.js
CHANGED
|
@@ -1,8 +1,63 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { startRequestContext, runWithRequestContext, getRequestContext } = require('./store')
|
|
2
2
|
const { createHttpLoggerTransport } = require('./streams/httpQueue')
|
|
3
|
-
const {
|
|
3
|
+
const { Worker } = require('worker_threads')
|
|
4
|
+
const path = require('path')
|
|
4
5
|
const os = require('os')
|
|
5
6
|
|
|
7
|
+
// Importar OpenTelemetry para sincronizar contexto
|
|
8
|
+
let trace, otelContext
|
|
9
|
+
try {
|
|
10
|
+
const otelApi = require('@opentelemetry/api')
|
|
11
|
+
trace = otelApi.trace
|
|
12
|
+
otelContext = otelApi.context
|
|
13
|
+
} catch (_) {
|
|
14
|
+
trace = { getSpan: () => null }
|
|
15
|
+
otelContext = { active: () => ({}) }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function fastUUID() {
|
|
19
|
+
const timestamp = Date.now().toString(36)
|
|
20
|
+
const randomPart = Math.random().toString(36).substring(2, 15)
|
|
21
|
+
const randomPart2 = Math.random().toString(36).substring(2, 15)
|
|
22
|
+
return `${timestamp}-${randomPart}-${randomPart2}`.substring(0, 36)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sanitizeTraceHex(value) {
|
|
26
|
+
if (!value || typeof value !== 'string') return null
|
|
27
|
+
const hex = value.replace(/[^0-9a-fA-F]/g, '').toLowerCase()
|
|
28
|
+
if (!hex) return null
|
|
29
|
+
return hex.padEnd(32, '0').slice(0, 32)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function safeSerializeBody(payload) {
|
|
33
|
+
if (payload == null) return ''
|
|
34
|
+
if (typeof payload === 'string') return payload
|
|
35
|
+
if (Buffer.isBuffer(payload)) return payload.toString('utf8')
|
|
36
|
+
try {
|
|
37
|
+
return JSON.stringify(payload)
|
|
38
|
+
} catch (err) {
|
|
39
|
+
try {
|
|
40
|
+
return String(payload)
|
|
41
|
+
} catch {
|
|
42
|
+
return ''
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeChunkToBuffer(chunk) {
|
|
48
|
+
if (chunk == null) return null
|
|
49
|
+
if (Buffer.isBuffer(chunk)) return chunk
|
|
50
|
+
if (typeof chunk === 'string') return Buffer.from(chunk)
|
|
51
|
+
if (chunk && chunk.type === 'Buffer' && Array.isArray(chunk.data)) {
|
|
52
|
+
return Buffer.from(chunk.data)
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return Buffer.from(String(chunk))
|
|
56
|
+
} catch {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
6
61
|
const HEADER_WHITELIST = new Set([
|
|
7
62
|
'content-type',
|
|
8
63
|
'content-length',
|
|
@@ -52,27 +107,52 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
52
107
|
|
|
53
108
|
const hostname = os.hostname()
|
|
54
109
|
|
|
110
|
+
const serviceObj = config.serviceName ? { name: config.serviceName, version: '1.0.0' } : null
|
|
111
|
+
|
|
112
|
+
const workerPool = []
|
|
113
|
+
const cpuCount = Math.max(1, (os.cpus() || []).length || 1)
|
|
114
|
+
const WORKER_POOL_SIZE = Math.min(8, Math.max(2, cpuCount))
|
|
115
|
+
const maxWorkers = Math.min(WORKER_POOL_SIZE, cpuCount)
|
|
116
|
+
let workerIndex = 0
|
|
117
|
+
let poolInitialized = false
|
|
118
|
+
|
|
119
|
+
function initWorkerPool() {
|
|
120
|
+
try {
|
|
121
|
+
const workerPath = path.join(__dirname, 'utils', 'bodyWorker.js')
|
|
122
|
+
for (let i = 0; i < maxWorkers; i++) {
|
|
123
|
+
try {
|
|
124
|
+
const worker = new Worker(workerPath)
|
|
125
|
+
workerPool.push(worker)
|
|
126
|
+
} catch (err) {
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function ensureWorkerPool() {
|
|
135
|
+
if (!poolInitialized && (config.captureResponseBody || config.captureRequestBody)) {
|
|
136
|
+
poolInitialized = true
|
|
137
|
+
initWorkerPool()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function getWorker() {
|
|
142
|
+
if (workerPool.length === 0) return null
|
|
143
|
+
const worker = workerPool[workerIndex % workerPool.length]
|
|
144
|
+
workerIndex++
|
|
145
|
+
return worker
|
|
146
|
+
}
|
|
147
|
+
|
|
55
148
|
function sendLog(level, message, meta = {}) {
|
|
56
149
|
if (!transport || typeof transport.enqueue !== 'function') return
|
|
57
150
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
},
|
|
64
|
-
environment: config.environment,
|
|
65
|
-
timestamp: new Date().toISOString(),
|
|
66
|
-
hostname: hostname,
|
|
67
|
-
...meta
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
transport.enqueue({
|
|
71
|
-
level,
|
|
72
|
-
message,
|
|
73
|
-
meta: metaObj
|
|
74
|
-
}, { 'content-type': 'application/json' })
|
|
75
|
-
} catch (err) { }
|
|
151
|
+
transport.enqueue({
|
|
152
|
+
level,
|
|
153
|
+
message,
|
|
154
|
+
meta
|
|
155
|
+
}, { 'content-type': 'application/json' })
|
|
76
156
|
}
|
|
77
157
|
|
|
78
158
|
return function azifyExpressLoggingMiddleware(req, res, next) {
|
|
@@ -85,181 +165,211 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
85
165
|
let responseChunkCaptured = false
|
|
86
166
|
let logSent = false
|
|
87
167
|
|
|
88
|
-
let requestId, traceId, spanId, parentSpanId,
|
|
168
|
+
let requestId, traceId, spanId, parentSpanId, clientIp, query, cachedHeaders
|
|
89
169
|
let idsCreated = false
|
|
90
170
|
let headersCached = false
|
|
171
|
+
let reqCtx = null
|
|
172
|
+
|
|
173
|
+
// Função para extrair traceId/spanId do contexto OTEL ativo
|
|
174
|
+
function getOtelTraceContext() {
|
|
175
|
+
try {
|
|
176
|
+
const activeContext = otelContext.active()
|
|
177
|
+
const span = trace.getSpan(activeContext)
|
|
178
|
+
if (span) {
|
|
179
|
+
const spanContext = span.spanContext()
|
|
180
|
+
if (spanContext && spanContext.traceId && spanContext.spanId) {
|
|
181
|
+
// Converter traceId de hex para formato UUID
|
|
182
|
+
const traceHex = spanContext.traceId.replace(/-/g, '')
|
|
183
|
+
return {
|
|
184
|
+
traceId: traceHex.length === 32 ? `${traceHex.substring(0, 8)}-${traceHex.substring(8, 12)}-${traceHex.substring(12, 16)}-${traceHex.substring(16, 20)}-${traceHex.substring(20, 32)}` : spanContext.traceId,
|
|
185
|
+
spanId: spanContext.spanId,
|
|
186
|
+
parentSpanId: null // OTEL gerencia isso internamente
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch (_) {}
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Criar contexto inicial sincronizado com OTEL
|
|
195
|
+
function ensureRequestContext() {
|
|
196
|
+
if (reqCtx) return reqCtx
|
|
197
|
+
|
|
198
|
+
// Tentar pegar do OTEL primeiro
|
|
199
|
+
const otelCtx = getOtelTraceContext()
|
|
200
|
+
const traceHex = otelCtx ? otelCtx.traceId.replace(/-/g, '').substring(0, 32) : sanitizeTraceHex(req.headers['x-trace-id'])
|
|
201
|
+
|
|
202
|
+
reqCtx = startRequestContext({
|
|
203
|
+
requestId: req.requestId || fastUUID(),
|
|
204
|
+
traceHex: traceHex || undefined,
|
|
205
|
+
parentSpanId: otelCtx?.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// Se OTEL tem contexto, usar os IDs do OTEL
|
|
209
|
+
if (otelCtx) {
|
|
210
|
+
reqCtx.traceId = otelCtx.traceId
|
|
211
|
+
reqCtx.spanId = otelCtx.spanId
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return reqCtx
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Garantir que o contexto está disponível e sincronizado com OTEL ANTES de processar
|
|
218
|
+
const ctx = ensureRequestContext()
|
|
219
|
+
|
|
220
|
+
// Executar a requisição dentro do contexto AsyncLocalStorage
|
|
221
|
+
// Isso garante que todos os logs intermediários peguem o mesmo traceId
|
|
222
|
+
runWithRequestContext(ctx, () => {
|
|
223
|
+
next()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
const logWithContext = (level, message, meta) => {
|
|
227
|
+
// Sempre tentar pegar do OTEL primeiro (mais confiável)
|
|
228
|
+
const otelCtx = getOtelTraceContext()
|
|
229
|
+
const ctx = getRequestContext() || ensureRequestContext()
|
|
230
|
+
|
|
231
|
+
meta.traceId = otelCtx?.traceId || meta.traceId || ctx.traceId
|
|
232
|
+
meta.spanId = otelCtx?.spanId || meta.spanId || ctx.spanId
|
|
233
|
+
meta.parentSpanId = otelCtx?.parentSpanId || meta.parentSpanId || ctx.parentSpanId
|
|
234
|
+
sendLog(level, message, meta)
|
|
235
|
+
}
|
|
91
236
|
|
|
92
237
|
function ensureIds() {
|
|
93
238
|
if (idsCreated) return
|
|
94
239
|
idsCreated = true
|
|
95
240
|
|
|
96
|
-
query = req.query
|
|
241
|
+
query = req.query || null
|
|
97
242
|
|
|
98
|
-
const
|
|
99
|
-
const headerTraceId = req.headers['x-trace-id']
|
|
100
|
-
const headerParentSpanId = req.headers['x-parent-span-id']
|
|
243
|
+
const ctx = ensureRequestContext()
|
|
101
244
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
traceId =
|
|
245
|
+
// Priorizar OTEL se disponível
|
|
246
|
+
const otelCtx = getOtelTraceContext()
|
|
247
|
+
if (otelCtx) {
|
|
248
|
+
traceId = otelCtx.traceId
|
|
249
|
+
spanId = otelCtx.spanId
|
|
250
|
+
parentSpanId = otelCtx.parentSpanId
|
|
106
251
|
} else {
|
|
107
|
-
traceId =
|
|
252
|
+
traceId = ctx.traceId
|
|
253
|
+
spanId = ctx.spanId
|
|
254
|
+
parentSpanId = ctx.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
108
255
|
}
|
|
109
256
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (!req.requestId) {
|
|
113
|
-
const singleUUID = randomUUID()
|
|
114
|
-
requestId = singleUUID
|
|
115
|
-
spanId = singleUUID.replace(/-/g, '').substring(0, 16)
|
|
116
|
-
} else {
|
|
117
|
-
requestId = req.requestId
|
|
118
|
-
if (requestId.length >= 16) {
|
|
119
|
-
spanId = requestId.replace(/-/g, '').substring(0, 16)
|
|
120
|
-
} else if (traceId.length >= 16) {
|
|
121
|
-
spanId = traceId.replace(/-/g, '').substring(0, 16)
|
|
122
|
-
} else {
|
|
123
|
-
spanId = randomUUID().replace(/-/g, '').substring(0, 16)
|
|
124
|
-
}
|
|
125
|
-
}
|
|
257
|
+
requestId = ctx.requestId || req.requestId || fastUUID()
|
|
126
258
|
|
|
127
259
|
clientIp = req.ip || req.connection?.remoteAddress || req.socket?.remoteAddress || 'unknown'
|
|
128
|
-
|
|
129
|
-
reqCtx = startRequestContext({ requestId, traceId, spanId, parentSpanId })
|
|
260
|
+
req.requestId = requestId
|
|
130
261
|
}
|
|
131
262
|
|
|
132
263
|
function ensureHeaders() {
|
|
133
264
|
if (headersCached) return
|
|
134
265
|
headersCached = true
|
|
135
|
-
|
|
266
|
+
if (config.captureHeaders) {
|
|
267
|
+
cachedHeaders = pickHeaders(req.headers || {})
|
|
268
|
+
} else {
|
|
269
|
+
cachedHeaders = {}
|
|
270
|
+
}
|
|
136
271
|
}
|
|
137
272
|
|
|
273
|
+
function emitResponseLog(meta, chunk) {
|
|
274
|
+
if (!config.captureResponseBody || chunk == null) {
|
|
275
|
+
logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (!meta.response) meta.response = {}
|
|
280
|
+
meta.response.body = safeSerializeBody(chunk)
|
|
281
|
+
logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
282
|
+
}
|
|
283
|
+
|
|
138
284
|
if (config.logRequest) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
285
|
+
process.nextTick(() => {
|
|
286
|
+
ensureIds()
|
|
287
|
+
ensureHeaders()
|
|
288
|
+
|
|
289
|
+
const request = {
|
|
290
|
+
id: requestId,
|
|
291
|
+
method,
|
|
292
|
+
url,
|
|
293
|
+
path,
|
|
294
|
+
ip: clientIp
|
|
295
|
+
}
|
|
296
|
+
if (query) request.query = query
|
|
297
|
+
if (config.captureHeaders && cachedHeaders) request.headers = cachedHeaders
|
|
298
|
+
if (config.captureRequestBody && req.body !== undefined && req.body != null) {
|
|
299
|
+
request.body = safeSerializeBody(req.body)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const meta = {
|
|
303
|
+
traceId: reqCtx.traceId,
|
|
304
|
+
spanId: reqCtx.spanId,
|
|
305
|
+
parentSpanId: reqCtx.parentSpanId || null,
|
|
306
|
+
requestId,
|
|
307
|
+
request,
|
|
308
|
+
timestamp: Date.now(),
|
|
309
|
+
hostname
|
|
310
|
+
}
|
|
311
|
+
if (serviceObj) meta.service = serviceObj
|
|
312
|
+
if (config.environment) meta.environment = config.environment
|
|
313
|
+
|
|
314
|
+
logWithContext('info', `[REQUEST] ${method} ${url}`, meta)
|
|
163
315
|
})
|
|
164
316
|
}
|
|
165
|
-
|
|
317
|
+
|
|
166
318
|
const originalEnd = res.end.bind(res)
|
|
167
319
|
|
|
168
320
|
res.end = (chunk, encoding) => {
|
|
321
|
+
if (logSent) {
|
|
322
|
+
return originalEnd(chunk, encoding)
|
|
323
|
+
}
|
|
324
|
+
logSent = true
|
|
325
|
+
|
|
169
326
|
const result = originalEnd(chunk, encoding)
|
|
170
327
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
328
|
+
if (chunk != null && config.captureResponseBody && !responseChunkCaptured) {
|
|
329
|
+
responseChunk = chunk
|
|
330
|
+
responseChunkCaptured = true
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
process.nextTick(() => {
|
|
334
|
+
ensureIds()
|
|
174
335
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
},
|
|
209
|
-
response: response
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
sendLog('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
213
|
-
} catch (err) { }
|
|
336
|
+
const statusCode = res.statusCode || 200
|
|
337
|
+
const duration = Date.now() - startTime
|
|
338
|
+
const response = { statusCode, durationMs: duration }
|
|
339
|
+
|
|
340
|
+
if (config.captureHeaders) {
|
|
341
|
+
ensureHeaders()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const requestObj = {
|
|
345
|
+
id: requestId,
|
|
346
|
+
method,
|
|
347
|
+
url,
|
|
348
|
+
path,
|
|
349
|
+
ip: clientIp
|
|
350
|
+
}
|
|
351
|
+
if (query) requestObj.query = query
|
|
352
|
+
if (config.captureHeaders && cachedHeaders) requestObj.headers = cachedHeaders
|
|
353
|
+
|
|
354
|
+
const meta = {
|
|
355
|
+
traceId: reqCtx.traceId,
|
|
356
|
+
spanId: reqCtx.spanId,
|
|
357
|
+
parentSpanId: reqCtx.parentSpanId || null,
|
|
358
|
+
requestId,
|
|
359
|
+
request: requestObj,
|
|
360
|
+
response,
|
|
361
|
+
timestamp: Date.now(),
|
|
362
|
+
hostname
|
|
363
|
+
}
|
|
364
|
+
if (serviceObj) meta.service = serviceObj
|
|
365
|
+
if (config.environment) meta.environment = config.environment
|
|
366
|
+
|
|
367
|
+
const chunkToProcess = (responseChunk !== null && responseChunkCaptured) ? responseChunk : null
|
|
368
|
+
emitResponseLog(meta, chunkToProcess)
|
|
214
369
|
})
|
|
215
370
|
|
|
216
371
|
return result
|
|
217
372
|
}
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
next()
|
|
221
|
-
} catch (err) {
|
|
222
|
-
if (!logSent) {
|
|
223
|
-
logSent = true
|
|
224
|
-
ensureIds()
|
|
225
|
-
runWithRequestContext(reqCtx, () => {
|
|
226
|
-
process.stderr.write(`[azify-logger][middleware] Erro no next(): ${err?.message || String(err)}\n`)
|
|
227
|
-
setImmediate(() => {
|
|
228
|
-
try {
|
|
229
|
-
const statusCode = res.statusCode || 500
|
|
230
|
-
const duration = Date.now() - startTime
|
|
231
|
-
|
|
232
|
-
ensureHeaders()
|
|
233
|
-
|
|
234
|
-
const response = { statusCode, durationMs: duration }
|
|
235
|
-
const meta = {
|
|
236
|
-
traceId: reqCtx.traceId,
|
|
237
|
-
spanId: reqCtx.spanId,
|
|
238
|
-
parentSpanId: reqCtx.parentSpanId,
|
|
239
|
-
requestId: requestId,
|
|
240
|
-
request: {
|
|
241
|
-
id: requestId,
|
|
242
|
-
method,
|
|
243
|
-
url,
|
|
244
|
-
path,
|
|
245
|
-
query: query || {},
|
|
246
|
-
headers: cachedHeaders,
|
|
247
|
-
ip: clientIp
|
|
248
|
-
},
|
|
249
|
-
response: response,
|
|
250
|
-
error: {
|
|
251
|
-
message: err?.message || String(err),
|
|
252
|
-
name: err?.name || 'Error',
|
|
253
|
-
stack: err?.stack
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
sendLog('error', `[RESPONSE] ${method} ${url}`, meta)
|
|
257
|
-
} catch (logErr) { }
|
|
258
|
-
})
|
|
259
|
-
})
|
|
260
|
-
}
|
|
261
|
-
throw err
|
|
262
|
-
}
|
|
263
373
|
}
|
|
264
374
|
}
|
|
265
375
|
|