@qlibs/apm 0.1.1 → 0.3.0

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 CHANGED
@@ -70,17 +70,124 @@ dependency of this package.
70
70
 
71
71
  | Variable | Default | Purpose |
72
72
  |---|---|---|
73
- | `APM_ENABLED` | `true` | Master switch. `false` fully disables tracing + remote log transports (console logging still works). |
73
+ | `APM_ENABLED` | `true` | `false` disables tracing. Log transports are controlled only by `APM_LOG_TRANSPORT`. |
74
74
  | `APM_SERVICE_NAME` | `ats-be` | Service name shown in traces/logs — set per app, e.g. `my-app-api`. |
75
- | `APM_ENVIRONMENT` | `NODE_ENV` | `deployment.environment` resource attribute. |
75
+ | `APM_ENVIRONMENT` | `NODE_ENV`, else `production` | `deployment.environment` resource attribute. Also drives the log level/format defaults below. Unset `NODE_ENV` is treated as `production` (never as local). |
76
76
  | `OTEL_EXPORTER_OTLP_ENDPOINT` | _(empty)_ | Base OTLP endpoint. Empty = tracing disabled. |
77
77
  | `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` | `http/protobuf` (port 4318 typically) or `grpc` (port 4317). Must match what your collector listens on. |
78
78
  | `OTEL_EXPORTER_OTLP_HEADERS` | _(empty)_ | Comma-separated `key=value` pairs, e.g. ingestion keys / auth tokens. |
79
- | `LOG_LEVEL` | `info` (`debug` locally) | Winston log level. |
80
- | `APM_LOG_FORMAT` | `pretty` locally, `json` otherwise | `pretty` = colorized console; `json` = structured stdout. |
79
+ | `LOG_LEVEL` | `info` (`debug` when env is `local`) | Winston log level (`error`, `warn`, `info`, `http`, `verbose`, `debug`). At `info`, Nest `verbose`/`debug` logs are dropped — set `verbose` or `debug` to see them. HTTP access logs and Nest `fatal` (logged as `error` with `fatal: true`) are always visible at `info`. On the console, `error`/`warn` go to stderr and everything else to stdout. |
80
+ | `APM_LOG_FORMAT` | `pretty` for `local`/`development`, `json` otherwise | `pretty` = colorized console; `json` = structured stdout. |
81
81
  | `APM_LOG_TRANSPORT` | `stdout` | Comma list of `stdout`, `elasticsearch`, `otlp`. Multiple can be active at once. |
