otel-agent-nodejs14
Version:
OpenTelemetry agent for Node.js 14+ applications with distributed tracing and metrics
171 lines (157 loc) • 5.79 kB
JavaScript
;
// NÃO use o register automático - vamos controlar manualmente
// require("@opentelemetry/auto-instrumentations-node/register"); // REMOVER ESTA LINHA
const { NodeSDK } = require("@opentelemetry/sdk-node");
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node");
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-proto");
const { OTLPMetricExporter } = require("@opentelemetry/exporter-metrics-otlp-proto");
const { PeriodicExportingMetricReader } = require("@opentelemetry/sdk-metrics");
const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const { GraphQLInstrumentation } = require("@opentelemetry/instrumentation-graphql");
const { diag, DiagConsoleLogger, DiagLogLevel } = require("@opentelemetry/api");
const { Resource } = require("@opentelemetry/resources");
const { SemanticResourceAttributes } = require("@opentelemetry/semantic-conventions");
// Configurar diagnóstico se necessário
if (process.env.OTEL_DIAGNOSTIC_LOGS === "true") {
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
}
// Configurar Resource com informações do serviço
const resource = Resource.default().merge(
new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || "nodejs-app",
[SemanticResourceAttributes.SERVICE_VERSION]: process.env.SERVICE_VERSION || "1.0.0",
})
);
// Configurar Trace Exporter
const traceExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || "http://otel-collector:4318/v1/traces",
headers: {},
});
// Configurar Span Processor
const spanProcessor = new BatchSpanProcessor(traceExporter);
// Configurar Metric Exporter
const metricExporter = new OTLPMetricExporter({
url: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT || "http://otel-collector:4318/v1/metrics",
headers: {},
});
// Configurar Metric Reader
const metricReader = new PeriodicExportingMetricReader({
exporter: metricExporter,
exportIntervalMillis: Number(process.env.OTEL_METRIC_EXPORT_INTERVAL) || 60000,
exportTimeoutMillis: Number(process.env.OTEL_METRIC_EXPORT_TIMEOUT) || 30000,
});
// Configurar Instrumentações
const instrumentations = [
getNodeAutoInstrumentations({
// Desabilitar fs para evitar overhead
"@opentelemetry/instrumentation-fs": {
enabled: false,
},
// Configuração customizada para HTTP
"@opentelemetry/instrumentation-http": {
enabled: true,
requestHook: (span, req) => {
span.setAttribute("http.method", req.method);
span.setAttribute("http.url", req.url);
if (req.headers?.["user-agent"]) {
span.setAttribute("http.user_agent", req.headers["user-agent"]);
}
if (req.socket?.remoteAddress) {
span.setAttribute("http.client_ip", req.socket.remoteAddress);
}
},
responseHook: (span, res) => {
span.setAttribute("http.status_code", res.statusCode);
},
},
// Express habilitado
"@opentelemetry/instrumentation-express": {
enabled: true,
},
// Redis habilitado
"@opentelemetry/instrumentation-redis": {
enabled: true,
},
// MongoDB habilitado
"@opentelemetry/instrumentation-mongodb": {
enabled: true,
},
// MySQL habilitado
"@opentelemetry/instrumentation-mysql": {
enabled: true,
},
// PostgreSQL habilitado
"@opentelemetry/instrumentation-pg": {
enabled: true,
},
}),
// GraphQL como instrumentação separada
new GraphQLInstrumentation({
mergeItems: true,
depth: 5,
allowValues: true,
requestHook: (span, info) => {
if (info.operationName) {
span.setAttribute("graphql.operation.name", info.operationName);
}
if (info.operation?.operation) {
span.setAttribute("graphql.operation.type", info.operation.operation);
}
},
responseHook: (span, result) => {
if (result?.errors) {
span.setStatus({ code: 2, message: "GraphQL Error" });
span.setAttribute("graphql.errors", JSON.stringify(result.errors));
}
},
}),
];
// Inicializar SDK
const sdk = new NodeSDK({
resource,
spanProcessor,
metricReader, // Passar como propriedade direta, não como array
instrumentations,
});
// Função de inicialização com melhor tratamento de erro
async function startSDK() {
try {
await sdk.start();
console.log("[otel-agent] OpenTelemetry SDK iniciado com sucesso");
console.log(`[otel-agent] Service Name: ${process.env.OTEL_SERVICE_NAME || "nodejs-app"}`);
console.log(`[otel-agent] Traces Endpoint: ${process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || "http://otel-collector:4318/v1/traces"}`);
console.log(`[otel-agent] Metrics Endpoint: ${process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT || "http://otel-collector:4318/v1/metrics"}`);
} catch (err) {
console.error("[otel-agent] Falha ao iniciar SDK:", err);
// Não encerrar o processo, deixar a aplicação continuar
}
}
// Iniciar SDK
startSDK();
// Graceful shutdown
process.on("SIGTERM", () => {
console.log("[otel-agent] SIGTERM recebido, iniciando shutdown...");
sdk
.shutdown()
.then(() => {
console.log("[otel-agent] SDK shutdown completo");
process.exit(0);
})
.catch((err) => {
console.error("[otel-agent] Erro no shutdown:", err);
process.exit(1);
});
});
process.on("SIGINT", () => {
console.log("[otel-agent] SIGINT recebido, iniciando shutdown...");
sdk
.shutdown()
.then(() => {
console.log("[otel-agent] SDK shutdown completo");
process.exit(0);
})
.catch((err) => {
console.error("[otel-agent] Erro no shutdown:", err);
process.exit(1);
});
});