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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azify-logger",
3
- "version": "1.0.29",
3
+ "version": "1.0.31",
4
4
  "description": "Azify Logger Client - Centralized logging for OpenSearch",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -17,7 +17,18 @@
17
17
  "docker:up": "docker-compose up -d",
18
18
  "docker:down": "docker-compose down",
19
19
  "docker:logs": "docker-compose logs -f",
20
- "worker": "node scripts/redis-worker.js"
20
+ "worker": "node scripts/redis-worker.js",
21
+ "validate:retention": "node scripts/validate-retention.js",
22
+ "inspect:zip": "node scripts/inspect-zip.js",
23
+ "retention": "node scripts/retention-manager.js",
24
+ "list:blob": "node scripts/list-blob-files.js",
25
+ "list:blob:recent": "node scripts/download-blob-zip.js",
26
+ "check:old-logs": "node scripts/check-old-logs.js",
27
+ "validate:blob": "node scripts/validate-blob-zip.js",
28
+ "validate:blob:keep": "KEEP_VALIDATION_FILES=true node scripts/validate-blob-zip.js",
29
+ "download:blob": "node scripts/download-blob-zip.js",
30
+ "reimport:logs": "node scripts/reimport-logs.js",
31
+ "check:server-time": "node -e \"const now = new Date(); const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; console.log('🕐 Hora do servidor:', now.toLocaleString('pt-BR', {timeZone: tz})); console.log('🌍 Timezone:', tz); console.log('🕐 Hora em São Paulo:', now.toLocaleString('pt-BR', {timeZone: 'America/Sao_Paulo'})); console.log('\\n💡 Configure RETENTION_RUN_AT_HOUR com a hora do servidor (0-23)');\""
21
32
  },