82
- | `ELASTICSEARCH_NODE` / `_USERNAME` / `_PASSWORD` / `_INDEX` | _(empty)_ | Only used when `APM_LOG_TRANSPORT` includes `elasticsearch`. |
83
- | `APM_HTTP_IGNORE_PATHS` | `/health,/favicon.ico` | Comma list of path prefixes excluded from the HTTP access log. |
82
+ | `ELASTICSEARCH_NODE` / `_USERNAME` / `_PASSWORD` / `_INDEX` | _(empty)_ | Only used when `APM_LOG_TRANSPORT` includes `elasticsearch`. While ES is unreachable at most 1000 log lines are buffered (the rest are dropped) and each is retried 5 times. Shipping via stdout + Filebeat/Fluent Bit is still preferred in production. |
83
+ | `APM_HTTP_IGNORE_PATHS` | `/health,/favicon.ico` | Comma list of path prefixes excluded from the HTTP access log and from tracing. |
84
+ | `OTEL_NODE_ENABLED_INSTRUMENTATIONS` | `http,undici,express,nestjs-core,pg,ioredis,redis,amqplib` | Which auto-instrumentations to load (names without the `@opentelemetry/instrumentation-` prefix). Add e.g. `mongodb`, `kafkajs` if your app needs them. |
85
+ | `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG` | `parentbased_always_on` | Standard OTel sampling. For high-traffic services use `parentbased_traceidratio` with e.g. `0.1` (10%). |
86
+
87
+ ## Example configurations
88
+
89
+ `LOG_LEVEL` is a threshold: it keeps that level and everything more severe
90
+ (`error` < `warn` < `info` < `http` < `verbose` < `debug` < `silly`). Nest's
91
+ `logger.log()` is `info`, `logger.fatal()` is `error`. Use lowercase values
92
+ exactly as listed — an unknown value (e.g. `WARN`, `log`, `trace`) currently
93
+ drops **all** logs, errors included.
94
+
95
+ What each setting affects:
96
+
97
+ | | stdout (pm2 / docker logs) | Logs in SigNoz | Traces in SigNoz |
98
+ |---|---|---|---|
99
+ | `LOG_LEVEL` | ✅ | ✅ | ❌ |
100
+ | `APM_LOG_TRANSPORT` | ✅ | ✅ (needs `otlp`) | ❌ |
101
+ | `OTEL_TRACES_SAMPLER` | ❌ | ❌ | ✅ |
102
+
103
+ Errors are still marked on the trace span by `recordException()` regardless of
104
+ `LOG_LEVEL`. After changing env vars, restart the app (pm2:
105
+ `pm2 restart <app> --update-env`).
106
+
107
+ **1. Production, minimal — only warn + error in pm2/docker, traces to SigNoz**
108
+ ```env
109
+ NODE_ENV=production
110
+ APM_SERVICE_NAME=my-app-api
111
+ LOG_LEVEL=warn
112
+ APM_LOG_TRANSPORT=stdout
113
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4318
114
+ ```
115
+ Note: at `warn` the HTTP access log (`GET /api/... 200`, level `info`) is
116
+ dropped too.
117
+
118
+ **2. Production, standard — info + access log in pm2/docker, logs and traces to SigNoz**
119
+ ```env
120
+ NODE_ENV=production
121
+ APM_SERVICE_NAME=my-app-api
122
+ LOG_LEVEL=info
123
+ APM_LOG_TRANSPORT=stdout,otlp
124
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4318
125
+ ```
126
+
127
+ **3. Production, high traffic — trace only 10% of requests**
128
+ ```env
129
+ NODE_ENV=production
130
+ APM_SERVICE_NAME=my-app-api
131
+ LOG_LEVEL=warn
132
+ APM_LOG_TRANSPORT=stdout,otlp
133
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4318
134
+ OTEL_TRACES_SAMPLER=parentbased_traceidratio
135
+ OTEL_TRACES_SAMPLER_ARG=0.1
136
+ ```
137
+
138
+ **4. Staging, investigating a bug — everything**
139
+ ```env
140
+ NODE_ENV=staging
141
+ APM_SERVICE_NAME=my-app-api
142
+ LOG_LEVEL=debug
143
+ APM_LOG_TRANSPORT=stdout,otlp
144
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4318
145
+ ```
146
+
147
+ **5. Local development, no SigNoz**
148
+ ```env
149
+ NODE_ENV=local
150
+ APM_SERVICE_NAME=my-app-api
151
+ # LOG_LEVEL defaults to debug, format to colorized pretty output
152
+ # OTEL_EXPORTER_OTLP_ENDPOINT left empty -> tracing off
153
+ ```
154
+
155
+ **6. Logs to pm2/docker only, no tracing at all**
156
+ ```env
157
+ NODE_ENV=production
158
+ APM_SERVICE_NAME=my-app-api
159
+ LOG_LEVEL=warn
160
+ APM_LOG_TRANSPORT=stdout
161
+ # OTEL_EXPORTER_OTLP_ENDPOINT not set -> nothing is sent to SigNoz
162
+ ```
163
+
164
+ **7. Collector listening on gRPC (port 4317)**
165
+ ```env
166
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4317
167
+ OTEL_EXPORTER_OTLP_PROTOCOL=grpc
168
+ ```
169
+
170
+ **8. Logs to Elasticsearch / Kibana**
171
+ ```env
172
+ NODE_ENV=production
173
+ APM_SERVICE_NAME=my-app-api
174
+ LOG_LEVEL=warn
175
+ APM_LOG_TRANSPORT=stdout,elasticsearch
176
+ ELASTICSEARCH_NODE=https://es.internal:9200
177
+ ELASTICSEARCH_USERNAME=elastic
178
+ ELASTICSEARCH_PASSWORD=changeme
179
+ ```
180
+
181
+ To stop shipping logs remotely, remove `otlp` / `elasticsearch` from
182
+ `APM_LOG_TRANSPORT` — `APM_ENABLED=false` currently only turns tracing off.
183
+
184
+ ## Shutdown
185
+
186
+ On `SIGTERM`/`SIGINT` the tracing SDK flushes pending spans (max 5s) but does
187
+ **not** call `process.exit()` — your app's own graceful shutdown (e.g.
188
+ `app.enableShutdownHooks()` in Nest) stays in control. If no other signal
189
+ handler is registered, the signal is re-raised after the flush so the process
190
+ still terminates normally.
84
191
 
85
192
  ## Pointing at a specific backend
86
193
 
