azify-logger 1.0.29 → 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 +32 -10
- package/middleware-express.js +84 -24
- package/package.json +2 -3
- package/register-otel.js +110 -2
- package/register.js +54 -36
- package/server.js +84 -0
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
|
|
package/middleware-express.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
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
|
+
// 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
|
+
|
|
7
18
|
function fastUUID() {
|
|
8
19
|
const timestamp = Date.now().toString(36)
|
|
9
20
|
const randomPart = Math.random().toString(36).substring(2, 15)
|
|
@@ -159,20 +170,67 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
159
170
|
let headersCached = false
|
|
160
171
|
let reqCtx = null
|
|
161
172
|
|
|
162
|
-
|
|
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
|
+
}
|
|
163
193
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
172
212
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
|
176
234
|
sendLog(level, message, meta)
|
|
177
235
|
}
|
|
178
236
|
|
|
@@ -182,19 +240,21 @@ function createExpressLoggingMiddleware(options = {}) {
|
|
|
182
240
|
|
|
183
241
|
query = req.query || null
|
|
184
242
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
243
|
+
const ctx = ensureRequestContext()
|
|
244
|
+
|
|
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
|
|
251
|
+
} else {
|
|
252
|
+
traceId = ctx.traceId
|
|
253
|
+
spanId = ctx.spanId
|
|
254
|
+
parentSpanId = ctx.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
192
255
|
}
|
|
193
256
|
|
|
194
|
-
requestId =
|
|
195
|
-
traceId = reqCtx.traceId
|
|
196
|
-
spanId = reqCtx.spanId
|
|
197
|
-
parentSpanId = reqCtx.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
257
|
+
requestId = ctx.requestId || req.requestId || fastUUID()
|
|
198
258
|
|
|
199
259
|
clientIp = req.ip || req.connection?.remoteAddress || req.socket?.remoteAddress || 'unknown'
|
|
200
260
|
req.requestId = requestId
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azify-logger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.30",
|
|
4
4
|
"description": "Azify Logger Client - Centralized logging for OpenSearch",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@opentelemetry/api": "1.0.4",
|
|
32
32
|
"@opentelemetry/auto-instrumentations-node": "0.27.0",
|
|
33
33
|
"@opentelemetry/core": "1.0.1",
|
|
34
|
-
"@opentelemetry/exporter-trace-otlp-http": "0.27.0",
|
|
34
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.27.0",
|
|
35
35
|
"@opentelemetry/instrumentation-express": "0.27.0",
|
|
36
36
|
"@opentelemetry/instrumentation-http": "0.27.0",
|
|
37
37
|
"@opentelemetry/instrumentation-restify": "0.27.0",
|
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"@opentelemetry/resources": "1.0.1",
|
|
56
56
|
"@opentelemetry/semantic-conventions": "1.0.1",
|
|
57
57
|
"@opentelemetry/sdk-node": "0.27.0",
|
|
58
|
-
"@opentelemetry/exporter-trace-otlp-http": "0.27.0",
|
|
59
58
|
"@opentelemetry/auto-instrumentations-node": "0.27.0"
|
|
60
59
|
},
|
|
61
60
|
"engines": {
|
package/register-otel.js
CHANGED
|
@@ -3,6 +3,8 @@ try {
|
|
|
3
3
|
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http')
|
|
4
4
|
const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express')
|
|
5
5
|
const { RestifyInstrumentation } = require('@opentelemetry/instrumentation-restify')
|
|
6
|
+
const { Resource } = require('@opentelemetry/resources')
|
|
7
|
+
const { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions')
|
|
6
8
|
|
|
7
9
|
const serviceName = process.env.OTEL_SERVICE_NAME || process.env.APP_NAME || 'app'
|
|
8
10
|
const serviceVersion = process.env.OTEL_SERVICE_VERSION || '1.0.0'
|
|
@@ -10,6 +12,12 @@ try {
|
|
|
10
12
|
process.env.OTEL_SERVICE_NAME = serviceName
|
|
11
13
|
process.env.OTEL_SERVICE_VERSION = serviceVersion
|
|
12
14
|
|
|
15
|
+
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
|
16
|
+
if (otlpEndpoint) {
|
|
17
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = otlpEndpoint
|
|
18
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = otlpEndpoint
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
let collectorHost = null
|
|
14
22
|
let collectorPath = null
|
|
15
23
|
try {
|
|
@@ -62,9 +70,109 @@ try {
|
|
|
62
70
|
enabled: true
|
|
63
71
|
})
|
|
64
72
|
|
|
73
|
+
let traceExporter = null
|
|
74
|
+
if (otlpEndpoint) {
|
|
75
|
+
try {
|
|
76
|
+
let endpointUrl
|
|
77
|
+
try {
|
|
78
|
+
endpointUrl = new URL(otlpEndpoint)
|
|
79
|
+
} catch (urlErr) {
|
|
80
|
+
throw new Error(`URL inválida: ${otlpEndpoint}`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const http = require('http')
|
|
84
|
+
const url = new URL(otlpEndpoint)
|
|
85
|
+
|
|
86
|
+
traceExporter = {
|
|
87
|
+
export: function(spans, resultCallback) {
|
|
88
|
+
if (!spans || spans.length === 0) {
|
|
89
|
+
if (resultCallback) resultCallback({ code: 0 })
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const resourceSpans = [{
|
|
95
|
+
resource: {
|
|
96
|
+
attributes: [
|
|
97
|
+
{ key: 'service.name', value: { stringValue: serviceName } },
|
|
98
|
+
{ key: 'service.version', value: { stringValue: serviceVersion } }
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
scopeSpans: [{
|
|
102
|
+
spans: spans.map(span => {
|
|
103
|
+
const ctx = span.spanContext()
|
|
104
|
+
return {
|
|
105
|
+
traceId: ctx.traceId.replace(/-/g, '').padStart(32, '0').substring(0, 32),
|
|
106
|
+
spanId: ctx.spanId.replace(/-/g, '').padStart(16, '0').substring(0, 16),
|
|
107
|
+
name: span.name || 'span',
|
|
108
|
+
kind: span.kind || 1,
|
|
109
|
+
startTimeUnixNano: String(span.startTime?.[0] * 1e9 + (span.startTime?.[1] || 0) || Date.now() * 1e6),
|
|
110
|
+
endTimeUnixNano: String(span.endTime?.[0] * 1e9 + (span.endTime?.[1] || 0) || Date.now() * 1e6),
|
|
111
|
+
attributes: Object.entries(span.attributes || {}).map(([k, v]) => ({
|
|
112
|
+
key: k,
|
|
113
|
+
value: { stringValue: String(v) }
|
|
114
|
+
}))
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}]
|
|
118
|
+
}]
|
|
119
|
+
|
|
120
|
+
const payload = JSON.stringify({ resourceSpans })
|
|
121
|
+
|
|
122
|
+
const options = {
|
|
123
|
+
hostname: url.hostname,
|
|
124
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
125
|
+
path: url.pathname,
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: {
|
|
128
|
+
'Content-Type': 'application/json',
|
|
129
|
+
'Content-Length': Buffer.byteLength(payload)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const req = http.request(options, (res) => {
|
|
134
|
+
let data = ''
|
|
135
|
+
res.on('data', (chunk) => { data += chunk })
|
|
136
|
+
res.on('end', () => {
|
|
137
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
138
|
+
if (resultCallback) resultCallback({ code: 0 })
|
|
139
|
+
} else {
|
|
140
|
+
if (resultCallback) resultCallback({ code: 1, error: new Error(`HTTP ${res.statusCode}`) })
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
req.on('error', (err) => {
|
|
146
|
+
if (resultCallback) resultCallback({ code: 1, error: err })
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
req.write(payload)
|
|
150
|
+
req.end()
|
|
151
|
+
} catch (err) {
|
|
152
|
+
if (resultCallback) resultCallback({ code: 1, error: err })
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
shutdown: function() {
|
|
156
|
+
return Promise.resolve()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
} catch (err) {
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let spanProcessor = null
|
|
165
|
+
if (traceExporter) {
|
|
166
|
+
const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base')
|
|
167
|
+
spanProcessor = new SimpleSpanProcessor(traceExporter)
|
|
168
|
+
}
|
|
169
|
+
|
|
65
170
|
const sdk = new NodeSDK({
|
|
66
|
-
|
|
67
|
-
|
|
171
|
+
resource: new Resource({
|
|
172
|
+
[SEMRESATTRS_SERVICE_NAME]: serviceName,
|
|
173
|
+
[SEMRESATTRS_SERVICE_VERSION]: serviceVersion
|
|
174
|
+
}),
|
|
175
|
+
spanProcessor,
|
|
68
176
|
instrumentations: [
|
|
69
177
|
httpInstrumentation,
|
|
70
178
|
expressInstrumentation,
|
package/register.js
CHANGED
|
@@ -175,14 +175,17 @@ try {
|
|
|
175
175
|
|
|
176
176
|
logger.info = function(obj, msg, ...args) {
|
|
177
177
|
const ctx = getRequestContext()
|
|
178
|
-
|
|
178
|
+
const otelCtx = !ctx || !ctx.traceId ? getOtelTraceContext() : null
|
|
179
|
+
const traceCtx = ctx || otelCtx
|
|
180
|
+
|
|
181
|
+
if (traceCtx && traceCtx.traceId) {
|
|
179
182
|
if (typeof obj === 'object' && obj !== null) {
|
|
180
|
-
obj.traceId =
|
|
181
|
-
obj.spanId =
|
|
182
|
-
obj.parentSpanId =
|
|
183
|
-
obj.requestId =
|
|
183
|
+
obj.traceId = traceCtx.traceId
|
|
184
|
+
obj.spanId = traceCtx.spanId
|
|
185
|
+
obj.parentSpanId = traceCtx.parentSpanId
|
|
186
|
+
obj.requestId = traceCtx.requestId || ctx?.requestId
|
|
184
187
|
} else {
|
|
185
|
-
obj = { traceId:
|
|
188
|
+
obj = { traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId, msg: obj }
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
return originalInfo.call(this, obj, msg, ...args)
|
|
@@ -190,14 +193,17 @@ try {
|
|
|
190
193
|
|
|
191
194
|
logger.error = function(obj, msg, ...args) {
|
|
192
195
|
const ctx = getRequestContext()
|
|
193
|
-
|
|
196
|
+
const otelCtx = !ctx || !ctx.traceId ? getOtelTraceContext() : null
|
|
197
|
+
const traceCtx = ctx || otelCtx
|
|
198
|
+
|
|
199
|
+
if (traceCtx && traceCtx.traceId) {
|
|
194
200
|
if (typeof obj === 'object' && obj !== null) {
|
|
195
|
-
obj.traceId =
|
|
196
|
-
obj.spanId =
|
|
197
|
-
obj.parentSpanId =
|
|
198
|
-
obj.requestId =
|
|
201
|
+
obj.traceId = traceCtx.traceId
|
|
202
|
+
obj.spanId = traceCtx.spanId
|
|
203
|
+
obj.parentSpanId = traceCtx.parentSpanId
|
|
204
|
+
obj.requestId = traceCtx.requestId || ctx?.requestId
|
|
199
205
|
} else {
|
|
200
|
-
obj = { traceId:
|
|
206
|
+
obj = { traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId, msg: obj }
|
|
201
207
|
}
|
|
202
208
|
}
|
|
203
209
|
return originalError.call(this, obj, msg, ...args)
|
|
@@ -205,14 +211,17 @@ try {
|
|
|
205
211
|
|
|
206
212
|
logger.warn = function(obj, msg, ...args) {
|
|
207
213
|
const ctx = getRequestContext()
|
|
208
|
-
|
|
214
|
+
const otelCtx = !ctx || !ctx.traceId ? getOtelTraceContext() : null
|
|
215
|
+
const traceCtx = ctx || otelCtx
|
|
216
|
+
|
|
217
|
+
if (traceCtx && traceCtx.traceId) {
|
|
209
218
|
if (typeof obj === 'object' && obj !== null) {
|
|
210
|
-
obj.traceId =
|
|
211
|
-
obj.spanId =
|
|
212
|
-
obj.parentSpanId =
|
|
213
|
-
obj.requestId =
|
|
219
|
+
obj.traceId = traceCtx.traceId
|
|
220
|
+
obj.spanId = traceCtx.spanId
|
|
221
|
+
obj.parentSpanId = traceCtx.parentSpanId
|
|
222
|
+
obj.requestId = traceCtx.requestId || ctx?.requestId
|
|
214
223
|
} else {
|
|
215
|
-
obj = { traceId:
|
|
224
|
+
obj = { traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId, msg: obj }
|
|
216
225
|
}
|
|
217
226
|
}
|
|
218
227
|
return originalWarn.call(this, obj, msg, ...args)
|
|
@@ -220,14 +229,17 @@ try {
|
|
|
220
229
|
|
|
221
230
|
logger.debug = function(obj, msg, ...args) {
|
|
222
231
|
const ctx = getRequestContext()
|
|
223
|
-
|
|
232
|
+
const otelCtx = !ctx || !ctx.traceId ? getOtelTraceContext() : null
|
|
233
|
+
const traceCtx = ctx || otelCtx
|
|
234
|
+
|
|
235
|
+
if (traceCtx && traceCtx.traceId) {
|
|
224
236
|
if (typeof obj === 'object' && obj !== null) {
|
|
225
|
-
obj.traceId =
|
|
226
|
-
obj.spanId =
|
|
227
|
-
obj.parentSpanId =
|
|
228
|
-
obj.requestId =
|
|
237
|
+
obj.traceId = traceCtx.traceId
|
|
238
|
+
obj.spanId = traceCtx.spanId
|
|
239
|
+
obj.parentSpanId = traceCtx.parentSpanId
|
|
240
|
+
obj.requestId = traceCtx.requestId || ctx?.requestId
|
|
229
241
|
} else {
|
|
230
|
-
obj = { traceId:
|
|
242
|
+
obj = { traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId, msg: obj }
|
|
231
243
|
}
|
|
232
244
|
}
|
|
233
245
|
return originalDebug.call(this, obj, msg, ...args)
|
|
@@ -235,14 +247,17 @@ try {
|
|
|
235
247
|
|
|
236
248
|
logger.fatal = function(obj, msg, ...args) {
|
|
237
249
|
const ctx = getRequestContext()
|
|
238
|
-
|
|
250
|
+
const otelCtx = !ctx || !ctx.traceId ? getOtelTraceContext() : null
|
|
251
|
+
const traceCtx = ctx || otelCtx
|
|
252
|
+
|
|
253
|
+
if (traceCtx && traceCtx.traceId) {
|
|
239
254
|
if (typeof obj === 'object' && obj !== null) {
|
|
240
|
-
obj.traceId =
|
|
241
|
-
obj.spanId =
|
|
242
|
-
obj.parentSpanId =
|
|
243
|
-
obj.requestId =
|
|
255
|
+
obj.traceId = traceCtx.traceId
|
|
256
|
+
obj.spanId = traceCtx.spanId
|
|
257
|
+
obj.parentSpanId = traceCtx.parentSpanId
|
|
258
|
+
obj.requestId = traceCtx.requestId || ctx?.requestId
|
|
244
259
|
} else {
|
|
245
|
-
obj = { traceId:
|
|
260
|
+
obj = { traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId, msg: obj }
|
|
246
261
|
}
|
|
247
262
|
}
|
|
248
263
|
return originalFatal.call(this, obj, msg, ...args)
|
|
@@ -250,14 +265,17 @@ try {
|
|
|
250
265
|
|
|
251
266
|
logger.trace = function(obj, msg, ...args) {
|
|
252
267
|
const ctx = getRequestContext()
|
|
253
|
-
|
|
268
|
+
const otelCtx = !ctx || !ctx.traceId ? getOtelTraceContext() : null
|
|
269
|
+
const traceCtx = ctx || otelCtx
|
|
270
|
+
|
|
271
|
+
if (traceCtx && traceCtx.traceId) {
|
|
254
272
|
if (typeof obj === 'object' && obj !== null) {
|
|
255
|
-
obj.traceId =
|
|
256
|
-
obj.spanId =
|
|
257
|
-
obj.parentSpanId =
|
|
258
|
-
obj.requestId =
|
|
273
|
+
obj.traceId = traceCtx.traceId
|
|
274
|
+
obj.spanId = traceCtx.spanId
|
|
275
|
+
obj.parentSpanId = traceCtx.parentSpanId
|
|
276
|
+
obj.requestId = traceCtx.requestId || ctx?.requestId
|
|
259
277
|
} else {
|
|
260
|
-
obj = { traceId:
|
|
278
|
+
obj = { traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId, msg: obj }
|
|
261
279
|
}
|
|
262
280
|
}
|
|
263
281
|
return originalTrace.call(this, obj, msg, ...args)
|
package/server.js
CHANGED
|
@@ -481,6 +481,90 @@ async function setupGrafanaForApp(appName) {
|
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
+
// Datasource Grafana Tempo para tracing completo (opcional - não bloqueia criação da org)
|
|
485
|
+
try {
|
|
486
|
+
const tempoDatasourceUid = `tempo-${appName.toLowerCase()}`
|
|
487
|
+
const tempoUrl = runningInDocker ? 'http://azify-tempo:3200' : 'http://localhost:3200'
|
|
488
|
+
const tempoDatasourceConfig = {
|
|
489
|
+
name: `Tempo-${appName}`,
|
|
490
|
+
type: 'tempo',
|
|
491
|
+
access: 'proxy',
|
|
492
|
+
url: tempoUrl,
|
|
493
|
+
uid: tempoDatasourceUid,
|
|
494
|
+
isDefault: false,
|
|
495
|
+
jsonData: {
|
|
496
|
+
httpMethod: 'GET',
|
|
497
|
+
tracesToLogs: {
|
|
498
|
+
datasourceUid: datasourceUid,
|
|
499
|
+
tags: ['job', 'service', 'pod'],
|
|
500
|
+
mappedTags: [{ key: 'service.name', value: 'service' }],
|
|
501
|
+
mapTagNamesEnabled: false,
|
|
502
|
+
spanStartTimeShift: '1h',
|
|
503
|
+
spanEndTimeShift: '1h',
|
|
504
|
+
filterByTraceID: false,
|
|
505
|
+
filterBySpanID: false
|
|
506
|
+
},
|
|
507
|
+
serviceMap: {
|
|
508
|
+
datasourceUid: datasourceUid
|
|
509
|
+
},
|
|
510
|
+
nodeGraph: {
|
|
511
|
+
enabled: true
|
|
512
|
+
},
|
|
513
|
+
search: {
|
|
514
|
+
hide: false
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
editable: true,
|
|
518
|
+
version: 1
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
console.log(`[setupGrafana] Verificando datasource Tempo existente: ${tempoDatasourceUid} na org ${org.id}`)
|
|
523
|
+
const existingTempo = await axios.get(`${grafanaUrl}/api/datasources/uid/${tempoDatasourceUid}`, {
|
|
524
|
+
auth,
|
|
525
|
+
headers: { 'X-Grafana-Org-Id': org.id },
|
|
526
|
+
timeout: 3000
|
|
527
|
+
})
|
|
528
|
+
console.log(`[setupGrafana] Datasource Tempo existente encontrado: ${existingTempo.data?.id || 'N/A'}`)
|
|
529
|
+
try {
|
|
530
|
+
await axios.put(
|
|
531
|
+
`${grafanaUrl}/api/datasources/${existingTempo.data.id}`,
|
|
532
|
+
tempoDatasourceConfig,
|
|
533
|
+
{
|
|
534
|
+
auth,
|
|
535
|
+
headers: { 'X-Grafana-Org-Id': org.id },
|
|
536
|
+
timeout: 3000
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
console.log(`[setupGrafana] ✅ Datasource Tempo atualizado: ${tempoDatasourceUid}`)
|
|
540
|
+
} catch (updateTempoErr) {
|
|
541
|
+
console.error(`[setupGrafana] ⚠️ Erro ao atualizar datasource Tempo: ${updateTempoErr.response?.data?.message || updateTempoErr.message}`)
|
|
542
|
+
}
|
|
543
|
+
} catch (tempoError) {
|
|
544
|
+
console.log(`[setupGrafana] Erro ao verificar datasource Tempo: status=${tempoError.response?.status || 'N/A'}, message=${tempoError.response?.data?.message || tempoError.message}`)
|
|
545
|
+
if (tempoError.response?.status === 404) {
|
|
546
|
+
try {
|
|
547
|
+
const tempoCreate = await axios.post(
|
|
548
|
+
`${grafanaUrl}/api/datasources`,
|
|
549
|
+
tempoDatasourceConfig,
|
|
550
|
+
{
|
|
551
|
+
auth,
|
|
552
|
+
headers: { 'X-Grafana-Org-Id': org.id },
|
|
553
|
+
timeout: 3000
|
|
554
|
+
}
|
|
555
|
+
)
|
|
556
|
+
console.log(`[setupGrafana] ✅ Datasource Tempo criado: ${tempoDatasourceUid} (ID: ${tempoCreate.data?.datasource?.id || 'N/A'})`)
|
|
557
|
+
} catch (createTempoError) {
|
|
558
|
+
console.error(`[setupGrafana] ⚠️ Erro ao criar datasource Tempo via API: ${createTempoError.response?.data?.message || createTempoError.message}`)
|
|
559
|
+
console.log(`[setupGrafana] 💡 Datasource Tempo será criado manualmente ou na próxima tentativa`)
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
} catch (tempoGeneralError) {
|
|
564
|
+
console.error(`[setupGrafana] ⚠️ Erro geral ao configurar datasource Tempo: ${tempoGeneralError.message}`)
|
|
565
|
+
console.log(`[setupGrafana] 💡 Continuando sem datasource Tempo - não é crítico para funcionamento básico`)
|
|
566
|
+
}
|
|
567
|
+
|
|
484
568
|
console.log(`[setupGrafana] 📈 Criando dashboard para ${appName}...`)
|
|
485
569
|
|
|
486
570
|
const appNameLower = appName.toLowerCase()
|