22
33
  "keywords": [
23
34
  "logging",
@@ -28,16 +39,19 @@
28
39
  "author": "Azify",
29
40
  "license": "MIT",
30
41
  "dependencies": {
42
+ "@azure/storage-blob": "^12.17.0",
31
43
  "@opentelemetry/api": "1.0.4",
32
44
  "@opentelemetry/auto-instrumentations-node": "0.27.0",
33
45
  "@opentelemetry/core": "1.0.1",
34
- "@opentelemetry/exporter-trace-otlp-http": "0.27.0",
46
+ "@opentelemetry/exporter-trace-otlp-http": "^0.27.0",
35
47
  "@opentelemetry/instrumentation-express": "0.27.0",
36
48
  "@opentelemetry/instrumentation-http": "0.27.0",
37
49
  "@opentelemetry/instrumentation-restify": "0.27.0",
38
50
  "@opentelemetry/resources": "1.0.1",
39
51
  "@opentelemetry/sdk-node": "0.27.0",
40
52
  "@opentelemetry/semantic-conventions": "1.0.1",
53
+ "adm-zip": "^0.5.16",
54
+ "archiver": "^6.0.1",
41
55
  "axios": "^1.6.0",
42
56
  "cors": "^2.8.5",
43
57
  "express": "^4.18.2",
@@ -55,7 +69,6 @@
55
69
  "@opentelemetry/resources": "1.0.1",
56
70
  "@opentelemetry/semantic-conventions": "1.0.1",
57
71
  "@opentelemetry/sdk-node": "0.27.0",
58
- "@opentelemetry/exporter-trace-otlp-http": "0.27.0",
59
72
  "@opentelemetry/auto-instrumentations-node": "0.27.0"
60
73
  },
61
74
  "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
- serviceName,
67
- serviceVersion,
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
- if (ctx && ctx.traceId) {
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 = ctx.traceId
181
- obj.spanId = ctx.spanId
182
- obj.parentSpanId = ctx.parentSpanId
183
- obj.requestId = ctx.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: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
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
- if (ctx && ctx.traceId) {
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 = ctx.traceId
196
- obj.spanId = ctx.spanId
197
- obj.parentSpanId = ctx.parentSpanId
198
- obj.requestId = ctx.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: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
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
- if (ctx && ctx.traceId) {
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 = ctx.traceId
211
- obj.spanId = ctx.spanId
212
- obj.parentSpanId = ctx.parentSpanId
213
- obj.requestId = ctx.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: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
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
- if (ctx && ctx.traceId) {
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 = ctx.traceId
226
- obj.spanId = ctx.spanId
227
- obj.parentSpanId = ctx.parentSpanId
228
- obj.requestId = ctx.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: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
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
- if (ctx && ctx.traceId) {
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 = ctx.traceId
241
- obj.spanId = ctx.spanId
242
- obj.parentSpanId = ctx.parentSpanId
243
- obj.requestId = ctx.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: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
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
- if (ctx && ctx.traceId) {
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 = ctx.traceId
256
- obj.spanId = ctx.spanId
257
- obj.parentSpanId = ctx.parentSpanId
258
- obj.requestId = ctx.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: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
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)
@@ -562,68 +580,143 @@ try {
562
580
 
563
581
  instance.interceptors.response.use(
564
582
  (response) => {
565
- const url = buildUrl(response.config)
566
- const testMeta = { url }
567
- if (isLoggerApiCall(testMeta)) {
568
- return response
569
- }
583
+ try {
584
+ if (!response || !response.config) {
585
+ return response
586
+ }
570
587
 
571
- const marker = response.config?.__azifyLogger
572
- const childCtx = response.config?.__azifyChildCtx
588
+ const url = buildUrl(response.config) || response.config?.url || response.request?.responseURL || 'unknown'
589
+ const testMeta = { url }
590
+ if (isLoggerApiCall(testMeta)) {
591
+ return response
592
+ }
573
593
 
574
- if (marker && marker.meta) {
575
- const duration = Number((performance.now() - marker.start).toFixed(2))
594
+ const marker = response.config?.__azifyLogger
595
+ const method = (response.config?.method || 'get').toUpperCase()
596
+ const requestHeaders = response.config?.headers || {}
597
+ const hasTraceHeaders = !!(requestHeaders['x-trace-id'] || requestHeaders['X-Trace-ID'] || requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'])
576
598
  const shouldLogResponse =
577
599
  HTTP_CLIENT_MODE === 'all' ||
578
600
  (HTTP_CLIENT_MODE === 'errors' && response.status >= 400)
579
601
 
580
- if (shouldLogResponse) {
581
- const meta = {
582
- ...marker.meta,
583
- statusCode: response.status,
584
- responseTimeMs: duration,
585
- responseHeaders: response.headers,
586
- responseBody: response.data
602
+ if (shouldLogResponse || hasTraceHeaders) {
603
+ const finalUrl = (url && url !== 'unknown') ? url : (marker?.meta?.url || response.config?.url || response.request?.responseURL || url)
604
+
605
+ if (finalUrl && finalUrl !== 'unknown') {
606
+ let meta
607
+ let duration = 0
608
+
609
+ if (marker && marker.meta) {
610
+ duration = Number((performance.now() - marker.start).toFixed(2))
611
+ meta = {
612
+ ...marker.meta,
613
+ url: finalUrl,
614
+ statusCode: response.status,
615
+ responseTimeMs: duration,
616
+ responseHeaders: response.headers,
617
+ responseBody: response.data
618
+ }
619
+ } else {
620
+ const requestHeaders = response.config?.headers || {}
621
+ const ctx = getRequestContext()
622
+ const traceId = requestHeaders['x-trace-id'] || requestHeaders['X-Trace-ID'] || ctx?.traceId || randomUUID()
623
+ const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
624
+ const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
625
+ const requestId = requestHeaders['x-request-id'] || requestHeaders['X-Request-ID'] || ctx?.requestId || randomUUID()
626
+
627
+ meta = {
628
+ traceId,
629
+ spanId,
630
+ parentSpanId,
631
+ requestId,
632
+ method,
633
+ url: finalUrl,
634
+ statusCode: response.status,
635
+ responseTimeMs: duration,
636
+ responseHeaders: response.headers,
637
+ responseBody: response.data
638
+ }
587
639
  }
588
640
 
589
641
  markSource(meta, 'http-client')
590
- const message = `[RESPONSE] ${meta.method} ${meta.url} ${response.status} ${duration}ms`
642
+ const message = `[RESPONSE] ${meta.method} ${meta.url} ${response.status} ${meta.responseTimeMs}ms`
591
643
  const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
592
644
 
593
645
  sendOutboundLog(level, message, meta)
646
+ }
594
647
  }
648
+ } catch (err) {
595
649
  }
596
650
 
597
651
  return response
598
652
  },
599
653
  (error) => {
600
- const config = error?.config
601
- if (config) {
602
- const url = buildUrl(config)
603
- const testMeta = { url }
604
- if (isLoggerApiCall(testMeta)) {
605
- return Promise.reject(error)
654
+ try {
655
+ const config = error?.config
656
+ if (config) {
657
+ const url = buildUrl(config)
658
+ const testMeta = { url }
659
+ if (isLoggerApiCall(testMeta)) {
660
+ return Promise.reject(error)
661
+ }
606
662
  }
607
- }
608
663
 
609
- const marker = config?.__azifyLogger
610
- const childCtx = config?.__azifyChildCtx
611
-
612
- if (marker && marker.meta) {
613
- const duration = Number((performance.now() - marker.start).toFixed(2))
614
- const meta = {
615
- ...marker.meta,
616
- responseTimeMs: duration,
617
- error: {
618
- name: error?.name || 'Error',
619
- message: error?.message || String(error),
620
- stack: error?.stack
664
+ const marker = config?.__azifyLogger
665
+ const method = (config?.method || 'get').toUpperCase()
666
+ const url = config ? buildUrl(config) : (error?.request?.responseURL || error?.config?.url || 'unknown')
667
+ const shouldLogError = HTTP_CLIENT_MODE === 'all' || HTTP_CLIENT_MODE === 'errors'
668
+
669
+ if (shouldLogError && url && url !== 'unknown') {
670
+ let meta
671
+ let duration = 0
672
+
673
+ if (marker && marker.meta) {
674
+ duration = Number((performance.now() - marker.start).toFixed(2))
675
+ meta = {
676
+ ...marker.meta,
677
+ responseTimeMs: duration,
678
+ error: {
679
+ name: error?.name || 'Error',
680
+ message: error?.message || String(error),
681
+ stack: error?.stack,
682
+ code: error?.code,
683
+ status: error?.response?.status,
684
+ statusText: error?.response?.statusText
685
+ }
686
+ }
687
+ } else {
688
+ const requestHeaders = config?.headers || {}
689
+ const ctx = getRequestContext()
690
+ const traceId = requestHeaders['x-trace-id'] || requestHeaders['X-Trace-ID'] || ctx?.traceId || randomUUID()
691
+ const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
692
+ const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
693
+ const requestId = requestHeaders['x-request-id'] || requestHeaders['X-Request-ID'] || ctx?.requestId || randomUUID()
694
+
695
+ meta = {
696
+ traceId,
697
+ spanId,
698
+ parentSpanId,
699
+ requestId,
700
+ method,
701
+ url,
702
+ responseTimeMs: duration,
703
+ error: {
704
+ name: error?.name || 'Error',
705
+ message: error?.message || String(error),
706
+ stack: error?.stack,
707
+ code: error?.code,
708
+ status: error?.response?.status,
709
+ statusText: error?.response?.statusText
710
+ }
711
+ }
621
712
  }
713
+
714
+ markSource(meta, 'http-client')
715
+ const message = `[ERROR] ${meta.method} ${meta.url}`
716
+
717
+ sendOutboundLog('error', message, meta)
622
718
  }
623
- markSource(meta, 'http-client')
624
- const message = `[ERROR] ${meta.method} ${meta.url}`
625
-
626
- sendOutboundLog('error', message, meta)
719
+ } catch (err) {
627
720
  }
628
721
 
629
722
  return Promise.reject(error)
@@ -717,18 +810,19 @@ try {
717
810
  })
718
811
 
719
812
  const duration = Number((performance.now() - start).toFixed(2))
813
+ const hasTraceHeaders = !!(requestMeta.traceId && requestMeta.spanId)
720
814
  const shouldLogResponse =
721
815
  HTTP_CLIENT_MODE === 'all' ||
722
- (HTTP_CLIENT_MODE === 'errors' && response.status >= 400)
816
+ (HTTP_CLIENT_MODE === 'errors' && response.status >= 400) ||
817
+ hasTraceHeaders
723
818
 
724
819
  if (shouldLogResponse) {
725
- (async () => {
820
+ const logResponse = async () => {
821
+ let responseBody = null
822
+ const contentType = response.headers.get('content-type') || ''
823
+
726
824
  try {
727
825
  const clonedResponse = response.clone()
728
- const contentType = response.headers.get('content-type') || ''
729
-
730
- let responseBody = null
731
-
732
826
  if (contentType.includes('application/json') || contentType.includes('text/')) {
733
827
  const bodyText = await clonedResponse.text()
734
828
  if (contentType.includes('application/json')) {
@@ -741,7 +835,26 @@ try {
741
835
  responseBody = bodyText
742
836
  }
743
837
  }
838
+ } catch (cloneError) {
839
+ try {
840
+ if (contentType.includes('application/json') || contentType.includes('text/')) {
841
+ const bodyText = await response.text()
842
+ if (contentType.includes('application/json')) {
843
+ try {
844
+ responseBody = JSON.parse(bodyText)
845
+ } catch (_) {
846
+ responseBody = bodyText
847
+ }
848
+ } else {
849
+ responseBody = bodyText
850
+ }
851
+ }
852
+ } catch (_) {
853
+ responseBody = null
854
+ }
855
+ }
744
856
 
857
+ try {
745
858
  const responseMeta = {
746
859
  ...requestMeta,
747
860
  statusCode: response.status,
@@ -755,21 +868,27 @@ try {
755
868
 
756
869
  const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
757
870
  sendOutboundLog(level, message, responseMeta)
758
- } catch (_) {
759
- const responseMeta = {
760
- ...requestMeta,
761
- statusCode: response.status,
762
- responseTimeMs: duration,
763
- responseHeaders: Object.fromEntries(response.headers.entries()),
764
- responseBody: null
871
+ } catch (err) {
872
+ try {
873
+ const responseMeta = {
874
+ ...requestMeta,
875
+ statusCode: response.status,
876
+ responseTimeMs: duration,
877
+ responseHeaders: Object.fromEntries(response.headers.entries()),
878
+ responseBody: null
879
+ }
880
+
881
+ markSource(responseMeta, 'http-client')
882
+ const message = `[RESPONSE] ${method} ${url} ${response.status} ${duration}ms`
883
+
884
+ const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
885
+ sendOutboundLog(level, message, responseMeta)
886
+ } catch (_) {
765
887
  }
766
-
767
- markSource(responseMeta, 'http-client')
768
- const message = `[RESPONSE] ${method} ${url} ${response.status} ${duration}ms`
769
- const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
770
- sendOutboundLog(level, message, responseMeta)
771
888
  }
772
- })()
889
+ }
890
+
891
+ logResponse().catch(() => {})
773
892
  }
774
893
 
775
894
  return response