@@ -29,7 +29,8 @@ function getApmConfig() {
29
29
  if (cachedConfig)
30
30
  return cachedConfig;
31
31
  const env = process.env;
32
- const nodeEnv = env.NODE_ENV || 'local';
32
+ const nodeEnv = env.APM_ENVIRONMENT || env.NODE_ENV || 'production';
33
+ const isLocal = nodeEnv === 'local' || nodeEnv === 'development';
33
34
  const otlpEndpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT || undefined;
34
35
  const transports = parseList(env.APM_LOG_TRANSPORT);
35
36
  cachedConfig = {
@@ -45,8 +46,7 @@ function getApmConfig() {
45
46
  },
46
47
  logging: {
47
48
  level: env.LOG_LEVEL || (nodeEnv === 'local' ? 'debug' : 'info'),
48
- format: env.APM_LOG_FORMAT ||
49
- (nodeEnv === 'local' || nodeEnv === 'development' ? 'pretty' : 'json'),
49
+ format: env.APM_LOG_FORMAT || (isLocal ? 'pretty' : 'json'),
50
50
  transports: transports.length ? transports : ['stdout'],
51
51
  elasticsearch: {
52
52
  node: env.ELASTICSEARCH_NODE || undefined,
@@ -14,7 +14,7 @@ let ApmExceptionFilter = class ApmExceptionFilter {
14
14
  (0, record_exception_1.recordException)(err, 'ApmExceptionFilter');
15
15
  const ctx = host.switchToHttp();
16
16
  const response = ctx.getResponse();
17
- if (!(response === null || response === void 0 ? void 0 : response.status))
17
+ if (!(response === null || response === void 0 ? void 0 : response.status) || response.headersSent)
18
18
  return;
19
19
  const status = err instanceof common_1.HttpException ? err.getStatus() : 500;
20
20
  const body = err instanceof common_1.HttpException
@@ -30,7 +30,7 @@ let ApmLoggingInterceptor = class ApmLoggingInterceptor {
30
30
  return next.handle().pipe((0, rxjs_1.tap)({
31
31
  next: () => {
32
32
  const response = context.switchToHttp().getResponse();
33
- winston_instance_1.winstonLogger.http(`${method} ${path} ${response.statusCode}`, {
33
+ winston_instance_1.winstonLogger.info(`${method} ${path} ${response.statusCode}`, {
34
34
  context: 'HTTP',
35
35
  method,
36
36
  path,
@@ -41,7 +41,7 @@ let ApmLoggingInterceptor = class ApmLoggingInterceptor {
41
41
  });
42
42
  },
43
43
  error: (err) => {
44
- winston_instance_1.winstonLogger.http(`${method} ${path} failed`, {
44
+ winston_instance_1.winstonLogger.info(`${method} ${path} failed`, {
45
45
  context: 'HTTP',
46
46
  method,
47
47
  path,
@@ -4,6 +4,7 @@ export declare class ApmLoggerService implements LoggerService {
4
4
  error(message: any, trace?: string, context?: string): void;
5
5
  warn(message: any, context?: string): void;
6
6
  debug(message: any, context?: string): void;
7
+ fatal(message: any, context?: string): void;
7
8
  verbose(message: any, context?: string): void;
8
9
  private stringify;
9
10
  }
@@ -22,6 +22,9 @@ let ApmLoggerService = class ApmLoggerService {
22
22
  debug(message, context) {
23
23
  winston_instance_1.winstonLogger.debug(this.stringify(message), { context });
24
24
  }
25
+ fatal(message, context) {
26
+ winston_instance_1.winstonLogger.error(this.stringify(message), { context, fatal: true });
27
+ }
25
28
  verbose(message, context) {
26
29
  winston_instance_1.winstonLogger.verbose(this.stringify(message), { context });
27
30
  }
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __rest = (this && this.__rest) || function (s, e) {
36
3
  var t = {};
37
4
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -43,9 +10,12 @@ var __rest = (this && this.__rest) || function (s, e) {
43
10
  }
44
11
  return t;
45
12
  };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
46
16
  Object.defineProperty(exports, "__esModule", { value: true });
47
17
  exports.OtlpLogTransport = void 0;
48
- const Transport = __importStar(require("winston-transport"));
18
+ const winston_transport_1 = __importDefault(require("winston-transport"));
49
19
  const api_1 = require("@opentelemetry/api");
50
20
  const apm_config_1 = require("../apm.config");
51
21
  const SEVERITY_MAP = {
@@ -58,29 +28,37 @@ const SEVERITY_MAP = {
58
28
  silly: { number: 1, text: 'TRACE' },
59
29
  };
60
30
  let otelLogger;
31
+ let emitFailureReported = false;
61
32
  function getOtelLogger() {
62
33
  if (otelLogger)
63
34
  return otelLogger;
64
35
  const config = (0, apm_config_1.getApmConfig)();
65
36
  const { LoggerProvider, BatchLogRecordProcessor, } = require('@opentelemetry/sdk-logs');
66
- const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
67
37
  const { resourceFromAttributes } = require('@opentelemetry/resources');
68
38
  const { ATTR_SERVICE_NAME } = require('@opentelemetry/semantic-conventions');
69
- const base = (config.tracing.otlpEndpoint || '').replace(/\/$/, '');
70
- const exporter = new OTLPLogExporter({
71
- url: `${base}/v1/logs`,
72
- headers: config.tracing.headers,
73
- });
39
+ let exporter;
40
+ if (config.tracing.protocol === 'grpc') {
41
+ const { OTLPLogExporter, } = require('@opentelemetry/exporter-logs-otlp-grpc');
42
+ exporter = new OTLPLogExporter({ url: config.tracing.otlpEndpoint });
43
+ }
44
+ else {
45
+ const { OTLPLogExporter, } = require('@opentelemetry/exporter-logs-otlp-http');
46
+ const base = (config.tracing.otlpEndpoint || '').replace(/\/$/, '');
47
+ exporter = new OTLPLogExporter({
48
+ url: `${base}/v1/logs`,
49
+ headers: config.tracing.headers,
50
+ });
51
+ }
74
52
  const provider = new LoggerProvider({
75
53
  resource: resourceFromAttributes({
76
54
  [ATTR_SERVICE_NAME]: config.serviceName,
77
55
  }),
56
+ processors: [new BatchLogRecordProcessor({ exporter })],
78
57
  });
79
- provider.addLogRecordProcessor(new BatchLogRecordProcessor(exporter));
80
58
  otelLogger = provider.getLogger(config.serviceName);
81
59
  return otelLogger;
82
60
  }
83
- class OtlpLogTransport extends Transport {
61
+ class OtlpLogTransport extends winston_transport_1.default {
84
62
  log(info, callback) {
85
63
  setImmediate(() => this.emit('logged', info));
86
64
  const { level, message } = info, rest = __rest(info, ["level", "message"]);
@@ -94,7 +72,11 @@ class OtlpLogTransport extends Transport {
94
72
  context: api_1.context.active(),
95
73
  });
96
74
  }
97
- catch (_a) {
75
+ catch (err) {
76
+ if (!emitFailureReported) {
77
+ emitFailureReported = true;
78
+ console.error('[apm] otlp log transport failed, logs are not being exported:', err instanceof Error ? err.message : err);
79
+ }
98
80
  }
99
81
  callback();
100
82
  }
@@ -61,7 +61,10 @@ function buildConsoleTransport() {
61
61
  return `${timestamp} ${level}${ctx} ${message}${trace}${extra}`;
62
62
  }))
63
63
  : winston.format.combine((0, trace_context_format_1.traceContextFormat)(), winston.format.timestamp(), winston.format.json());
64
- return new winston.transports.Console({ format });
64
+ return new winston.transports.Console({
65
+ format,
66
+ stderrLevels: ['error', 'warn'],
67
+ });
65
68
  }
66
69
  function buildElasticsearchTransport() {
67
70
  const config = (0, apm_config_1.getApmConfig)();
@@ -73,6 +76,8 @@ function buildElasticsearchTransport() {
73
76
  return new ElasticsearchTransport({
74
77
  level: config.logging.level,
75
78
  indexPrefix: config.logging.elasticsearch.index,
79
+ bufferLimit: 1000,
80
+ retryLimit: 5,
76
81
  clientOpts: {
77
82
  node: config.logging.elasticsearch.node,
78
83
  auth: config.logging.elasticsearch.username &&
@@ -122,3 +127,6 @@ exports.winstonLogger = winston.createLogger({
122
127
  defaultMeta: { service: config.serviceName, environment: config.environment },
123
128
  transports: buildTransports(),
124
129
  });
130
+ exports.winstonLogger.on('error', (err) => {
131
+ console.error('[apm] log transport error:', err instanceof Error ? err.message : err);
132
+ });
package/dist/tracing.js CHANGED
@@ -3,17 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const apm_config_1 = require("./apm.config");
4
4
  const config = (0, apm_config_1.getApmConfig)();
5
5
  if (config.enabled && config.tracing.enabled && config.tracing.otlpEndpoint) {
6
+ if (!process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS) {
7
+ process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS =
8
+ 'http,undici,express,nestjs-core,pg,ioredis,redis,amqplib';
9
+ }
6
10
  const { NodeSDK } = require('@opentelemetry/sdk-node');
7
11
  const { getNodeAutoInstrumentations, } = require('@opentelemetry/auto-instrumentations-node');
8
- const { OTLPTraceExporter, } = require('@opentelemetry/exporter-trace-otlp-http');
9
12
  const { resourceFromAttributes } = require('@opentelemetry/resources');
10
13
  const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } = require('@opentelemetry/semantic-conventions');
11
- const traceExporter = new OTLPTraceExporter({
12
- url: config.tracing.protocol === 'grpc'
13
- ? config.tracing.otlpEndpoint
14
- : `${config.tracing.otlpEndpoint.replace(/\/$/, '')}/v1/traces`,
15
- headers: config.tracing.headers,
16
- });
14
+ const traceExporter = createTraceExporter();
15
+ const ignorePaths = config.http.ignorePaths;
17
16
  const sdk = new NodeSDK({
18
17
  resource: resourceFromAttributes({
19
18
  [ATTR_SERVICE_NAME]: config.serviceName,
@@ -24,20 +23,42 @@ if (config.enabled && config.tracing.enabled && config.tracing.otlpEndpoint) {
24
23
  instrumentations: [
25
24
  getNodeAutoInstrumentations({
26
25
  '@opentelemetry/instrumentation-fs': { enabled: false },
26
+ '@opentelemetry/instrumentation-http': {
27
+ ignoreIncomingRequestHook: (req) => ignorePaths.some((p) => { var _a; return (_a = req.url) === null || _a === void 0 ? void 0 : _a.startsWith(p); }),
28
+ },
27
29
  }),
28
30
  ],
29
31
  });
30
32
  sdk.start();
31
- const shutdown = () => {
32
- sdk
33
- .shutdown()
34
- .catch((err) => console.error('Error shutting down OTel SDK', err))
35
- .finally(() => process.exit(0));
33
+ const SHUTDOWN_TIMEOUT_MS = 5000;
34
+ const onSignal = (signal) => {
35
+ Promise.race([
36
+ sdk.shutdown(),
37
+ new Promise((resolve) => setTimeout(resolve, SHUTDOWN_TIMEOUT_MS).unref()),
38
+ ])
39
+ .catch((err) => console.error('[apm] error shutting down OTel SDK', err))
40
+ .finally(() => {
41
+ if (process.listenerCount(signal) === 0) {
42
+ process.kill(process.pid, signal);
43
+ }
44
+ });
36
45
  };
37
- process.on('SIGTERM', shutdown);
38
- process.on('SIGINT', shutdown);
39
- console.info(`[apm] tracing enabled -> ${config.tracing.otlpEndpoint} (service: ${config.serviceName})`);
46
+ process.once('SIGTERM', onSignal);
47
+ process.once('SIGINT', onSignal);
48
+ console.info(`[apm] tracing enabled -> ${config.tracing.otlpEndpoint} (${config.tracing.protocol}, service: ${config.serviceName})`);
40
49
  }
41
50
  else {
42
51
  console.info('[apm] tracing disabled (set OTEL_EXPORTER_OTLP_ENDPOINT to enable)');
43
52
  }
53
+ function createTraceExporter() {
54
+ const { otlpEndpoint, protocol, headers } = config.tracing;
55
+ if (protocol === 'grpc') {
56
+ const { OTLPTraceExporter, } = require('@opentelemetry/exporter-trace-otlp-grpc');
57
+ return new OTLPTraceExporter({ url: otlpEndpoint });
58
+ }
59
+ const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
60
+ return new OTLPTraceExporter({
61
+ url: `${otlpEndpoint.replace(/\/$/, '')}/v1/traces`,
62
+ headers,
63
+ });
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlibs/apm",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Vendor-neutral APM for NestJS: OpenTelemetry tracing (OTLP) + structured logging + error monitoring. Works with SigNoz, Elastic/Kibana, Jaeger, or any OTel collector.",
5
5
  "author": "QBIT Developer",
6
6
  "license": "MIT",
@@ -28,7 +28,9 @@
28
28
  "@opentelemetry/api": "^1.9.1",
29
29
  "@opentelemetry/api-logs": "^0.221.0",
30
30
  "@opentelemetry/auto-instrumentations-node": "^0.79.0",
31
+ "@opentelemetry/exporter-logs-otlp-grpc": "^0.221.0",
31
32
  "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
33
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0",
32
34
  "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
33
35
  "@opentelemetry/resources": "^2.10.0",
34
36
  "@opentelemetry/sdk-logs": "^0.221.0",