azify-logger 1.0.29 → 1.0.31
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 +144 -10
- package/middleware-express.js +73 -24
- package/middleware-fastify.js +142 -55
- package/middleware-restify.js +59 -10
- package/package.json +17 -4
- package/register-otel.js +110 -2
- package/register.js +214 -95
- package/scripts/redis-worker.js +37 -11
- package/server.js +132 -5
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
|
|
|
@@ -68,11 +68,12 @@ await fastify.listen({ port: 3000 })
|
|
|
68
68
|
|
|
69
69
|
## ⚙️ Variáveis de Ambiente
|
|
70
70
|
|
|
71
|
-
| Variável | Padrão
|
|
72
|
-
|
|
73
|
-
| `APP_NAME` | -
|
|
74
|
-
| `AZIFY_LOGGER_URL` | `http://localhost:3001/log`
|
|
75
|
-
| `
|
|
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 |
|
|
76
77
|
|
|
77
78
|
## 🎯 O Que Você Ganha
|
|
78
79
|
|
|
@@ -81,6 +82,27 @@ await fastify.listen({ port: 3000 })
|
|
|
81
82
|
- ✅ **Trace Consistente**: REQUEST e RESPONSE com mesmo traceId/spanId
|
|
82
83
|
- ✅ **Genérico**: Funciona com Bunyan, Pino, console.log ou qualquer logger
|
|
83
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` |
|
|
84
106
|
|
|
85
107
|
## 🔍 Como Funciona
|
|
86
108
|
|
|
@@ -183,6 +205,22 @@ curl -X POST http://localhost:3001/log \
|
|
|
183
205
|
|
|
184
206
|
Acesse `http://localhost:3002` e faça login com Azure AD.
|
|
185
207
|
|
|
208
|
+
### Apps fora da mesma máquina / rede (Azure)
|
|
209
|
+
|
|
210
|
+
Por padrão, o `azify-logger` **só aceita chamadas dos próprios serviços locais** (mesma VM / rede privada).
|
|
211
|
+
|
|
212
|
+
Se sua aplicação **não estiver rodando na mesma máquina/VM** do `azify-logger`:
|
|
213
|
+
|
|
214
|
+
- Descubra o **IP público** da aplicação cliente
|
|
215
|
+
- Envie esse IP para que ele seja incluído na configuração interna (`ALLOWED_SOURCE_IPS`)
|
|
216
|
+
|
|
217
|
+
Isso vale tanto para:
|
|
218
|
+
|
|
219
|
+
- Envio de logs (`/log` e `/send`)
|
|
220
|
+
- Envio de traces (`/v1/traces`)
|
|
221
|
+
|
|
222
|
+
Enquanto o IP não for incluído nessa lista, chamadas vindas de fora da mesma máquina/rede serão bloqueadas com `403 Forbidden`.
|
|
223
|
+
|
|
186
224
|
## 🚀 Deploy
|
|
187
225
|
|
|
188
226
|
### Staging - Deploy Automático
|
|
@@ -200,6 +238,102 @@ O deploy é feito automaticamente via GitHub Actions.
|
|
|
200
238
|
3. Configure: **Environment:** `production`
|
|
201
239
|
4. Clique em **Run workflow**
|
|
202
240
|
|
|
241
|
+
## 📦 Retenção e Arquivamento de Logs
|
|
242
|
+
|
|
243
|
+
> **⚠️ Importante**: A retenção é configurada **apenas no servidor `azify-logger`**. As aplicações que usam a lib **não precisam se preocupar** com retenção - elas apenas enviam logs normalmente.
|
|
244
|
+
|
|
245
|
+
O `azify-logger` inclui um sistema automático de retenção que:
|
|
246
|
+
|
|
247
|
+
- ✅ **Mantém sempre os últimos 30 dias** de logs disponíveis no OpenSearch para consulta imediata
|
|
248
|
+
- ✅ **Compacta e arquiva** logs mais antigos automaticamente
|
|
249
|
+
- ✅ **Envia para Azure Blob Storage** para armazenamento de longo prazo
|
|
250
|
+
- ✅ **Remove logs antigos** do OpenSearch após arquivamento bem-sucedido
|
|
251
|
+
|
|
252
|
+
### Como Funciona
|
|
253
|
+
|
|
254
|
+
1. **Agendamento**: O serviço de retenção executa automaticamente 1x por dia às 3h da manhã (configurável via `RETENTION_RUN_AT_HOUR`)
|
|
255
|
+
2. **Identificação**: Busca todos os logs com mais de 30 dias no OpenSearch
|
|
256
|
+
3. **Arquivamento**:
|
|
257
|
+
- Exporta logs mais antigos que 30 dias
|
|
258
|
+
- Compacta em formato ZIP
|
|
259
|
+
- Envia para Azure Blob Storage na estrutura `logs/{app}/{YYYYMM}/{DD}/`
|
|
260
|
+
- Remove do OpenSearch após confirmação de upload bem-sucedido
|
|
261
|
+
|
|
262
|
+
### Configuração (Apenas no Servidor azify-logger)
|
|
263
|
+
|
|
264
|
+
A configuração de retenção é feita **apenas no servidor `azify-logger`**, no arquivo `env/app.env`:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
RETENTION_DAYS=30 # Dias de logs mantidos no OpenSearch (padrão: 30)
|
|
268
|
+
RETENTION_RUN_AT_HOUR=3 # Horário de execução diária (0-23, padrão: 3h da manhã)
|
|
269
|
+
|
|
270
|
+
**⚠️ As aplicações que usam a lib `azify-logger` não precisam configurar nada relacionado à retenção.**
|
|
271
|
+
|
|
272
|
+
### Execução Manual
|
|
273
|
+
|
|
274
|
+
Para executar a retenção manualmente (útil para testes):
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Executar uma vez
|
|
278
|
+
npm run retention -- --once
|
|
279
|
+
|
|
280
|
+
# Ou via Docker
|
|
281
|
+
docker exec azify-retention-manager node scripts/retention-manager.js --once
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Estrutura no Blob Storage
|
|
285
|
+
|
|
286
|
+
Os arquivos são organizados da seguinte forma:
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
{container}/
|
|
290
|
+
└── logs/
|
|
291
|
+
└── {YYYYMM}/ (ex: 202512/)
|
|
292
|
+
└── log-YYYY-MM-DD-HH-MM-SS-UUID.zip
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Cada arquivo ZIP contém:
|
|
296
|
+
- `metadata.json`: Metadados do índice (nome, data de exportação, contagem de documentos, dias de retenção)
|
|
297
|
+
- `{indexName}-{timestamp}.json`: Todos os documentos exportados em JSON
|
|
298
|
+
|
|
299
|
+
O sistema sempre mantém **os últimos 30 dias** de logs disponíveis no OpenSearch/Grafana.
|
|
300
|
+
|
|
301
|
+
### Scripts Úteis
|
|
302
|
+
|
|
303
|
+
#### Consulta e Validação de Blob Storage
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Listar todos os arquivos no blob storage
|
|
307
|
+
npm run list:blob
|
|
308
|
+
|
|
309
|
+
# Listar arquivos ZIP recentes (última hora)
|
|
310
|
+
npm run list:blob:recent
|
|
311
|
+
|
|
312
|
+
# Baixar um arquivo ZIP específico
|
|
313
|
+
npm run download:blob "logs/harmony/202512/05/log-2025-12-05-14-29-01.zip" ~/Downloads
|
|
314
|
+
|
|
315
|
+
# Baixar todos os arquivos de uma pasta
|
|
316
|
+
npm run download:blob "logs/harmony/202512/05/*" ~/Downloads
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Reimportação de Logs
|
|
320
|
+
|
|
321
|
+
Para reimportar logs arquivados de volta para o OpenSearch (útil para análise de logs antigos):
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# Reimportar um arquivo ZIP específico
|
|
325
|
+
npm run reimport:logs ~/Downloads/log-2025-12-05-18-31-32.zip
|
|
326
|
+
|
|
327
|
+
# Reimportar todos os ZIPs de uma pasta
|
|
328
|
+
npm run reimport:logs ~/Downloads/arquivos-exportados/
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Importante:**
|
|
332
|
+
- Os logs serão reimportados no índice original
|
|
333
|
+
- Os timestamps originais são preservados
|
|
334
|
+
- A aplicação já deve estar configurada no Grafana (organização e datasource)
|
|
335
|
+
- Após a reimportação, os logs estarão disponíveis no Grafana imediatamente
|
|
336
|
+
|
|
203
337
|
## ⚠️ Preservação de Dados
|
|
204
338
|
|
|
205
339
|
**IMPORTANTE:** Logs do OpenSearch são armazenados em volume Docker persistente.
|
package/middleware-express.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
const { startRequestContext } = require('./store')
|
|
1
|
+
const { startRequestContext, runWithRequestContext, getRequestContext } = require('./store')
|
|
2
2
|
const { createHttpLoggerTransport } = require('./streams/httpQueue')
|
|
3
3
|
const { Worker } = require('worker_threads')
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const os = require('os')
|
|
6
6
|
|
|
7
|
+
let trace, otelContext
|
|
8
|
+
try {
|
|
9
|
+
const otelApi = require('@opentelemetry/api')
|
|
10
|
+
trace = otelApi.trace
|
|
11
|
+
otelContext = otelApi.context
|
|
12
|
+
} catch (_) {
|
|
13
|
+
trace = { getSpan: () => null }
|
|
14
|
+
otelContext = { active: () => ({}) }
|
|
15
|
+
}
|
|
16
|
+
|
|
7
17
|
function fastUUID() {
|
|
8
18
|
const timestamp = Date.now().toString(36)
|
|
9
19
|
const randomPart = Math.random().toString(36).substring(2, 15)
|
|
@@ -159,20 +169,58 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
159
169
|
let headersCached = false
|
|
160
170
|
let reqCtx = null
|
|
161
171
|
|
|
162
|
-
|
|
172
|
+
function getOtelTraceContext() {
|
|
173
|
+
try {
|
|
174
|
+
const activeContext = otelContext.active()
|
|
175
|
+
const span = trace.getSpan(activeContext)
|
|
176
|
+
if (span) {
|
|
177
|
+
const spanContext = span.spanContext()
|
|
178
|
+
if (spanContext && spanContext.traceId && spanContext.spanId) {
|
|
179
|
+
const traceHex = spanContext.traceId.replace(/-/g, '')
|
|
180
|
+
return {
|
|
181
|
+
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,
|
|
182
|
+
spanId: spanContext.spanId,
|
|
183
|
+
parentSpanId: null
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} catch (_) {}
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
163
190
|
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
191
|
+
function ensureRequestContext() {
|
|
192
|
+
if (reqCtx) return reqCtx
|
|
193
|
+
|
|
194
|
+
const otelCtx = getOtelTraceContext()
|
|
195
|
+
const traceHex = otelCtx ? otelCtx.traceId.replace(/-/g, '').substring(0, 32) : sanitizeTraceHex(req.headers['x-trace-id'])
|
|
196
|
+
|
|
197
|
+
reqCtx = startRequestContext({
|
|
198
|
+
requestId: req.requestId || fastUUID(),
|
|
199
|
+
traceHex: traceHex || undefined,
|
|
200
|
+
parentSpanId: otelCtx?.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
if (otelCtx) {
|
|
204
|
+
reqCtx.traceId = otelCtx.traceId
|
|
205
|
+
reqCtx.spanId = otelCtx.spanId
|
|
172
206
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
207
|
+
|
|
208
|
+
return reqCtx
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const ctx = ensureRequestContext()
|
|
212
|
+
|
|
213
|
+
runWithRequestContext(ctx, () => {
|
|
214
|
+
next()
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
const logWithContext = (level, message, meta) => {
|
|
218
|
+
const otelCtx = getOtelTraceContext()
|
|
219
|
+
const ctx = getRequestContext() || ensureRequestContext()
|
|
220
|
+
|
|
221
|
+
meta.traceId = otelCtx?.traceId || meta.traceId || ctx.traceId
|
|
222
|
+
meta.spanId = otelCtx?.spanId || meta.spanId || ctx.spanId
|
|
223
|
+
meta.parentSpanId = otelCtx?.parentSpanId || meta.parentSpanId || ctx.parentSpanId
|
|
176
224
|
sendLog(level, message, meta)
|
|
177
225
|
}
|
|
178
226
|
|
|
@@ -182,19 +230,20 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
182
230
|
|
|
183
231
|
query = req.query || null
|
|
184
232
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
233
|
+
const ctx = ensureRequestContext()
|
|
234
|
+
|
|
235
|
+
const otelCtx = getOtelTraceContext()
|
|
236
|
+
if (otelCtx) {
|
|
237
|
+
traceId = otelCtx.traceId
|
|
238
|
+
spanId = otelCtx.spanId
|
|
239
|
+
parentSpanId = otelCtx.parentSpanId
|
|
240
|
+
} else {
|
|
241
|
+
traceId = ctx.traceId
|
|
242
|
+
spanId = ctx.spanId
|
|
243
|
+
parentSpanId = ctx.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
192
244
|
}
|
|
193
245
|
|
|
194
|
-
requestId =
|
|
195
|
-
traceId = reqCtx.traceId
|
|
196
|
-
spanId = reqCtx.spanId
|
|
197
|
-
parentSpanId = reqCtx.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
246
|
+
requestId = ctx.requestId || req.requestId || fastUUID()
|
|
198
247
|
|
|
199
248
|
clientIp = req.ip || req.connection?.remoteAddress || req.socket?.remoteAddress || 'unknown'
|
|
200
249
|
req.requestId = requestId
|
package/middleware-fastify.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
const { startRequestContext } = require('./store')
|
|
1
|
+
const { startRequestContext, runWithRequestContext, getRequestContext } = require('./store')
|
|
2
2
|
const { createHttpLoggerTransport } = require('./streams/httpQueue')
|
|
3
3
|
const os = require('os')
|
|
4
4
|
|
|
5
|
+
let trace, otelContext
|
|
6
|
+
try {
|
|
7
|
+
const otelApi = require('@opentelemetry/api')
|
|
8
|
+
trace = otelApi.trace
|
|
9
|
+
otelContext = otelApi.context
|
|
10
|
+
} catch (_) {
|
|
11
|
+
trace = { getSpan: () => null }
|
|
12
|
+
otelContext = { active: () => ({}) }
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
function fastUUID() {
|
|
6
16
|
const timestamp = Date.now().toString(36)
|
|
7
17
|
const randomPart = Math.random().toString(36).substring(2, 15)
|
|
@@ -92,6 +102,50 @@ function createFastifyLoggingPlugin(options = {}) {
|
|
|
92
102
|
|
|
93
103
|
return async function azifyFastifyPlugin(fastify, opts) {
|
|
94
104
|
fastify.addHook('onRequest', async (request, reply) => {
|
|
105
|
+
let reqCtx = null
|
|
106
|
+
|
|
107
|
+
function getOtelTraceContext() {
|
|
108
|
+
try {
|
|
109
|
+
const activeContext = otelContext.active()
|
|
110
|
+
const span = trace.getSpan(activeContext)
|
|
111
|
+
if (span) {
|
|
112
|
+
const spanContext = span.spanContext()
|
|
113
|
+
if (spanContext && spanContext.traceId && spanContext.spanId) {
|
|
114
|
+
const traceHex = spanContext.traceId.replace(/-/g, '')
|
|
115
|
+
return {
|
|
116
|
+
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,
|
|
117
|
+
spanId: spanContext.spanId,
|
|
118
|
+
parentSpanId: null
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (_) {}
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function ensureRequestContext() {
|
|
127
|
+
if (reqCtx) return reqCtx
|
|
128
|
+
|
|
129
|
+
const otelCtx = getOtelTraceContext()
|
|
130
|
+
const traceHex = otelCtx ? otelCtx.traceId.replace(/-/g, '').substring(0, 32) : sanitizeTraceHex(request.headers['x-trace-id'])
|
|
131
|
+
|
|
132
|
+
reqCtx = startRequestContext({
|
|
133
|
+
requestId: request.requestId || fastUUID(),
|
|
134
|
+
traceHex: traceHex || undefined,
|
|
135
|
+
parentSpanId: otelCtx?.parentSpanId || request.headers['x-parent-span-id'] || null
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
if (otelCtx) {
|
|
139
|
+
reqCtx.traceId = otelCtx.traceId
|
|
140
|
+
reqCtx.spanId = otelCtx.spanId
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return reqCtx
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ctx = ensureRequestContext()
|
|
147
|
+
|
|
148
|
+
runWithRequestContext(ctx, () => {})
|
|
95
149
|
const startTime = Date.now()
|
|
96
150
|
const method = request.method
|
|
97
151
|
const url = request.url
|
|
@@ -104,20 +158,14 @@ function createFastifyLoggingPlugin(options = {}) {
|
|
|
104
158
|
let requestId, traceId, spanId, parentSpanId, clientIp, query, cachedHeaders
|
|
105
159
|
let idsCreated = false
|
|
106
160
|
let headersCached = false
|
|
107
|
-
let reqCtx = null
|
|
108
161
|
|
|
109
162
|
const logWithContext = (level, message, meta) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
meta.traceId = meta.traceId || reqCtx.traceId
|
|
119
|
-
meta.spanId = meta.spanId || reqCtx.spanId
|
|
120
|
-
meta.parentSpanId = meta.parentSpanId || reqCtx.parentSpanId
|
|
163
|
+
const otelCtx = getOtelTraceContext()
|
|
164
|
+
const ctx = getRequestContext() || ensureRequestContext()
|
|
165
|
+
|
|
166
|
+
meta.traceId = otelCtx?.traceId || meta.traceId || ctx.traceId
|
|
167
|
+
meta.spanId = otelCtx?.spanId || meta.spanId || ctx.spanId
|
|
168
|
+
meta.parentSpanId = otelCtx?.parentSpanId || meta.parentSpanId || ctx.parentSpanId
|
|
121
169
|
sendLog(level, message, meta)
|
|
122
170
|
}
|
|
123
171
|
|
|
@@ -127,20 +175,20 @@ function createFastifyLoggingPlugin(options = {}) {
|
|
|
127
175
|
|
|
128
176
|
query = request.query || null
|
|
129
177
|
|
|
130
|
-
|
|
131
|
-
const traceHex = sanitizeTraceHex(request.headers['x-trace-id'])
|
|
132
|
-
reqCtx = startRequestContext({
|
|
133
|
-
requestId: request.requestId || fastUUID(),
|
|
134
|
-
traceHex,
|
|
135
|
-
parentSpanId: request.headers['x-parent-span-id'] || null
|
|
136
|
-
})
|
|
137
|
-
}
|
|
178
|
+
const ctx = ensureRequestContext()
|
|
138
179
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
180
|
+
const otelCtx = getOtelTraceContext()
|
|
181
|
+
if (otelCtx) {
|
|
182
|
+
traceId = otelCtx.traceId
|
|
183
|
+
spanId = otelCtx.spanId
|
|
184
|
+
parentSpanId = otelCtx.parentSpanId
|
|
185
|
+
} else {
|
|
186
|
+
traceId = ctx.traceId
|
|
187
|
+
spanId = ctx.spanId
|
|
188
|
+
parentSpanId = ctx.parentSpanId || request.headers['x-parent-span-id'] || null
|
|
189
|
+
}
|
|
143
190
|
|
|
191
|
+
requestId = ctx.requestId || request.requestId || fastUUID()
|
|
144
192
|
clientIp = request.ip || request.socket?.remoteAddress || 'unknown'
|
|
145
193
|
request.requestId = requestId
|
|
146
194
|
}
|
|
@@ -204,10 +252,13 @@ function createFastifyLoggingPlugin(options = {}) {
|
|
|
204
252
|
requestObj.body = safeSerializeBody(request.body)
|
|
205
253
|
}
|
|
206
254
|
|
|
255
|
+
const otelCtx = getOtelTraceContext()
|
|
256
|
+
const ctx = getRequestContext() || ensureRequestContext()
|
|
257
|
+
|
|
207
258
|
const meta = {
|
|
208
|
-
traceId:
|
|
209
|
-
spanId:
|
|
210
|
-
parentSpanId:
|
|
259
|
+
traceId: otelCtx?.traceId || ctx.traceId,
|
|
260
|
+
spanId: otelCtx?.spanId || ctx.spanId,
|
|
261
|
+
parentSpanId: otelCtx?.parentSpanId || ctx.parentSpanId || null,
|
|
211
262
|
requestId,
|
|
212
263
|
request: requestObj,
|
|
213
264
|
timestamp: Date.now(),
|
|
@@ -240,20 +291,53 @@ function createFastifyLoggingPlugin(options = {}) {
|
|
|
240
291
|
let requestId, traceId, spanId, parentSpanId, clientIp, query, cachedHeaders
|
|
241
292
|
let idsCreated = false
|
|
242
293
|
let headersCached = false
|
|
243
|
-
let reqCtx = null
|
|
244
294
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
295
|
+
function getOtelTraceContext() {
|
|
296
|
+
try {
|
|
297
|
+
const activeContext = otelContext.active()
|
|
298
|
+
const span = trace.getSpan(activeContext)
|
|
299
|
+
if (span) {
|
|
300
|
+
const spanContext = span.spanContext()
|
|
301
|
+
if (spanContext && spanContext.traceId && spanContext.spanId) {
|
|
302
|
+
const traceHex = spanContext.traceId.replace(/-/g, '')
|
|
303
|
+
return {
|
|
304
|
+
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,
|
|
305
|
+
spanId: spanContext.spanId,
|
|
306
|
+
parentSpanId: null
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
} catch (_) {}
|
|
311
|
+
return null
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function ensureRequestContext() {
|
|
315
|
+
if (reqCtx) return reqCtx
|
|
316
|
+
|
|
317
|
+
const otelCtx = getOtelTraceContext()
|
|
318
|
+
const traceHex = otelCtx ? otelCtx.traceId.replace(/-/g, '').substring(0, 32) : sanitizeTraceHex(request.headers['x-trace-id'])
|
|
319
|
+
|
|
320
|
+
reqCtx = startRequestContext({
|
|
321
|
+
requestId: request.requestId || fastUUID(),
|
|
322
|
+
traceHex: traceHex || undefined,
|
|
323
|
+
parentSpanId: otelCtx?.parentSpanId || request.headers['x-parent-span-id'] || null
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
if (otelCtx) {
|
|
327
|
+
reqCtx.traceId = otelCtx.traceId
|
|
328
|
+
reqCtx.spanId = otelCtx.spanId
|
|
253
329
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
330
|
+
|
|
331
|
+
return reqCtx
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const logWithContext = (level, message, meta) => {
|
|
335
|
+
const otelCtx = getOtelTraceContext()
|
|
336
|
+
const ctx = getRequestContext() || ensureRequestContext()
|
|
337
|
+
|
|
338
|
+
meta.traceId = otelCtx?.traceId || meta.traceId || ctx.traceId
|
|
339
|
+
meta.spanId = otelCtx?.spanId || meta.spanId || ctx.spanId
|
|
340
|
+
meta.parentSpanId = otelCtx?.parentSpanId || meta.parentSpanId || ctx.parentSpanId
|
|
257
341
|
sendLog(level, message, meta)
|
|
258
342
|
}
|
|
259
343
|
|
|
@@ -263,20 +347,20 @@ function createFastifyLoggingPlugin(options = {}) {
|
|
|
263
347
|
|
|
264
348
|
query = request.query || null
|
|
265
349
|
|
|
266
|
-
|
|
267
|
-
const traceHex = sanitizeTraceHex(request.headers['x-trace-id'])
|
|
268
|
-
reqCtx = startRequestContext({
|
|
269
|
-
requestId: request.requestId || fastUUID(),
|
|
270
|
-
traceHex,
|
|
271
|
-
parentSpanId: request.headers['x-parent-span-id'] || null
|
|
272
|
-
})
|
|
273
|
-
}
|
|
350
|
+
const ctx = ensureRequestContext()
|
|
274
351
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
352
|
+
const otelCtx = getOtelTraceContext()
|
|
353
|
+
if (otelCtx) {
|
|
354
|
+
traceId = otelCtx.traceId
|
|
355
|
+
spanId = otelCtx.spanId
|
|
356
|
+
parentSpanId = otelCtx.parentSpanId
|
|
357
|
+
} else {
|
|
358
|
+
traceId = ctx.traceId
|
|
359
|
+
spanId = ctx.spanId
|
|
360
|
+
parentSpanId = ctx.parentSpanId || request.headers['x-parent-span-id'] || null
|
|
361
|
+
}
|
|
279
362
|
|
|
363
|
+
requestId = ctx.requestId || request.requestId || fastUUID()
|
|
280
364
|
clientIp = request.ip || request.socket?.remoteAddress || 'unknown'
|
|
281
365
|
}
|
|
282
366
|
|
|
@@ -322,10 +406,13 @@ function createFastifyLoggingPlugin(options = {}) {
|
|
|
322
406
|
if (query) requestObj.query = query
|
|
323
407
|
if (config.captureHeaders && cachedHeaders) requestObj.headers = cachedHeaders
|
|
324
408
|
|
|
409
|
+
const otelCtx = getOtelTraceContext()
|
|
410
|
+
const ctx = getRequestContext() || ensureRequestContext()
|
|
411
|
+
|
|
325
412
|
const meta = {
|
|
326
|
-
traceId:
|
|
327
|
-
spanId:
|
|
328
|
-
parentSpanId:
|
|
413
|
+
traceId: otelCtx?.traceId || ctx.traceId,
|
|
414
|
+
spanId: otelCtx?.spanId || ctx.spanId,
|
|
415
|
+
parentSpanId: otelCtx?.parentSpanId || ctx.parentSpanId || null,
|
|
329
416
|
requestId,
|
|
330
417
|
request: requestObj,
|
|
331
418
|
response,
|
package/middleware-restify.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
const { startRequestContext } = require('./store')
|
|
1
|
+
const { startRequestContext, runWithRequestContext, getRequestContext } = require('./store')
|
|
2
2
|
const { createHttpLoggerTransport } = require('./streams/httpQueue')
|
|
3
3
|
const os = require('os')
|
|
4
4
|
|
|
5
|
+
let trace, otelContext
|
|
6
|
+
try {
|
|
7
|
+
const otelApi = require('@opentelemetry/api')
|
|
8
|
+
trace = otelApi.trace
|
|
9
|
+
otelContext = otelApi.context
|
|
10
|
+
} catch (_) {
|
|
11
|
+
trace = { getSpan: () => null }
|
|
12
|
+
otelContext = { active: () => ({}) }
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
function fastUUID() {
|
|
6
16
|
const timestamp = Date.now().toString(36)
|
|
7
17
|
const randomPart = Math.random().toString(36).substring(2, 15)
|
|
@@ -78,18 +88,54 @@ function createRestifyLoggingMiddleware (options = {}) {
|
|
|
78
88
|
|
|
79
89
|
return function azifyLoggingMiddleware (req, res, next) {
|
|
80
90
|
const startTime = Date.now()
|
|
81
|
-
const requestId = req.requestId || fastUUID()
|
|
82
91
|
|
|
83
92
|
if (res._azifySetup) {
|
|
84
93
|
return next()
|
|
85
94
|
}
|
|
86
95
|
res._azifySetup = true
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
function getOtelTraceContext() {
|
|
98
|
+
try {
|
|
99
|
+
const activeContext = otelContext.active()
|
|
100
|
+
const span = trace.getSpan(activeContext)
|
|
101
|
+
if (span) {
|
|
102
|
+
const spanContext = span.spanContext()
|
|
103
|
+
if (spanContext && spanContext.traceId && spanContext.spanId) {
|
|
104
|
+
const traceHex = spanContext.traceId.replace(/-/g, '')
|
|
105
|
+
return {
|
|
106
|
+
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,
|
|
107
|
+
spanId: spanContext.spanId,
|
|
108
|
+
parentSpanId: null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch (_) {}
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function ensureRequestContext() {
|
|
117
|
+
const otelCtx = getOtelTraceContext()
|
|
118
|
+
const traceHex = otelCtx ? otelCtx.traceId.replace(/-/g, '').substring(0, 32) : sanitizeTraceHex(req.headers['x-trace-id'])
|
|
119
|
+
|
|
120
|
+
const reqCtx = startRequestContext({
|
|
121
|
+
requestId: req.requestId || fastUUID(),
|
|
122
|
+
traceHex: traceHex || undefined,
|
|
123
|
+
parentSpanId: otelCtx?.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (otelCtx) {
|
|
127
|
+
reqCtx.traceId = otelCtx.traceId
|
|
128
|
+
reqCtx.spanId = otelCtx.spanId
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return reqCtx
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const reqCtx = ensureRequestContext()
|
|
135
|
+
const requestId = reqCtx.requestId
|
|
136
|
+
|
|
137
|
+
runWithRequestContext(reqCtx, () => {
|
|
138
|
+
next()
|
|
93
139
|
})
|
|
94
140
|
|
|
95
141
|
let normalizedPath = req.url
|
|
@@ -143,11 +189,14 @@ function createRestifyLoggingMiddleware (options = {}) {
|
|
|
143
189
|
const duration = Date.now() - startTime
|
|
144
190
|
const statusCode = responseStatus || res.statusCode || 200
|
|
145
191
|
const responseHeaders = typeof res.getHeaders === 'function' ? res.getHeaders() : {}
|
|
192
|
+
|
|
193
|
+
const otelCtx = getOtelTraceContext()
|
|
194
|
+
const ctx = getRequestContext() || reqCtx
|
|
146
195
|
|
|
147
196
|
const meta = {
|
|
148
|
-
traceId:
|
|
149
|
-
spanId:
|
|
150
|
-
parentSpanId:
|
|
197
|
+
traceId: otelCtx?.traceId || ctx.traceId,
|
|
198
|
+
spanId: otelCtx?.spanId || ctx.spanId,
|
|
199
|
+
parentSpanId: otelCtx?.parentSpanId || ctx.parentSpanId || null,
|
|
151
200
|
requestId,
|
|
152
201
|
request: requestSnapshot,
|
|
153
202
|
response: {
|