azify-logger 1.0.24 → 1.0.26
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 +86 -241
- package/package.json +30 -8
- package/register.js +149 -0
- package/server.js +756 -61
package/README.md
CHANGED
|
@@ -6,36 +6,22 @@ Sistema de logging centralizado com OpenTelemetry e OpenSearch para múltiplas a
|
|
|
6
6
|
|
|
7
7
|
### URLs para configurar nas aplicações:
|
|
8
8
|
|
|
9
|
-
| Ambiente | URL
|
|
10
|
-
|
|
11
|
-
| **Development** | `http://localhost:3001/log`
|
|
12
|
-
| **Staging** | `https://logsdashboard.azify.dev/send`
|
|
13
|
-
| **Production** | `https://logsdashboard.azify.prd/send` (a configurar)
|
|
9
|
+
| Ambiente | URL |
|
|
10
|
+
|----------|-----|
|
|
11
|
+
| **Development** | `http://localhost:3001/log` |
|
|
12
|
+
| **Staging** | `https://logsdashboard.azify.dev/send` |
|
|
13
|
+
| **Production** | `https://logsdashboard.azify.prd/send` | (a configurar)
|
|
14
14
|
|
|
15
|
+
### URLs para acessar os logs:
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
|
19
|
-
|
|
20
|
-
| **
|
|
21
|
-
| **Staging** | `https://logsdashboard.azify.dev` |
|
|
22
|
-
| **Production** | `https://logsdashboard.azify.com` (a configurar) |
|
|
23
|
-
|
|
17
|
+
| Ambiente | URL |
|
|
18
|
+
|----------|-----|
|
|
19
|
+
| **Development** | Grafana: `http://localhost:3002` |
|
|
20
|
+
| **Staging** | `https://logsdashboard.azify.dev` |
|
|
21
|
+
| **Production** | `https://logsdashboard.azify.com` |
|
|
24
22
|
|
|
25
23
|
## 📦 Instalação
|
|
26
24
|
|
|
27
|
-
Na sua aplicação, adicione ao `package.json`:
|
|
28
|
-
|
|
29
|
-
```json
|
|
30
|
-
{
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"azify-logger": "latest"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Ou via npm:
|
|
38
|
-
|
|
39
25
|
```bash
|
|
40
26
|
npm install azify-logger
|
|
41
27
|
```
|
|
@@ -44,91 +30,36 @@ npm install azify-logger
|
|
|
44
30
|
|
|
45
31
|
### Para aplicações Restify:
|
|
46
32
|
|
|
47
|
-
**1. No arquivo de inicialização:**
|
|
48
33
|
```javascript
|
|
49
|
-
|
|
34
|
+
// 1. No arquivo de inicialização
|
|
50
35
|
require('azify-logger/register-otel');
|
|
51
|
-
// Resto do código...
|
|
52
|
-
```
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
```javascript
|
|
37
|
+
// 2. No servidor
|
|
56
38
|
import { middleware as azifyMiddleware } from 'azify-logger'
|
|
57
|
-
|
|
58
39
|
const server = restify.createServer()
|
|
59
40
|
server.use(azifyMiddleware.restify())
|
|
60
|
-
```
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
```bash
|
|
42
|
+
// 3. Variável de ambiente
|
|
64
43
|
APP_NAME=nome-app
|
|
65
44
|
```
|
|
66
45
|
|
|
67
|
-
**PRONTO! 🎉**
|
|
68
|
-
|
|
69
46
|
### Para aplicações Express:
|
|
70
47
|
|
|
71
48
|
```javascript
|
|
72
49
|
require('azify-logger')
|
|
73
|
-
|
|
74
50
|
const express = require('express')
|
|
75
51
|
const app = express()
|
|
76
52
|
// Logs automáticos via OpenTelemetry
|
|
77
53
|
```
|
|
78
54
|
|
|
79
|
-
### ❌ O que NÃO precisa:
|
|
80
|
-
- ❌ Instalar dependências OpenTelemetry separadamente
|
|
81
|
-
- ❌ Configurar tracing manualmente
|
|
82
|
-
|
|
83
55
|
## ⚙️ Variáveis de Ambiente
|
|
84
56
|
|
|
85
|
-
| Variável | Padrão
|
|
86
|
-
|
|
87
|
-
| `APP_NAME` | -
|
|
88
|
-
| `AZIFY_LOGGER_URL` | `http://localhost:3001/log`
|
|
57
|
+
| Variável | Padrão | Descrição |
|
|
58
|
+
|----------|-------|-----------|
|
|
59
|
+
| `APP_NAME` | - | Nome da aplicação |
|
|
60
|
+
| `AZIFY_LOGGER_URL` | `http://localhost:3001/log` | URL do logger |
|
|
89
61
|
| `NODE_ENV` | `development` | Ambiente |
|
|
90
62
|
|
|
91
|
-
### 🌐 URLs por Ambiente
|
|
92
|
-
|
|
93
|
-
**Desenvolvimento (Local):**
|
|
94
|
-
```env
|
|
95
|
-
APP_NAME=minha-app
|
|
96
|
-
AZIFY_LOGGER_URL=http://localhost:3001/log
|
|
97
|
-
NODE_ENV=development
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Staging:**
|
|
101
|
-
```env
|
|
102
|
-
APP_NAME=minha-app
|
|
103
|
-
AZIFY_LOGGER_URL=https://logsdashboard.azify.dev/send
|
|
104
|
-
NODE_ENV=staging
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
**Production:**
|
|
108
|
-
```env
|
|
109
|
-
APP_NAME=minha-app
|
|
110
|
-
AZIFY_LOGGER_URL=https://logsdashboard.azify.prd/send (a conigurar)
|
|
111
|
-
NODE_ENV=production
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### 🐳 Docker Compose (mesma network)
|
|
115
|
-
|
|
116
|
-
Apenas se estiver usando Docker Compose com rede compartilhada:
|
|
117
|
-
|
|
118
|
-
```env
|
|
119
|
-
AZIFY_LOGGER_URL=http://azify-logger:3001/log
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Docker e Node antigos
|
|
123
|
-
|
|
124
|
-
Se seu container usa uma versão antiga do Node e você ver erros de inicialização do OpenTelemetry (ex.: `Cannot find module 'node:events'` saindo de `google-logging-utils` ou `@opentelemetry/resource-detector-gcp`), defina no container:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
AZIFY_LOGGER_AUTOREG_DISABLE=1
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Isso evita carregar `register-otel.js` e mantém os envios de log via streams/middleware normalmente, permitindo visualização no OpenSearch.
|
|
131
|
-
|
|
132
63
|
## 🎯 O Que Você Ganha
|
|
133
64
|
|
|
134
65
|
- ✅ **Zero Config**: OpenTelemetry habilitado automaticamente
|
|
@@ -137,89 +68,61 @@ Isso evita carregar `register-otel.js` e mantém os envios de log via streams/mi
|
|
|
137
68
|
- ✅ **Genérico**: Funciona com Bunyan, Pino, console.log ou qualquer logger
|
|
138
69
|
- ✅ **Centralizado**: Todos os logs no OpenSearch
|
|
139
70
|
|
|
140
|
-
##
|
|
71
|
+
## 🔍 Como Funciona
|
|
141
72
|
|
|
142
|
-
|
|
73
|
+
1. **OpenTelemetry** cria um span para cada request HTTP
|
|
74
|
+
2. **Middleware** captura request e response com o **mesmo span ativo**
|
|
75
|
+
3. **Logger** envia ambos os logs com **traceId e spanId iguais**
|
|
76
|
+
4. **OpenSearch** indexa e permite buscar por traceId
|
|
143
77
|
|
|
144
|
-
|
|
78
|
+
## 📖 Arquitetura
|
|
145
79
|
|
|
146
|
-
```javascript
|
|
147
|
-
// Forma segura com try/catch (não quebra se não tiver instalado)
|
|
148
|
-
let createAzifyBunyanStream
|
|
149
|
-
try {
|
|
150
|
-
createAzifyBunyanStream = require('azify-logger/streams/bunyan')
|
|
151
|
-
} catch (_) {
|
|
152
|
-
createAzifyBunyanStream = null
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const bunyan = require('bunyan')
|
|
156
|
-
const streams = [
|
|
157
|
-
{ level: 'info', stream: process.stdout }
|
|
158
|
-
]
|
|
159
|
-
|
|
160
|
-
// Adiciona stream do azify-logger se disponível
|
|
161
|
-
if (createAzifyBunyanStream) {
|
|
162
|
-
streams.push({
|
|
163
|
-
level: 'info',
|
|
164
|
-
type: 'raw',
|
|
165
|
-
stream: createAzifyBunyanStream({
|
|
166
|
-
loggerUrl: process.env.AZIFY_LOGGER_URL || 'http://localhost:3001/log',
|
|
167
|
-
serviceName: process.env.APP_NAME || 'app'
|
|
168
|
-
loggerUrl: process.env.AZIFY_LOGGER_URL,
|
|
169
|
-
serviceName: process.env.APP_NAME
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const logger = bunyan.createLogger({ name: 'app', streams })
|
|
175
|
-
logger.info('Teste') // Vai para stdout E para azify-logger
|
|
176
80
|
```
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
81
|
+
┌─────────────┐
|
|
82
|
+
│ aplicação │
|
|
83
|
+
└──────┬──────┘
|
|
84
|
+
│ HTTP POST
|
|
85
|
+
↓
|
|
86
|
+
┌─────────────┐
|
|
87
|
+
│ azify-logger│ (recebe logs)
|
|
88
|
+
└──────┬──────┘
|
|
89
|
+
│
|
|
90
|
+
↓
|
|
91
|
+
┌─────────────┐
|
|
92
|
+
│ OpenSearch │ (armazena em logs-{appName})
|
|
93
|
+
└──────┬──────┘
|
|
94
|
+
│
|
|
95
|
+
↓
|
|
96
|
+
┌─────────────┐
|
|
97
|
+
│ Grafana │ (visualiza - Organizations por app)
|
|
98
|
+
└─────────────┘
|
|
187
99
|
```
|
|
188
100
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
```javascript
|
|
192
|
-
const { streams } = require('azify-logger')
|
|
193
|
-
const pino = require('pino')
|
|
101
|
+
## 📈 Grafana - Visualização de Logs
|
|
194
102
|
|
|
195
|
-
|
|
196
|
-
loggerUrl: 'http://localhost:3001/log',
|
|
197
|
-
serviceName: 'nome-app'
|
|
198
|
-
}))
|
|
103
|
+
O azify-logger usa Grafana para visualização dos logs com controle de acesso por aplicação.
|
|
199
104
|
|
|
200
|
-
|
|
201
|
-
```
|
|
105
|
+
### Funcionalidades
|
|
202
106
|
|
|
203
|
-
|
|
107
|
+
- **📊 Explore**: Busca e visualização de logs em tempo real
|
|
108
|
+
- **📈 Dashboards**: Dashboards automáticos com métricas (Total de Requisições, Taxa de Sucesso, Erros, Tempo Médio de Resposta, etc.)
|
|
204
109
|
|
|
205
|
-
|
|
206
|
-
const { createAzifyLogger } = require('azify-logger')
|
|
110
|
+
### Controle de Acesso Automático
|
|
207
111
|
|
|
208
|
-
|
|
209
|
-
serviceName: 'nome-app',
|
|
210
|
-
loggerUrl: 'http://localhost:3001/log'
|
|
211
|
-
})
|
|
112
|
+
Quando uma aplicação envia logs pela primeira vez, o sistema automaticamente:
|
|
212
113
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
114
|
+
1. ✅ Cria uma **Organization** no Grafana com o nome da aplicação
|
|
115
|
+
2. ✅ Cria um **Datasource** filtrado para o índice `logs-{appName}`
|
|
116
|
+
3. ✅ Cria um **Dashboard** completo com métricas
|
|
117
|
+
4. ✅ Isola completamente os logs da aplicação
|
|
216
118
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
119
|
+
**Gerenciar acesso:**
|
|
120
|
+
- Acesse Grafana como Server Admin
|
|
121
|
+
- Vá em **Administration** → **Organizations**
|
|
122
|
+
- Adicione usuários à Organization da app
|
|
123
|
+
- **⚠️ Importante**: Use role **Editor** ou **Admin** para acesso ao Explorer (onde estão os logs). Role **Viewer** não tem acesso ao Explorer.
|
|
222
124
|
|
|
125
|
+
## 🛠️ Setup Local
|
|
223
126
|
|
|
224
127
|
```bash
|
|
225
128
|
./start-docker.sh
|
|
@@ -230,12 +133,22 @@ Aguarde alguns minutos. Você verá:
|
|
|
230
133
|
```
|
|
231
134
|
✅ Tudo pronto!
|
|
232
135
|
📊 OpenSearch: http://localhost:9200
|
|
233
|
-
|
|
136
|
+
📈 Grafana: http://localhost:3002
|
|
234
137
|
📝 Logger API: http://localhost:3001/log
|
|
235
|
-
🔭 OTEL Collector: http://localhost:4318
|
|
236
138
|
```
|
|
237
139
|
|
|
238
|
-
###
|
|
140
|
+
### Configuração de Ambiente
|
|
141
|
+
|
|
142
|
+
Copie os arquivos de exemplo e preencha com seus valores:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
cp env/app.env.example env/app.env
|
|
146
|
+
cp env/grafana.env.example env/grafana.env
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Edite `env/app.env` e `env/grafana.env` com suas configurações do Azure AD e outras variáveis.
|
|
150
|
+
|
|
151
|
+
### Testar
|
|
239
152
|
|
|
240
153
|
```bash
|
|
241
154
|
curl -X POST http://localhost:3001/log \
|
|
@@ -243,97 +156,30 @@ curl -X POST http://localhost:3001/log \
|
|
|
243
156
|
-d '{
|
|
244
157
|
"level": "info",
|
|
245
158
|
"message": "Teste",
|
|
246
|
-
"meta": {"service": {"name": "
|
|
159
|
+
"meta": {"service": {"name": "minha-app"}}
|
|
247
160
|
}'
|
|
248
161
|
```
|
|
249
162
|
|
|
250
|
-
|
|
163
|
+
Acesse `http://localhost:3002` e faça login com Azure AD.
|
|
251
164
|
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
http://localhost:5601
|
|
255
|
-
```
|
|
165
|
+
## 🚀 Deploy
|
|
256
166
|
|
|
257
|
-
|
|
258
|
-
Acesse: http://localhost:5601
|
|
259
|
-
|
|
260
|
-
## 📊 Estrutura dos Logs
|
|
261
|
-
|
|
262
|
-
Cada log no OpenSearch contém:
|
|
263
|
-
|
|
264
|
-
```json
|
|
265
|
-
{
|
|
266
|
-
"timestamp": "2025-10-01T10:15:30.123Z",
|
|
267
|
-
"level": "info",
|
|
268
|
-
"message": "[REQUEST] POST /api/payment",
|
|
269
|
-
"service": {
|
|
270
|
-
"name": "nome-app",
|
|
271
|
-
"version": "1.0.0"
|
|
272
|
-
},
|
|
273
|
-
"traceId": "abc123...",
|
|
274
|
-
"spanId": "xyz789...",
|
|
275
|
-
"method": "POST",
|
|
276
|
-
"url": "/api/payment",
|
|
277
|
-
"requestBody": { ... },
|
|
278
|
-
"statusCode": 200,
|
|
279
|
-
"responseTime": 123.45,
|
|
280
|
-
"responseBody": { ... },
|
|
281
|
-
"environment": "production",
|
|
282
|
-
"hostname": "app-server-01"
|
|
283
|
-
}
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
## 🔍 Como Funciona
|
|
167
|
+
### Staging - Deploy Automático
|
|
287
168
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
3. **Logger** envia ambos os logs com **traceId e spanId iguais**
|
|
291
|
-
4. **OpenSearch** indexa e permite buscar por traceId
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## 📖 Arquitetura
|
|
296
|
-
|
|
297
|
-
```
|
|
298
|
-
┌─────────────┐
|
|
299
|
-
│ aplicação │
|
|
300
|
-
└──────┬──────┘
|
|
301
|
-
│ HTTP POST
|
|
302
|
-
↓
|
|
303
|
-
┌─────────────┐
|
|
304
|
-
│ azify-logger│ (recebe logs)
|
|
305
|
-
└──────┬──────┘
|
|
306
|
-
│
|
|
307
|
-
↓
|
|
308
|
-
┌─────────────┐
|
|
309
|
-
│ OpenSearch │ (armazena)
|
|
310
|
-
└──────┬──────┘
|
|
311
|
-
│
|
|
312
|
-
↓
|
|
313
|
-
┌─────────────┐
|
|
314
|
-
│ Dashboards |
|
|
315
|
-
| OpenSearch │ (visualiza)
|
|
316
|
-
└─────────────┘
|
|
169
|
+
```bash
|
|
170
|
+
git push origin master
|
|
317
171
|
```
|
|
318
172
|
|
|
319
|
-
|
|
173
|
+
O deploy é feito automaticamente via GitHub Actions.
|
|
320
174
|
|
|
321
|
-
|
|
175
|
+
### Production - Deploy Manual
|
|
322
176
|
|
|
323
|
-
|
|
177
|
+
1. Acesse: **GitHub** → **Actions** → **Deploy**
|
|
178
|
+
2. Clique em **Run workflow**
|
|
179
|
+
3. Configure: **Environment:** `production`
|
|
180
|
+
4. Clique em **Run workflow**
|
|
324
181
|
|
|
325
|
-
|
|
326
|
-
- Filtros por serviço, app, traceId, statusCode
|
|
327
|
-
- Busca em tempo real
|
|
328
|
-
- Visualização detalhada de cada log
|
|
329
|
-
|
|
330
|
-
2. **📈 Dashboard** - "Application Health Dashboard"
|
|
331
|
-
- **Taxa de Sucesso** - Percentual de requisições 2xx
|
|
332
|
-
- **Latência Média** - Tempo médio de resposta
|
|
333
|
-
- **Erros 4xx/5xx** - Contador de erros
|
|
334
|
-
- **Requisições por Minuto** - Gráfico temporal
|
|
335
|
-
|
|
336
|
-
### ⚠️ Preservação de Dados
|
|
182
|
+
## ⚠️ Preservação de Dados
|
|
337
183
|
|
|
338
184
|
**IMPORTANTE:** Logs do OpenSearch são armazenados em volume Docker persistente.
|
|
339
185
|
|
|
@@ -341,10 +187,9 @@ O azify-logger usa OpenSearch Dashboards para visualização dos logs:
|
|
|
341
187
|
```bash
|
|
342
188
|
docker-compose stop
|
|
343
189
|
docker-compose restart
|
|
344
|
-
pm2 stop azify-logger
|
|
345
190
|
```
|
|
346
191
|
|
|
347
192
|
**Comandos destrutivos** (APAGAM logs):
|
|
348
193
|
```bash
|
|
349
194
|
docker-compose down -v # ⚠️ APAGA VOLUMES!
|
|
350
|
-
```
|
|
195
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azify-logger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "Azify Logger Client - Centralized logging for OpenSearch",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -30,17 +30,39 @@
|
|
|
30
30
|
"axios": "^1.6.0",
|
|
31
31
|
"cors": "^2.8.5",
|
|
32
32
|
"express": "^4.18.2",
|
|
33
|
+
"express-session": "^1.17.3",
|
|
34
|
+
"passport": "^0.6.0",
|
|
35
|
+
"passport-azure-ad": "^4.3.5",
|
|
36
|
+
"js-yaml": "^4.1.0",
|
|
33
37
|
"require-in-the-middle": "^7.4.0",
|
|
34
38
|
"uuid": "^9.0.1"
|
|
35
39
|
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@opentelemetry/api": ">=1.1.0 <1.6.0",
|
|
42
|
+
"@opentelemetry/core": ">=1.1.0 <1.6.0",
|
|
43
|
+
"@opentelemetry/resources": ">=1.1.0 <1.6.0",
|
|
44
|
+
"@opentelemetry/semantic-conventions": ">=1.1.0 <1.6.0",
|
|
45
|
+
"@opentelemetry/sdk-node": ">=0.39.1 <0.42.0",
|
|
46
|
+
"@opentelemetry/exporter-trace-otlp-http": ">=0.39.1 <0.42.0",
|
|
47
|
+
"@opentelemetry/auto-instrumentations-node": ">=0.39.1 <0.42.0"
|
|
48
|
+
},
|
|
36
49
|
"optionalDependencies": {
|
|
37
|
-
"@opentelemetry/api": "
|
|
38
|
-
"@opentelemetry/
|
|
39
|
-
"@opentelemetry/
|
|
40
|
-
"@opentelemetry/
|
|
41
|
-
"@opentelemetry/
|
|
42
|
-
"@opentelemetry/
|
|
43
|
-
"@opentelemetry/
|
|
50
|
+
"@opentelemetry/api": "1.1.0",
|
|
51
|
+
"@opentelemetry/core": "1.1.0",
|
|
52
|
+
"@opentelemetry/resources": "1.1.0",
|
|
53
|
+
"@opentelemetry/semantic-conventions": "1.1.0",
|
|
54
|
+
"@opentelemetry/sdk-node": "0.39.1",
|
|
55
|
+
"@opentelemetry/exporter-trace-otlp-http": "0.39.1",
|
|
56
|
+
"@opentelemetry/auto-instrumentations-node": "0.39.1"
|
|
57
|
+
},
|
|
58
|
+
"overrides": {
|
|
59
|
+
"@opentelemetry/api": "1.1.0",
|
|
60
|
+
"@opentelemetry/core": "1.1.0",
|
|
61
|
+
"@opentelemetry/resources": "1.1.0",
|
|
62
|
+
"@opentelemetry/semantic-conventions": "1.1.0",
|
|
63
|
+
"@opentelemetry/sdk-node": "0.39.1",
|
|
64
|
+
"@opentelemetry/exporter-trace-otlp-http": "0.39.1",
|
|
65
|
+
"@opentelemetry/auto-instrumentations-node": "0.39.1"
|
|
44
66
|
},
|
|
45
67
|
"engines": {
|
|
46
68
|
"node": ">=12.0.0"
|
package/register.js
CHANGED
|
@@ -380,6 +380,155 @@ try {
|
|
|
380
380
|
}
|
|
381
381
|
} catch (_) {}
|
|
382
382
|
|
|
383
|
+
try {
|
|
384
|
+
if (typeof globalThis.fetch === 'function') {
|
|
385
|
+
const g = globalThis
|
|
386
|
+
if (!g.__azifyLoggerFetchPatched) {
|
|
387
|
+
g.__azifyLoggerFetchPatched = true
|
|
388
|
+
|
|
389
|
+
const { getRequestContext, runWithRequestContext } = require('./store')
|
|
390
|
+
const { randomUUID, randomBytes } = require('crypto')
|
|
391
|
+
const { performance } = require('perf_hooks')
|
|
392
|
+
const axios = require('axios')
|
|
393
|
+
const os = require('os')
|
|
394
|
+
|
|
395
|
+
const serviceName = process.env.APP_NAME || 'app'
|
|
396
|
+
const loggerUrl = process.env.AZIFY_LOGGER_URL || 'http://localhost:3001/log'
|
|
397
|
+
const environment = process.env.NODE_ENV || 'development'
|
|
398
|
+
|
|
399
|
+
async function sendLog(level, message, meta) {
|
|
400
|
+
const payload = {
|
|
401
|
+
level,
|
|
402
|
+
message,
|
|
403
|
+
meta: {
|
|
404
|
+
...meta,
|
|
405
|
+
service: {
|
|
406
|
+
name: serviceName,
|
|
407
|
+
version: (meta && meta.service && meta.service.version) || '1.0.0'
|
|
408
|
+
},
|
|
409
|
+
environment,
|
|
410
|
+
timestamp: new Date().toISOString(),
|
|
411
|
+
hostname: os.hostname()
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
await axios.post(loggerUrl, payload, { timeout: 5000 })
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error('Erro ao enviar log:', error && error.message ? error.message : error)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const originalFetch = globalThis.fetch.bind(globalThis)
|
|
423
|
+
|
|
424
|
+
function sanitizeHeaders(headers) {
|
|
425
|
+
const SENSITIVE_HEADER_KEYS = new Set([
|
|
426
|
+
'authorization',
|
|
427
|
+
'cookie',
|
|
428
|
+
'set-cookie',
|
|
429
|
+
'x-api-key',
|
|
430
|
+
'x-auth-token',
|
|
431
|
+
'x-access-token',
|
|
432
|
+
'proxy-authorization'
|
|
433
|
+
])
|
|
434
|
+
|
|
435
|
+
const sanitized = {}
|
|
436
|
+
headers.forEach((value, key) => {
|
|
437
|
+
const lower = key.toLowerCase()
|
|
438
|
+
if (SENSITIVE_HEADER_KEYS.has(lower)) {
|
|
439
|
+
sanitized[lower] = '***'
|
|
440
|
+
} else {
|
|
441
|
+
sanitized[lower] = value
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
return sanitized
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function ensureRequest(input, init) {
|
|
448
|
+
if (typeof Request !== 'undefined' && input instanceof Request) {
|
|
449
|
+
return init ? new Request(input, init) : input
|
|
450
|
+
}
|
|
451
|
+
return new Request(input, init)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
globalThis.fetch = async function patchedFetch(input, init) {
|
|
455
|
+
const request = ensureRequest(input, init)
|
|
456
|
+
const method = request.method.toUpperCase()
|
|
457
|
+
const url = request.url
|
|
458
|
+
|
|
459
|
+
const ctx = getRequestContext()
|
|
460
|
+
const traceId = (ctx && ctx.traceId) || randomUUID()
|
|
461
|
+
const parentSpanId = (ctx && ctx.spanId) || null
|
|
462
|
+
const requestId = (ctx && ctx.requestId) || randomUUID()
|
|
463
|
+
const spanId = randomBytes(8).toString('hex')
|
|
464
|
+
|
|
465
|
+
request.headers.set('x-trace-id', traceId)
|
|
466
|
+
request.headers.set('x-span-id', spanId)
|
|
467
|
+
request.headers.set('x-parent-span-id', parentSpanId || '')
|
|
468
|
+
request.headers.set('x-request-id', requestId)
|
|
469
|
+
|
|
470
|
+
const requestMeta = {
|
|
471
|
+
traceId,
|
|
472
|
+
spanId,
|
|
473
|
+
parentSpanId,
|
|
474
|
+
requestId,
|
|
475
|
+
method,
|
|
476
|
+
url,
|
|
477
|
+
headers: sanitizeHeaders(request.headers)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const start = performance.now()
|
|
481
|
+
void sendLog('info', `[REQUEST] ${method} ${url}`, requestMeta)
|
|
482
|
+
|
|
483
|
+
const childCtx = {
|
|
484
|
+
traceId,
|
|
485
|
+
spanId,
|
|
486
|
+
parentSpanId,
|
|
487
|
+
requestId
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
try {
|
|
491
|
+
const response = await runWithRequestContext(childCtx, function() {
|
|
492
|
+
return originalFetch(request)
|
|
493
|
+
})
|
|
494
|
+
|
|
495
|
+
const duration = Number((performance.now() - start).toFixed(2))
|
|
496
|
+
const responseMeta = {
|
|
497
|
+
...requestMeta,
|
|
498
|
+
statusCode: response.status,
|
|
499
|
+
responseTimeMs: duration
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const message = `[RESPONSE] ${method} ${url} ${response.status} ${duration}ms`
|
|
503
|
+
if (response.status >= 500) {
|
|
504
|
+
void sendLog('error', message, responseMeta)
|
|
505
|
+
} else if (response.status >= 400) {
|
|
506
|
+
void sendLog('warn', message, responseMeta)
|
|
507
|
+
} else {
|
|
508
|
+
void sendLog('info', message, responseMeta)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return response
|
|
512
|
+
} catch (error) {
|
|
513
|
+
const duration = Number((performance.now() - start).toFixed(2))
|
|
514
|
+
const errorMeta = {
|
|
515
|
+
...requestMeta,
|
|
516
|
+
responseTimeMs: duration
|
|
517
|
+
}
|
|
518
|
+
const err = error instanceof Error ? error : new Error(String(error))
|
|
519
|
+
errorMeta.error = {
|
|
520
|
+
name: err.name,
|
|
521
|
+
message: err.message,
|
|
522
|
+
stack: err.stack
|
|
523
|
+
}
|
|
524
|
+
void sendLog('error', `[ERROR] ${method} ${url}`, errorMeta)
|
|
525
|
+
throw error
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
} catch (_) {}
|
|
531
|
+
|
|
383
532
|
try {
|
|
384
533
|
const Bull = require('bull')
|
|
385
534
|
const { runWithRequestContext } = require('./store')
|