@skediprof/telemetry 1.0.0 → 1.1.1
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 +200 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +17 -1
- package/dist/init.js.map +1 -1
- package/dist/logging.d.ts +24 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +62 -0
- package/dist/logging.js.map +1 -0
- package/dist/shutdown.d.ts.map +1 -1
- package/dist/shutdown.js +2 -0
- package/dist/shutdown.js.map +1 -1
- package/package.json +4 -1
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# @skediprof/telemetry
|
|
2
|
+
|
|
3
|
+
Biblioteca compartilhada de OpenTelemetry para os microserviços da Skedi.
|
|
4
|
+
Encapsula o SDK do OTel e exporta **traces + métricas** via OTLP/HTTP
|
|
5
|
+
diretamente para o New Relic (sem OTel Collector).
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @skediprof/telemetry
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> Requer **Node.js >= 18**.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Como os microserviços usam
|
|
16
|
+
|
|
17
|
+
Cada serviço escolhe **um** dos dois padrões abaixo, conforme o runtime.
|
|
18
|
+
|
|
19
|
+
### 1. Cloud Run / servidor persistente (`runtime: 'server'`)
|
|
20
|
+
|
|
21
|
+
Chame `initTelemetry()` **no topo absoluto** do entrypoint, **antes** de
|
|
22
|
+
qualquer `import` de Express, Firebase ou bibliotecas HTTP. A
|
|
23
|
+
auto-instrumentação funciona via monkey-patching — se os módulos forem
|
|
24
|
+
importados antes, os patches não se aplicam.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// index.ts — PRIMEIRA coisa do arquivo
|
|
28
|
+
import { initTelemetry } from '@skediprof/telemetry';
|
|
29
|
+
|
|
30
|
+
initTelemetry({
|
|
31
|
+
serviceName: 'skedi-students-imdb',
|
|
32
|
+
serviceVersion: process.env.SERVICE_VERSION,
|
|
33
|
+
runtime: 'server',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// só DEPOIS importe o resto
|
|
37
|
+
import express from 'express';
|
|
38
|
+
import { shutdownTelemetry } from '@skediprof/telemetry';
|
|
39
|
+
|
|
40
|
+
const app = express();
|
|
41
|
+
// ... suas rotas ...
|
|
42
|
+
app.listen(8080);
|
|
43
|
+
|
|
44
|
+
// flush dos dados pendentes no encerramento gracioso
|
|
45
|
+
process.on('SIGTERM', async () => {
|
|
46
|
+
await shutdownTelemetry();
|
|
47
|
+
process.exit(0);
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Cloud Functions (`runtime: 'function'`)
|
|
52
|
+
|
|
53
|
+
Use o wrapper `withTelemetry()`. Ele inicializa no cold start e faz **flush
|
|
54
|
+
após cada invocação** (crítico em Functions — sem isso, dados se perdem
|
|
55
|
+
quando a instância congela).
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { withTelemetry } from '@skediprof/telemetry';
|
|
59
|
+
|
|
60
|
+
export const handler = withTelemetry(
|
|
61
|
+
{ serviceName: 'ms-skedi-notifications', runtime: 'function' },
|
|
62
|
+
async (req, res) => {
|
|
63
|
+
// ... sua lógica ...
|
|
64
|
+
res.send('ok');
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Em JavaScript puro (ex: `whatsapp-bot-skedi`) o uso é idêntico com `require`:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
const { withTelemetry } = require('@skediprof/telemetry');
|
|
73
|
+
|
|
74
|
+
exports.handler = withTelemetry(
|
|
75
|
+
{ serviceName: 'whatsapp-bot-skedi', runtime: 'function' },
|
|
76
|
+
async (req, res) => { /* ... */ }
|
|
77
|
+
);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Configuração (`TelemetryConfig`)
|
|
83
|
+
|
|
84
|
+
| Campo | Tipo | Default | Descrição |
|
|
85
|
+
|-------|------|---------|-----------|
|
|
86
|
+
| `serviceName` | `string` | **(obrigatório)** | Nome do serviço; vira o *entity name* no New Relic. |
|
|
87
|
+
| `serviceVersion` | `string` | `'0.0.0'` | Versão do serviço. |
|
|
88
|
+
| `environment` | `string` | `NODE_ENV` ou `'production'` | `production` \| `staging` \| `development`. |
|
|
89
|
+
| `runtime` | `'server' \| 'function'` | `'server'` | Estratégia de flush/shutdown. |
|
|
90
|
+
| `apiKey` | `string` | `NEW_RELIC_LICENSE_KEY` | License key do New Relic. |
|
|
91
|
+
| `otlpEndpoint` | `string` | `https://otlp.nr-data.net:4318` | Endpoint OTLP. |
|
|
92
|
+
| `metricExportInterval` | `number` | `60000` (server) / `10000` (function) | Intervalo de export de métricas (ms). |
|
|
93
|
+
| `autoInstrumentHttp` | `boolean` | `true` | Auto-instrumenta HTTP/Express. |
|
|
94
|
+
| `autoInstrumentFirestore` | `boolean` | `true` | Auto-instrumenta Firestore/gRPC. |
|
|
95
|
+
|
|
96
|
+
### Variáveis de ambiente
|
|
97
|
+
|
|
98
|
+
| Variável | Efeito |
|
|
99
|
+
|----------|--------|
|
|
100
|
+
| `NEW_RELIC_LICENSE_KEY` | License key usada se `apiKey` não for passado. **Sem chave = telemetria desligada** (apenas avisa, não quebra). |
|
|
101
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Sobrescreve o endpoint OTLP. |
|
|
102
|
+
| `TELEMETRY_ENABLED=false` | Desativa **toda** a telemetria (nenhum SDK é carregado). |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Métricas customizadas
|
|
107
|
+
|
|
108
|
+
Convenção de nomes: **`skedi.<domínio>.<métrica>`** (ex: `skedi.classes.created`).
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { createCounter, createHistogram, createGauge } from '@skediprof/telemetry';
|
|
112
|
+
|
|
113
|
+
const classesCreated = createCounter(
|
|
114
|
+
'skedi.classes.created',
|
|
115
|
+
'Total de aulas criadas'
|
|
116
|
+
);
|
|
117
|
+
classesCreated.add(1, { tenant: 'acme' });
|
|
118
|
+
|
|
119
|
+
const apiLatency = createHistogram(
|
|
120
|
+
'skedi.api.latency',
|
|
121
|
+
'Latência das requisições da API',
|
|
122
|
+
'ms'
|
|
123
|
+
);
|
|
124
|
+
apiLatency.record(123, { route: '/students' });
|
|
125
|
+
|
|
126
|
+
const queueDepth = createGauge(
|
|
127
|
+
'skedi.queue.depth',
|
|
128
|
+
'Mensagens pendentes na fila'
|
|
129
|
+
);
|
|
130
|
+
queueDepth.addCallback((result) => result.observe(getQueueSize()));
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Correlação de logs com traces
|
|
136
|
+
|
|
137
|
+
Use `getTraceContext()` para injetar `traceId`/`spanId` nos logs (Winston/Pino),
|
|
138
|
+
ligando cada linha de log ao trace correspondente no New Relic.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { getTraceContext } from '@skediprof/telemetry';
|
|
142
|
+
|
|
143
|
+
const ctx = getTraceContext(); // null se não houver span ativo
|
|
144
|
+
logger.info('processando aula', {
|
|
145
|
+
...(ctx && { 'trace.id': ctx.traceId, 'span.id': ctx.spanId }),
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Encerramento (flush)
|
|
152
|
+
|
|
153
|
+
`shutdownTelemetry()` faz flush de spans/métricas pendentes e encerra os
|
|
154
|
+
exporters (timeout de 5s). É chamado automaticamente pelo `withTelemetry()`
|
|
155
|
+
em Cloud Functions; em servidores, chame-o no handler de `SIGTERM`/`SIGINT`.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { shutdownTelemetry } from '@skediprof/telemetry';
|
|
159
|
+
|
|
160
|
+
process.on('SIGTERM', async () => {
|
|
161
|
+
await shutdownTelemetry();
|
|
162
|
+
process.exit(0);
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Consumidores
|
|
169
|
+
|
|
170
|
+
| Serviço | Runtime | Linguagem | `runtime` |
|
|
171
|
+
|---------|---------|-----------|-----------|
|
|
172
|
+
| `zico` | Cloud Functions | TypeScript | `'function'` |
|
|
173
|
+
| `skedi-students-imdb` | Cloud Run | TypeScript | `'server'` |
|
|
174
|
+
| `ms-skedi-notifications` | Cloud Functions | TypeScript | `'function'` |
|
|
175
|
+
| `whatsapp-bot-skedi` | Cloud Functions | JavaScript | `'function'` |
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## API pública
|
|
180
|
+
|
|
181
|
+
| Export | Descrição |
|
|
182
|
+
|--------|-----------|
|
|
183
|
+
| `initTelemetry(config)` | Inicializa traces + métricas. Idempotente. |
|
|
184
|
+
| `withTelemetry(config, handler)` | Wrapper para Cloud Functions (init + flush por invocação). |
|
|
185
|
+
| `createCounter(name, description)` | Cria um contador monotônico. |
|
|
186
|
+
| `createHistogram(name, description, unit?)` | Cria um histograma. |
|
|
187
|
+
| `createGauge(name, description)` | Cria um gauge observável (via callback). |
|
|
188
|
+
| `getTraceContext()` | Retorna `{ traceId, spanId, traceFlags }` do span ativo, ou `null`. |
|
|
189
|
+
| `shutdownTelemetry()` | Flush + encerramento dos exporters. |
|
|
190
|
+
| `TelemetryConfig`, `TraceContext` | Tipos TypeScript exportados. |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Versionamento
|
|
195
|
+
|
|
196
|
+
Versões seguem semver e são publicadas no npm. Cada consumidor fixa uma versão:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{ "dependencies": { "@skediprof/telemetry": "^1.0.0" } }
|
|
200
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { initTelemetry, withTelemetry } from './init';
|
|
2
2
|
export { createCounter, createHistogram, createGauge } from './metrics';
|
|
3
|
+
export { emitLogRecord } from './logging';
|
|
3
4
|
export { getTraceContext } from './context';
|
|
4
5
|
export { shutdownTelemetry } from './shutdown';
|
|
5
6
|
export type { TelemetryConfig, TraceContext } from './types';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.shutdownTelemetry = exports.getTraceContext = exports.createGauge = exports.createHistogram = exports.createCounter = exports.withTelemetry = exports.initTelemetry = void 0;
|
|
3
|
+
exports.shutdownTelemetry = exports.getTraceContext = exports.emitLogRecord = exports.createGauge = exports.createHistogram = exports.createCounter = exports.withTelemetry = exports.initTelemetry = void 0;
|
|
4
4
|
var init_1 = require("./init");
|
|
5
5
|
Object.defineProperty(exports, "initTelemetry", { enumerable: true, get: function () { return init_1.initTelemetry; } });
|
|
6
6
|
Object.defineProperty(exports, "withTelemetry", { enumerable: true, get: function () { return init_1.withTelemetry; } });
|
|
@@ -8,6 +8,8 @@ var metrics_1 = require("./metrics");
|
|
|
8
8
|
Object.defineProperty(exports, "createCounter", { enumerable: true, get: function () { return metrics_1.createCounter; } });
|
|
9
9
|
Object.defineProperty(exports, "createHistogram", { enumerable: true, get: function () { return metrics_1.createHistogram; } });
|
|
10
10
|
Object.defineProperty(exports, "createGauge", { enumerable: true, get: function () { return metrics_1.createGauge; } });
|
|
11
|
+
var logging_1 = require("./logging");
|
|
12
|
+
Object.defineProperty(exports, "emitLogRecord", { enumerable: true, get: function () { return logging_1.emitLogRecord; } });
|
|
11
13
|
var context_1 = require("./context");
|
|
12
14
|
Object.defineProperty(exports, "getTraceContext", { enumerable: true, get: function () { return context_1.getTraceContext; } });
|
|
13
15
|
var shutdown_1 = require("./shutdown");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+BAAsD;AAA7C,qGAAA,aAAa,OAAA;AAAE,qGAAA,aAAa,OAAA;AACrC,qCAAwE;AAA/D,wGAAA,aAAa,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,sGAAA,WAAW,OAAA;AACpD,qCAA4C;AAAnC,0GAAA,eAAe,OAAA;AACxB,uCAA+C;AAAtC,6GAAA,iBAAiB,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+BAAsD;AAA7C,qGAAA,aAAa,OAAA;AAAE,qGAAA,aAAa,OAAA;AACrC,qCAAwE;AAA/D,wGAAA,aAAa,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,sGAAA,WAAW,OAAA;AACpD,qCAA0C;AAAjC,wGAAA,aAAa,OAAA;AACtB,qCAA4C;AAAnC,0GAAA,eAAe,OAAA;AACxB,uCAA+C;AAAtC,6GAAA,iBAAiB,OAAA"}
|
package/dist/init.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TelemetryConfig } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* Inicializa toda a stack de telemetria (traces + metrics).
|
|
3
|
+
* Inicializa toda a stack de telemetria (traces + metrics + logs).
|
|
4
4
|
* DEVE ser chamado ANTES de qualquer import de framework (Express, Firebase).
|
|
5
5
|
*/
|
|
6
6
|
export declare function initTelemetry(config: TelemetryConfig): void;
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAM/C;;;GAGG;AAIH,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAqF3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,SAAS,OAAO,EAAE,EACvB,OAAO,EAEP,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,GAC5C,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,CAetC"}
|
package/dist/init.js
CHANGED
|
@@ -5,7 +5,9 @@ exports.withTelemetry = withTelemetry;
|
|
|
5
5
|
const sdk_node_1 = require("@opentelemetry/sdk-node");
|
|
6
6
|
const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
|
|
7
7
|
const exporter_metrics_otlp_http_1 = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
8
|
+
const exporter_logs_otlp_http_1 = require("@opentelemetry/exporter-logs-otlp-http");
|
|
8
9
|
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
|
10
|
+
const sdk_logs_1 = require("@opentelemetry/sdk-logs");
|
|
9
11
|
const resources_1 = require("@opentelemetry/resources");
|
|
10
12
|
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
|
|
11
13
|
const instrumentations_1 = require("./instrumentations");
|
|
@@ -15,7 +17,7 @@ const shutdown_1 = require("./shutdown");
|
|
|
15
17
|
// on cold start. Do not switch to gRPC exporter. See ADR-002.
|
|
16
18
|
const DEFAULT_ENDPOINT = 'https://otlp.nr-data.net:4318';
|
|
17
19
|
/**
|
|
18
|
-
* Inicializa toda a stack de telemetria (traces + metrics).
|
|
20
|
+
* Inicializa toda a stack de telemetria (traces + metrics + logs).
|
|
19
21
|
* DEVE ser chamado ANTES de qualquer import de framework (Express, Firebase).
|
|
20
22
|
*/
|
|
21
23
|
// AIDEV: initTelemetry MUST be called before any Express/Firebase import.
|
|
@@ -62,10 +64,24 @@ function initTelemetry(config) {
|
|
|
62
64
|
exporter: metricExporter,
|
|
63
65
|
exportIntervalMillis: metricInterval,
|
|
64
66
|
});
|
|
67
|
+
// AIDEV: Third OTel pillar — logs. Without this, logger.error() calls only
|
|
68
|
+
// go to GCP Cloud Logging (console.error) but never reach New Relic,
|
|
69
|
+
// causing the "Logs ERROR" dashboard widget to show 0 while gcloud shows dozens.
|
|
70
|
+
const logExporter = new exporter_logs_otlp_http_1.OTLPLogExporter({
|
|
71
|
+
url: `${endpoint}/v1/logs`,
|
|
72
|
+
headers,
|
|
73
|
+
});
|
|
74
|
+
// AIDEV: For Cloud Functions (runtime='function'), use smaller batch + shorter
|
|
75
|
+
// delay to ensure logs are flushed before the function terminates.
|
|
76
|
+
const logProcessor = new sdk_logs_1.BatchLogRecordProcessor(logExporter, {
|
|
77
|
+
scheduledDelayMillis: runtime === 'function' ? 1000 : 5000,
|
|
78
|
+
maxExportBatchSize: runtime === 'function' ? 50 : 512,
|
|
79
|
+
});
|
|
65
80
|
const sdk = new sdk_node_1.NodeSDK({
|
|
66
81
|
resource,
|
|
67
82
|
traceExporter,
|
|
68
83
|
metricReader,
|
|
84
|
+
logRecordProcessors: [logProcessor],
|
|
69
85
|
instrumentations: (0, instrumentations_1.buildInstrumentations)(config),
|
|
70
86
|
});
|
|
71
87
|
sdk.start();
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";;AA4BA,sCAqFC;AAKD,sCAqBC;AA3ID,sDAAkD;AAClD,sFAA4E;AAC5E,0FAA+E;AAC/E,oFAAyE;AACzE,4DAA2E;AAC3E,sDAAkE;AAClE,wDAAkE;AAClE,8EAI6C;AAC7C,yDAA2D;AAC3D,qCAAiE;AACjE,yCAA+C;AAG/C,iFAAiF;AACjF,8DAA8D;AAC9D,MAAM,gBAAgB,GAAG,+BAA+B,CAAC;AAEzD;;;GAGG;AACH,0EAA0E;AAC1E,uEAAuE;AACvE,2CAA2C;AAC3C,SAAgB,aAAa,CAAC,MAAuB;IACnD,IAAI,IAAA,sBAAa,GAAE,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;QACtE,OAAO;IACT,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,OAAO,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,IAAA,uBAAc,EAAC,IAAI,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GACf,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,YAAY,CAAC;IAC7D,MAAM,QAAQ,GACZ,OAAO,CAAC,GAAG,CAAC,2BAA2B;QACvC,MAAM,CAAC,YAAY;QACnB,gBAAgB,CAAC;IACnB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,QAAQ,CAAC;IAC3C,MAAM,cAAc,GAClB,MAAM,CAAC,oBAAoB,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAE1E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CACV,kHAAkH,CACnH,CAAC;QACF,IAAA,uBAAc,EAAC,IAAI,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,iFAAiF;IACjF,MAAM,OAAO,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAEtC,MAAM,QAAQ,GAAG,IAAA,kCAAsB,EAAC;QACtC,CAAC,wCAAiB,CAAC,EAAE,MAAM,CAAC,WAAW;QACvC,CAAC,2CAAoB,CAAC,EAAE,MAAM,CAAC,cAAc,IAAI,OAAO;QACxD,CAAC,uDAAgC,CAAC,EAAE,WAAW;KAChD,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,IAAI,4CAAiB,CAAC;QAC1C,GAAG,EAAE,GAAG,QAAQ,YAAY;QAC5B,OAAO;KACR,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,IAAI,+CAAkB,CAAC;QAC5C,GAAG,EAAE,GAAG,QAAQ,aAAa;QAC7B,OAAO;KACR,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,IAAI,2CAA6B,CAAC;QACrD,QAAQ,EAAE,cAAc;QACxB,oBAAoB,EAAE,cAAc;KACrC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,qEAAqE;IACrE,iFAAiF;IACjF,MAAM,WAAW,GAAG,IAAI,yCAAe,CAAC;QACtC,GAAG,EAAE,GAAG,QAAQ,UAAU;QAC1B,OAAO;KACR,CAAC,CAAC;IAEH,+EAA+E;IAC/E,mEAAmE;IACnE,MAAM,YAAY,GAAG,IAAI,kCAAuB,CAAC,WAAW,EAAE;QAC5D,oBAAoB,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QAC1D,kBAAkB,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG;KACtD,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,IAAI,kBAAO,CAAC;QACtB,QAAQ;QACR,aAAa;QACb,YAAY;QACZ,mBAAmB,EAAE,CAAC,YAAY,CAAC;QACnC,gBAAgB,EAAE,IAAA,wCAAqB,EAAC,MAAM,CAAC;KAChD,CAAC,CAAC;IAEH,GAAG,CAAC,KAAK,EAAE,CAAC;IACZ,IAAA,eAAM,EAAC,GAAG,CAAC,CAAC;IACZ,IAAA,uBAAc,EAAC,IAAI,CAAC,CAAC;IAErB,OAAO,CAAC,GAAG,CACT,2CAA2C,MAAM,CAAC,WAAW,MAAM,OAAO,IAAI,WAAW,GAAG,CAC7F,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAI3B,MAAuB,EACvB,OAA6C;IAE7C,MAAM,cAAc,GAAoB;QACtC,GAAG,MAAM;QACT,OAAO,EAAE,UAAU;QACnB,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,IAAI,KAAK;KAC3D,CAAC;IAEF,OAAO,KAAK,EAAE,GAAG,IAAW,EAAoB,EAAE;QAChD,aAAa,CAAC,cAAc,CAAC,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAChC,CAAC;gBAAS,CAAC;YACT,MAAM,IAAA,4BAAiB,GAAE,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Severity levels matching Winston/console conventions.
|
|
3
|
+
* Maps to OTel SeverityNumber for proper level filtering in New Relic.
|
|
4
|
+
*/
|
|
5
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
6
|
+
/**
|
|
7
|
+
* Emit a log record via OTel Logs API → OTLP/HTTP → New Relic.
|
|
8
|
+
*
|
|
9
|
+
* This is the bridge between the app's logger (Winston/console) and OTel.
|
|
10
|
+
* Call this alongside console.log/error — it doesn't replace stdout logging
|
|
11
|
+
* (which GCP Cloud Logging still captures), it adds the New Relic pipeline.
|
|
12
|
+
*
|
|
13
|
+
* @param level - Log severity (debug, info, warn, error)
|
|
14
|
+
* @param message - Log message body
|
|
15
|
+
* @param attributes - Optional structured attributes (key-value pairs)
|
|
16
|
+
*/
|
|
17
|
+
export declare function emitLogRecord(level: LogLevel, message: string, attributes?: Record<string, string | number | boolean>): void;
|
|
18
|
+
/**
|
|
19
|
+
* Reset cached logger (needed for Cloud Functions warm-start re-init).
|
|
20
|
+
* Called by shutdownTelemetry().
|
|
21
|
+
*/
|
|
22
|
+
export declare function resetLoggerCache(): void;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,KAAK,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AASpD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GACrD,IAAI,CAqBN;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.emitLogRecord = emitLogRecord;
|
|
4
|
+
exports.resetLoggerCache = resetLoggerCache;
|
|
5
|
+
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
6
|
+
const context_1 = require("./context");
|
|
7
|
+
const LOGGER_NAME = '@skediprof/telemetry';
|
|
8
|
+
const LOGGER_VERSION = '1.0.0';
|
|
9
|
+
// AIDEV: Cache the OTel logger instance. It's safe to cache because the
|
|
10
|
+
// LoggerProvider is set globally by NodeSDK.start() and doesn't change.
|
|
11
|
+
let cachedLogger = null;
|
|
12
|
+
function getOTelLogger() {
|
|
13
|
+
if (!cachedLogger) {
|
|
14
|
+
cachedLogger = api_logs_1.logs.getLoggerProvider().getLogger(LOGGER_NAME, LOGGER_VERSION);
|
|
15
|
+
}
|
|
16
|
+
return cachedLogger;
|
|
17
|
+
}
|
|
18
|
+
const SEVERITY_MAP = {
|
|
19
|
+
debug: { number: api_logs_1.SeverityNumber.DEBUG, text: 'DEBUG' },
|
|
20
|
+
info: { number: api_logs_1.SeverityNumber.INFO, text: 'INFO' },
|
|
21
|
+
warn: { number: api_logs_1.SeverityNumber.WARN, text: 'WARN' },
|
|
22
|
+
error: { number: api_logs_1.SeverityNumber.ERROR, text: 'ERROR' },
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Emit a log record via OTel Logs API → OTLP/HTTP → New Relic.
|
|
26
|
+
*
|
|
27
|
+
* This is the bridge between the app's logger (Winston/console) and OTel.
|
|
28
|
+
* Call this alongside console.log/error — it doesn't replace stdout logging
|
|
29
|
+
* (which GCP Cloud Logging still captures), it adds the New Relic pipeline.
|
|
30
|
+
*
|
|
31
|
+
* @param level - Log severity (debug, info, warn, error)
|
|
32
|
+
* @param message - Log message body
|
|
33
|
+
* @param attributes - Optional structured attributes (key-value pairs)
|
|
34
|
+
*/
|
|
35
|
+
function emitLogRecord(level, message, attributes) {
|
|
36
|
+
const severity = SEVERITY_MAP[level];
|
|
37
|
+
const logger = getOTelLogger();
|
|
38
|
+
// Build attributes, injecting trace context for log↔trace correlation
|
|
39
|
+
// AIDEV: FIX (11/06/2026) - Add 'level' attribute so New Relic maps it
|
|
40
|
+
// to the same field used by APM classic agents. Without this, OTel logs
|
|
41
|
+
// only populate severity.text and the dashboard WHERE level='error' misses them.
|
|
42
|
+
const attrs = { level, ...attributes };
|
|
43
|
+
const traceCtx = (0, context_1.getTraceContext)();
|
|
44
|
+
if (traceCtx) {
|
|
45
|
+
attrs['trace.id'] = traceCtx.traceId;
|
|
46
|
+
attrs['span.id'] = traceCtx.spanId;
|
|
47
|
+
}
|
|
48
|
+
logger.emit({
|
|
49
|
+
severityNumber: severity.number,
|
|
50
|
+
severityText: severity.text,
|
|
51
|
+
body: message,
|
|
52
|
+
attributes: attrs,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reset cached logger (needed for Cloud Functions warm-start re-init).
|
|
57
|
+
* Called by shutdownTelemetry().
|
|
58
|
+
*/
|
|
59
|
+
function resetLoggerCache() {
|
|
60
|
+
cachedLogger = null;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":";;AA0CA,sCAyBC;AAMD,4CAEC;AA3ED,sDAA+D;AAE/D,uCAA4C;AAE5C,MAAM,WAAW,GAAG,sBAAsB,CAAC;AAC3C,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,wEAAwE;AACxE,wEAAwE;AACxE,IAAI,YAAY,GAAkB,IAAI,CAAC;AAEvC,SAAS,aAAa;IACpB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,eAAI,CAAC,iBAAiB,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAQD,MAAM,YAAY,GAA+D;IAC/E,KAAK,EAAE,EAAE,MAAM,EAAE,yBAAc,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE;IACtD,IAAI,EAAG,EAAE,MAAM,EAAE,yBAAc,CAAC,IAAI,EAAG,IAAI,EAAE,MAAM,EAAE;IACrD,IAAI,EAAG,EAAE,MAAM,EAAE,yBAAc,CAAC,IAAI,EAAG,IAAI,EAAE,MAAM,EAAE;IACrD,KAAK,EAAE,EAAE,MAAM,EAAE,yBAAc,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE;CACvD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,SAAgB,aAAa,CAC3B,KAAe,EACf,OAAe,EACf,UAAsD;IAEtD,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,sEAAsE;IACtE,uEAAuE;IACvE,wEAAwE;IACxE,iFAAiF;IACjF,MAAM,KAAK,GAA8C,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAAC;IAClF,MAAM,QAAQ,GAAG,IAAA,yBAAe,GAAE,CAAC;IACnC,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC;QACrC,KAAK,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC;QACV,cAAc,EAAE,QAAQ,CAAC,MAAM;QAC/B,YAAY,EAAE,QAAQ,CAAC,IAAI;QAC3B,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,KAAK;KAClB,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB;IAC9B,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC"}
|
package/dist/shutdown.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAKH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuBvD"}
|
package/dist/shutdown.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.shutdownTelemetry = shutdownTelemetry;
|
|
4
4
|
const _state_1 = require("./_state");
|
|
5
|
+
const logging_1 = require("./logging");
|
|
5
6
|
const SHUTDOWN_TIMEOUT_MS = 5000;
|
|
6
7
|
/**
|
|
7
8
|
* Faz flush de todos os spans/metricas pendentes e encerra exporters.
|
|
@@ -30,6 +31,7 @@ async function shutdownTelemetry() {
|
|
|
30
31
|
finally {
|
|
31
32
|
(0, _state_1.setSDK)(null);
|
|
32
33
|
(0, _state_1.setInitialized)(false);
|
|
34
|
+
(0, logging_1.resetLoggerCache)();
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
//# sourceMappingURL=shutdown.js.map
|
package/dist/shutdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":";;AAeA,8CAuBC;AAtCD,qCAAyE;AACzE,uCAA6C;AAE7C,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC;;;;;GAKG;AACH,8EAA8E;AAC9E,iFAAiF;AACjF,iFAAiF;AACjF,0CAA0C;AACnC,KAAK,UAAU,iBAAiB;IACrC,MAAM,GAAG,GAAG,IAAA,eAAM,GAAE,CAAC;IACrB,IAAI,CAAC,GAAG,IAAI,CAAC,IAAA,sBAAa,GAAE,EAAE,CAAC;QAC7B,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,CAAC;YACjB,GAAG,CAAC,QAAQ,EAAE;YACd,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC9B,UAAU,CACR,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,EAC7C,mBAAmB,CACpB,CACF;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAC;IAC/D,CAAC;YAAS,CAAC;QACT,IAAA,eAAM,EAAC,IAAI,CAAC,CAAC;QACb,IAAA,uBAAc,EAAC,KAAK,CAAC,CAAC;QACtB,IAAA,0BAAgB,GAAE,CAAC;IACrB,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skediprof/telemetry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Shared OpenTelemetry library for Skedi microservices",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,12 +18,15 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@opentelemetry/api": "^1.9.0",
|
|
21
|
+
"@opentelemetry/api-logs": "^0.218.0",
|
|
22
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.218.0",
|
|
21
23
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.218.0",
|
|
22
24
|
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
|
|
23
25
|
"@opentelemetry/instrumentation-express": "^0.66.0",
|
|
24
26
|
"@opentelemetry/instrumentation-grpc": "^0.218.0",
|
|
25
27
|
"@opentelemetry/instrumentation-http": "^0.218.0",
|
|
26
28
|
"@opentelemetry/resources": "^2.7.1",
|
|
29
|
+
"@opentelemetry/sdk-logs": "^0.218.0",
|
|
27
30
|
"@opentelemetry/sdk-metrics": "^2.7.1",
|
|
28
31
|
"@opentelemetry/sdk-node": "^0.218.0",
|
|
29
32
|
"@opentelemetry/sdk-trace-node": "^2.7.1",
|