azify-logger 1.0.54 → 1.0.55-test

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.54",
3
+ "version": "1.0.55-test",
4
4
  "description": "Azify Logger Client - Centralized logging for OpenSearch",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,13 +28,70 @@ try {
28
28
  const MAX_EARLY_LOG_QUEUE = 2000
29
29
  const earlyLogQueue = []
30
30
 
31
+ function normalizePathEarly(p) {
32
+ if (!p) return '/'
33
+ const trimmed = String(p).replace(/\/+$/, '')
34
+ return trimmed === '' ? '/' : trimmed
35
+ }
36
+
37
+ /** Ignora esquema (http vs https): undici muitas vezes resolve como https e AZIFY_LOGGER_URL é http em dev. */
31
38
  function isLoggerUrl(urlStr) {
32
- const loggerUrl = process.env.AZIFY_LOGGER_URL
33
- if (!loggerUrl || typeof urlStr !== 'string' || !urlStr.trim()) return false
34
- const a = String(urlStr).trim().toLowerCase().replace(/\/+$/, '')
35
- const b = String(loggerUrl).trim().toLowerCase().replace(/\/+$/, '')
36
- if (!b) return false
37
- return a === b || a.startsWith(b + '/')
39
+ const loggerUrlRaw = process.env.AZIFY_LOGGER_URL || 'http://localhost:3001/log'
40
+ if (typeof urlStr !== 'string' || !urlStr.trim()) return false
41
+ try {
42
+ const base = new URL(String(loggerUrlRaw).trim())
43
+ const cand = new URL(String(urlStr).trim())
44
+ if (base.hostname.toLowerCase() !== cand.hostname.toLowerCase()) return false
45
+ const portBase = base.port || (base.protocol === 'https:' ? '443' : '80')
46
+ const portCand = cand.port || (cand.protocol === 'https:' ? '443' : '80')
47
+ if (String(portBase) !== String(portCand)) return false
48
+ const lp = normalizePathEarly(base.pathname)
49
+ const tp = normalizePathEarly(cand.pathname)
50
+ return tp === lp || tp.startsWith(lp + '/')
51
+ } catch (_) {
52
+ return false
53
+ }
54
+ }
55
+
56
+ /** undici.request(url, opts) / request(opts) — muitas chamadas não trazem origin; path pode ser URL absoluta ou só path + hostname. */
57
+ function resolveUrlStrFromUndiciRequestArgs(url, options) {
58
+ const opts = (typeof url === 'object' && url !== null && !(url instanceof URL)) ? url : options
59
+ const urlArg = (typeof url === 'string' || (url && (url.href || url instanceof URL))) ? url : (opts && (opts.url || opts.uri))
60
+ if (typeof urlArg === 'string') return urlArg
61
+ if (urlArg && typeof urlArg.href === 'string') return urlArg.href
62
+ if (urlArg && typeof urlArg.toString === 'function') return urlArg.toString()
63
+ if (opts && typeof opts.origin === 'string' && opts.path != null) {
64
+ return opts.origin + (String(opts.path).startsWith('/') ? '' : '/') + String(opts.path)
65
+ }
66
+ if (opts && typeof opts.path === 'string' && /^https?:\/\//i.test(opts.path)) {
67
+ return opts.path
68
+ }
69
+ if (opts && (opts.hostname || opts.host)) {
70
+ const h = String(opts.hostname || String(opts.host || '').split(':')[0]).trim()
71
+ if (h) {
72
+ const hl = h.toLowerCase()
73
+ const isLocal = hl === 'localhost' || hl === '127.0.0.1' || hl === '::1'
74
+ const proto = (opts.protocol && String(opts.protocol).replace(/:$/, '')) || (isLocal ? 'http' : 'https')
75
+ const pnum = opts.port != null && opts.port !== '' ? Number(opts.port) : NaN
76
+ const port = !Number.isNaN(pnum) && pnum !== 80 && pnum !== 443 ? ':' + pnum : ''
77
+ const p = opts.path != null ? String(opts.path) : '/'
78
+ return proto + '://' + h + port + (p.startsWith('/') ? p : '/' + p)
79
+ }
80
+ }
81
+ return 'unknown'
82
+ }
83
+
84
+ function resolveUrlStrFromDispatcherOpts(opts) {
85
+ if (!opts || typeof opts !== 'object') return 'unknown'
86
+ const origin = (opts.origin) || ''
87
+ const pathPart = opts.path != null ? opts.path : '/'
88
+ const pathStr = String(pathPart)
89
+ if (/^https?:\/\//i.test(pathStr)) return pathStr
90
+ if (origin) return origin + (pathStr.startsWith('/') ? pathStr : '/' + pathStr)
91
+ if (opts.hostname || opts.host) {
92
+ return resolveUrlStrFromUndiciRequestArgs(null, opts)
93
+ }
94
+ return 'unknown'
38
95
  }
39
96
 
40
97
  function send(level, message, meta) {
@@ -98,8 +155,19 @@ try {
98
155
  }
99
156
 
100
157
  function patchUndiciExports(exports) {
158
+ if (!exports || typeof exports !== 'object') return
159
+ if (typeof exports.fetch === 'function' && typeof globalThis.fetch === 'function' && globalThis.fetch.__azifyLoggerFetchPatched) {
160
+ if (exports.fetch !== globalThis.fetch) {
161
+ exports.fetch = globalThis.fetch
162
+ httpLog('undici exports.fetch -> globalThis.fetch (body logging)')
163
+ }
164
+ }
101
165
  const hasRequest = exports && typeof exports.request === 'function' && !exports.request.__azifyPatched
102
- const hasFetch = exports && typeof exports.fetch === 'function' && !exports.fetch.__azifyPatched
166
+ const hasFetch =
167
+ exports &&
168
+ typeof exports.fetch === 'function' &&
169
+ !exports.fetch.__azifyPatched &&
170
+ !exports.fetch.__azifyLoggerFetchPatched
103
171
  if (!exports || (!hasRequest && !hasFetch)) return
104
172
  const deps = loadStoreAndSampling()
105
173
  const noDeps = !deps
@@ -118,12 +186,7 @@ try {
118
186
  exports.request = function (url, options, callback) {
119
187
  const opts = (typeof url === 'object' && url !== null && !(url instanceof URL)) ? url : options
120
188
  const method = ((opts && opts.method) || 'GET').toUpperCase()
121
- let urlStr = 'unknown'
122
- const urlArg = (typeof url === 'string' || (url && (url.href || url instanceof URL))) ? url : (opts && (opts.url || opts.uri))
123
- if (typeof urlArg === 'string') urlStr = urlArg
124
- else if (urlArg && typeof urlArg.href === 'string') urlStr = urlArg.href
125
- else if (urlArg && typeof urlArg.toString === 'function') urlStr = urlArg.toString()
126
- else if (opts && typeof opts.origin === 'string' && opts.path != null) urlStr = opts.origin + (String(opts.path).startsWith('/') ? '' : '/') + String(opts.path)
189
+ const urlStr = resolveUrlStrFromUndiciRequestArgs(url, options)
127
190
  if (process.env.AZIFY_LOGGER_DEBUG === '1') { try { process.stderr.write('[AZIFY-PATCH] undici.request ' + method + ' ' + urlStr.slice(0, 80) + '\n') } catch (_) {} }
128
191
  const _ep = process.env.AZIFY_DEBUG_LOG_PATH
129
192
  if (_ep) { try { require('fs').appendFileSync(_ep, new Date().toISOString() + ' [AZIFY] WRAPPER exports.request ' + method + ' ' + urlStr.slice(0, 100) + '\n') } catch (_) {} }
@@ -174,9 +237,7 @@ try {
174
237
  const origDisp = proto.request
175
238
  proto.request = function (opts, callback) {
176
239
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
177
- const origin = (opts && opts.origin) || ''
178
- const path = (opts && opts.path) != null ? opts.path : '/'
179
- const urlStr = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
240
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
180
241
  httpLog('WRAPPER Dispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 80))
181
242
  const ctx = getRequestContext() || getLastJobContext()
182
243
  const otelCtx = getOtelTraceContext()
@@ -255,9 +316,7 @@ try {
255
316
  if (typeof origReq !== 'function') return d
256
317
  d.request = function (opts, callback) {
257
318
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
258
- const origin = (opts && opts.origin) || ''
259
- const pathPart = (opts && opts.path) != null ? opts.path : '/'
260
- const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
319
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
261
320
  httpLog('WRAPPER getGlobalDispatcher().request ' + methodStr + ' ' + urlStr.slice(0, 80))
262
321
  const ctx = getRequestContext ? getRequestContext() : (getLastJobContext ? getLastJobContext() : null)
263
322
  const otelCtx = getOtelTraceContext()
@@ -333,9 +392,7 @@ try {
333
392
  const HTTP_CLIENT_MODE = deps ? deps.HTTP_CLIENT_MODE : 'all'
334
393
  d.request = function (opts, callback) {
335
394
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
336
- const origin = (opts && opts.origin) || ''
337
- const pathPart = (opts && opts.path) != null ? opts.path : '/'
338
- const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
395
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
339
396
  httpLog('WRAPPER globalThis.dispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 80))
340
397
  const _dp = process.env.AZIFY_DEBUG_LOG_PATH
341
398
  if (_dp) { try { require('fs').appendFileSync(_dp, new Date().toISOString() + ' [AZIFY] WRAPPER globalDispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 100) + '\n') } catch (_) {} }
@@ -608,14 +665,12 @@ try {
608
665
  const RequestHandler = mod.exports.RequestHandler
609
666
  mod.exports = function wrappedApiRequest(opts, callback) {
610
667
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
611
- const urlPart = (opts && opts.origin) ? String(opts.origin).slice(0, 80) : (opts && opts.path) || ''
668
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
669
+ const urlPart = urlStr !== 'unknown' ? urlStr.slice(0, 80) : ((opts && opts.origin) ? String(opts.origin).slice(0, 80) : (opts && opts.path) || '')
612
670
  if (HTTP_DEBUG) try { process.stderr.write('[AZIFY-PATCH] api-request ' + methodStr + ' ' + urlPart + '\n') } catch (_) {}
613
671
  if (HTTP_VERBOSE || HTTP_DEBUG) {
614
672
  try { process.stderr.write('[AZIFY] HTTP wrapper api-request ' + methodStr + ' ' + urlPart + '\n') } catch (_) {}
615
673
  }
616
- const origin = (opts && opts.origin) || ''
617
- const pathPart = (opts && opts.path) != null ? opts.path : '/'
618
- const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
619
674
  httpLog('WRAPPER api-request ' + methodStr + ' ' + urlStr.slice(0, 80))
620
675
  const ctx = getRequestContext() || getLastJobContext()
621
676
  const otelCtx = getOtelTraceContext()
@@ -692,6 +747,7 @@ try {
692
747
  function patchGlobalFetch() {
693
748
  try {
694
749
  if (typeof globalThis.fetch !== 'function') return
750
+ if (globalThis.fetch.__azifyLoggerFetchPatched) return
695
751
  if (globalThis.fetch.__azifyPatched) return
696
752
  const deps = loadStoreAndSampling()
697
753
  if (!deps || deps.HTTP_CLIENT_MODE === 'off') return
package/register.js CHANGED
@@ -10,7 +10,12 @@ try {
10
10
  }
11
11
  const ModuleForRequire = require('module')
12
12
  const nodeRequireOriginal = ModuleForRequire.prototype.require
13
- const bunyan = require('bunyan')
13
+ let bunyan = null
14
+ try {
15
+ bunyan = require('bunyan')
16
+ } catch (_) {
17
+ /* bunyan é opcional; sem ele o patch Bunyan não aplica, mas HTTP/Pino/axios devem continuar */
18
+ }
14
19
  const createBunyanStream = require('./streams/bunyan')
15
20
  const { createHttpLoggerTransport } = require('./streams/httpQueue')
16
21
  const { getRequestContext } = require('./store')
@@ -145,12 +150,34 @@ try {
145
150
 
146
151
  const debug = process.env.AZIFY_LOGGER_DEBUG === '1'
147
152
  const httpVerbose = process.env.AZIFY_LOGGER_HTTP_VERBOSE === '1'
153
+
154
+ function extractUrlFromReqResMessage(msgStr) {
155
+ const s = String(msgStr)
156
+ const m1 = s.match(/\[(?:REQUEST|RESPONSE)\]\s+\S+\s+(https?:\/\/\S+)/i)
157
+ if (m1) return m1[1].replace(/[)\]}>.,;]+$/, '')
158
+ const m2 = s.match(/\[(?:REQUEST|RESPONSE)\]\s+\S+\s+(\S+)/i)
159
+ if (m2 && /^https?:\/\//i.test(m2[1])) return m2[1].replace(/[)\]}>.,;]+$/, '')
160
+ return null
161
+ }
162
+
163
+ const OPAQUE_NO_URL = 'https://opaque.invalid/azify-logger-no-url'
164
+
148
165
  function sendOutboundLog(level, message, meta) {
149
166
  try {
150
167
  const msgStr = String(message)
151
168
  const isReqRes = msgStr.includes('[REQUEST]') || msgStr.includes('[RESPONSE]')
169
+ if (isReqRes && meta && typeof meta === 'object') {
170
+ const u = meta.url
171
+ const badUrl =
172
+ u === 'unknown' ||
173
+ !String(u || '').trim() ||
174
+ String(u).toLowerCase() === 'unknown'
175
+ if (badUrl) {
176
+ const fromMsg = extractUrlFromReqResMessage(msgStr)
177
+ meta = fromMsg ? { ...meta, url: fromMsg } : { ...meta, url: OPAQUE_NO_URL }
178
+ }
179
+ }
152
180
  if (isReqRes && meta && meta.url && isLoggerApiCall({ url: meta.url })) return
153
- if (isReqRes && meta && (meta.url === 'unknown' || !String(meta.url || '').trim() || String(meta.url).toLowerCase() === 'unknown')) return
154
181
  const source = meta && meta.__source
155
182
  if (debug && source === 'pino-stdout') {
156
183
  if (debug) try { process.stderr.write('[azify-logger] SENDOUTBOUND_ENTER level=' + level + ' source=' + source + ' msg=' + msgStr.slice(0, 60) + '\n') } catch (_) {}
@@ -350,9 +377,16 @@ try {
350
377
 
351
378
  try {
352
379
  const target = new URL(candidate, normalizedLoggerOrigin)
353
- const targetOrigin = `${target.protocol}//${target.host}`
380
+ if (loggerEndpoint.hostname.toLowerCase() !== target.hostname.toLowerCase()) {
381
+ return false
382
+ }
383
+ const portLogger = loggerEndpoint.port || (loggerEndpoint.protocol === 'https:' ? '443' : '80')
384
+ const portTarget = target.port || (target.protocol === 'https:' ? '443' : '80')
385
+ if (String(portLogger) !== String(portTarget)) {
386
+ return false
387
+ }
354
388
  const targetPath = normalizePath(target.pathname)
355
- return targetOrigin === normalizedLoggerOrigin && targetPath === normalizedLoggerPath
389
+ return targetPath === normalizedLoggerPath || targetPath.startsWith(normalizedLoggerPath + '/')
356
390
  } catch (_) {
357
391
  if (typeof candidate === 'string') {
358
392
  const relativePath = normalizePath(candidate)
@@ -363,22 +397,24 @@ try {
363
397
  return false
364
398
  }
365
399
 
366
- const originalCreate = bunyan.createLogger
367
- bunyan.createLogger = function patchedCreateLogger(options) {
368
- const logger = originalCreate.call(bunyan, options)
369
- try {
370
- const level = process.env.AZIFY_LOG_LEVEL || (options && options.level) || 'info'
371
- const loggerUrl = process.env.AZIFY_LOGGER_URL
372
- const serviceName = process.env.APP_NAME || (options && options.name)
373
- const environment = process.env.NODE_ENV
374
-
375
- logger.addStream({
376
- level,
377
- type: 'raw',
378
- stream: createBunyanStream({ loggerUrl, serviceName, environment })
379
- })
380
- } catch (_) {}
381
- return logger
400
+ if (bunyan && typeof bunyan.createLogger === 'function') {
401
+ const originalCreate = bunyan.createLogger
402
+ bunyan.createLogger = function patchedCreateLogger(options) {
403
+ const logger = originalCreate.call(bunyan, options)
404
+ try {
405
+ const level = process.env.AZIFY_LOG_LEVEL || (options && options.level) || 'info'
406
+ const loggerUrl = process.env.AZIFY_LOGGER_URL
407
+ const serviceName = process.env.APP_NAME || (options && options.name)
408
+ const environment = process.env.NODE_ENV
409
+
410
+ logger.addStream({
411
+ level,
412
+ type: 'raw',
413
+ stream: createBunyanStream({ loggerUrl, serviceName, environment })
414
+ })
415
+ } catch (_) {}
416
+ return logger
417
+ }
382
418
  }
383
419
 
384
420
  const getOtelTraceContext = () => {
@@ -1552,6 +1588,35 @@ try {
1552
1588
  return url
1553
1589
  }
1554
1590
 
1591
+ const MAX_HTTP_BODY_CHARS = Math.min(
1592
+ Math.max(parseInt(String(process.env.AZIFY_LOGGER_HTTP_BODY_MAX_CHARS || '5000'), 10) || 5000, 100),
1593
+ 100000
1594
+ )
1595
+ function clipHttpLogString(s) {
1596
+ if (typeof s !== 'string') return s
1597
+ return s.length > MAX_HTTP_BODY_CHARS ? s.slice(0, MAX_HTTP_BODY_CHARS) : s
1598
+ }
1599
+ function stringifyHttpBodyForLog(value) {
1600
+ if (value == null) return null
1601
+ try {
1602
+ if (typeof value === 'string') {
1603
+ return clipHttpLogString(value)
1604
+ }
1605
+ if (Buffer.isBuffer(value)) {
1606
+ return clipHttpLogString(value.toString('utf8'))
1607
+ }
1608
+ if (value && typeof value.pipe === 'function') {
1609
+ return '[Stream]'
1610
+ }
1611
+ if (typeof value === 'object') {
1612
+ return clipHttpLogString(JSON.stringify(value))
1613
+ }
1614
+ return clipHttpLogString(String(value))
1615
+ } catch (_) {
1616
+ return '[unserializable]'
1617
+ }
1618
+ }
1619
+
1555
1620
  const patchInstance = (instance) => {
1556
1621
  if (instance.__azifyLoggerPatched) {
1557
1622
  return instance
@@ -1595,6 +1660,11 @@ try {
1595
1660
  headers: config.headers
1596
1661
  }
1597
1662
 
1663
+ const requestBodyString = stringifyHttpBodyForLog(config.data)
1664
+ if (requestBodyString != null) {
1665
+ requestMeta.requestBody = requestBodyString
1666
+ }
1667
+
1598
1668
  markSource(requestMeta, 'http-client')
1599
1669
  config.__azifyLogger = {
1600
1670
  meta: requestMeta,
@@ -1648,12 +1718,15 @@ try {
1648
1718
 
1649
1719
  const stringifyBody = (value) => {
1650
1720
  if (value == null) return null
1651
- if (typeof value === 'string') return value
1721
+ if (typeof value === 'string') return clipHttpLogString(value)
1722
+ if (Buffer.isBuffer(value)) {
1723
+ return clipHttpLogString(value.toString('utf8'))
1724
+ }
1652
1725
  try {
1653
- return JSON.stringify(value)
1726
+ return clipHttpLogString(JSON.stringify(value))
1654
1727
  } catch (_) {
1655
1728
  try {
1656
- return String(value)
1729
+ return clipHttpLogString(String(value))
1657
1730
  } catch (_) {
1658
1731
  return null
1659
1732
  }
@@ -1662,17 +1735,16 @@ try {
1662
1735
 
1663
1736
  if (marker && marker.meta) {
1664
1737
  duration = Number((performance.now() - marker.start).toFixed(2))
1665
- let responseBodyString = stringifyBody(response.data)
1666
- if (typeof responseBodyString === 'string' && responseBodyString.length > 5000) {
1667
- responseBodyString = responseBodyString.slice(0, 5000)
1668
- }
1738
+ const responseBodyString = stringifyBody(response.data)
1669
1739
  meta = {
1670
1740
  ...marker.meta,
1671
1741
  url: finalUrl,
1672
1742
  statusCode: response.status,
1673
1743
  responseTimeMs: duration,
1674
- responseHeaders: response.headers,
1675
- responseBody: responseBodyString
1744
+ responseHeaders: response.headers
1745
+ }
1746
+ if (responseBodyString != null) {
1747
+ meta.responseBody = responseBodyString
1676
1748
  }
1677
1749
  } else {
1678
1750
  const requestHeaders = response.config?.headers || {}
@@ -1681,10 +1753,7 @@ try {
1681
1753
  const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
1682
1754
  const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
1683
1755
  const requestId = requestHeaders['x-request-id'] || requestHeaders['X-Request-ID'] || ctx?.requestId || randomUUID()
1684
- let responseBodyString = stringifyBody(response.data)
1685
- if (typeof responseBodyString === 'string' && responseBodyString.length > 5000) {
1686
- responseBodyString = responseBodyString.slice(0, 5000)
1687
- }
1756
+ const responseBodyString = stringifyBody(response.data)
1688
1757
 
1689
1758
  meta = {
1690
1759
  traceId,
@@ -1695,8 +1764,10 @@ try {
1695
1764
  url: finalUrl,
1696
1765
  statusCode: response.status,
1697
1766
  responseTimeMs: duration,
1698
- responseHeaders: response.headers,
1699
- responseBody: responseBodyString
1767
+ responseHeaders: response.headers
1768
+ }
1769
+ if (responseBodyString != null) {
1770
+ meta.responseBody = responseBodyString
1700
1771
  }
1701
1772
  }
1702
1773
 
@@ -254,6 +254,9 @@ function sanitizePayload(payload) {
254
254
  if (sanitized.meta.responseBody) {
255
255
  sanitized.meta.responseBody = sanitizeBody(sanitized.meta.responseBody)
256
256
  }
257
+ if (sanitized.meta.requestBody) {
258
+ sanitized.meta.requestBody = sanitizeBody(sanitized.meta.requestBody)
259
+ }
257
260
  }
258
261
 
259
262
  return sanitized
package/server.js CHANGED
@@ -186,7 +186,11 @@ async function ensureIndexTemplate() {
186
186
  settings: {
187
187
  number_of_shards: 1,
188
188
  number_of_replicas: 0,
189
- 'index.refresh_interval': '5s'
189
+ 'index.refresh_interval': '5s',
190
+ 'index.mapping.total_fields.limit': Math.min(
191
+ Math.max(Number(process.env.AZIFY_LOGGER_OPENSEARCH_TOTAL_FIELDS_LIMIT) || 5000, 1000),
192
+ 20000
193
+ )
190
194
  },
191
195
  mappings: {
192
196
  properties: {
@@ -2327,14 +2331,22 @@ function _drainOpenSearchQueue() {
2327
2331
  if (_openSearchCircuitRetryTimer.unref) _openSearchCircuitRetryTimer.unref()
2328
2332
  }
2329
2333
  }
2330
- if (_openSearchFailuresInRow <= 2) {
2331
- const status = error?.response?.status
2332
- const errorMsg = error?.response?.data?.error?.reason ||
2333
- error?.response?.data?.message ||
2334
- error?.message ||
2335
- 'Erro desconhecido'
2336
- console.error('❌ Falha ao enviar log para OpenSearch', { status, message: errorMsg })
2337
- if (error?.code) console.error('⚙️ Código de erro:', error.code)
2334
+ const status = error?.response?.status
2335
+ const rawMsg = error?.response?.data?.error?.reason ||
2336
+ error?.response?.data?.error?.type ||
2337
+ error?.response?.data?.message ||
2338
+ error?.message ||
2339
+ 'Erro desconhecido'
2340
+ const errorMsg = typeof rawMsg === 'string' ? rawMsg.slice(0, 800) : String(rawMsg).slice(0, 800)
2341
+ const logPayload = {
2342
+ index: job.indexName,
2343
+ serviceName: job.serviceName,
2344
+ status,
2345
+ message: errorMsg
2346
+ }
2347
+ console.error('❌ Falha ao enviar log para OpenSearch', logPayload)
2348
+ if (error?.code && _openSearchFailuresInRow <= 10) {
2349
+ console.error('⚙️ Código de erro:', error.code)
2338
2350
  }
2339
2351
  })
2340
2352
  .finally(() => {