1+ /**
2+ * OpenTelemetry Configuration for Lowcoder Application
3+ *
4+ * This file sets up OpenTelemetry auto-instrumentation for traces and metrics.
5+ *
6+ * How to use:
7+ * 1. Save this file as `otel-config.js` in your application's root directory.
8+ * 2. Ensure you have the necessary OpenTelemetry packages installed:
9+ * @opentelemetry /sdk-node
10+ * @opentelemetry /api
11+ * @opentelemetry /exporter-trace-otlp-http (or -grpc)
12+ * @opentelemetry /exporter-metrics-otlp-http (or -grpc)
13+ * @opentelemetry /resources
14+ * @opentelemetry /semantic-conventions
15+ * @opentelemetry /auto-instrumentations-node (CRITICAL for auto-instrumentation)
16+ *
17+ * Install @opentelemetry/auto-instrumentations-node if you haven't:
18+ * `npm install @opentelemetry/auto-instrumentations-node`
19+ * or
20+ * `yarn add @opentelemetry/auto-instrumentations-node`
21+ *
22+ * 3. Start your application using the `-r` flag to preload this configuration:
23+ * `export NODE_OPTIONS="-r ./otel-config.js"`
24+ *
25+ * Environment Variables for Configuration:
26+ * - OTEL_EXPORTER_OTLP_ENDPOINT: Base URL for OTLP HTTP exporters (e.g., http://localhost:4318).
27+ * If not set, defaults to http://localhost:4318.
28+ * - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: Specific URL for OTLP HTTP traces exporter (e.g., http://localhost:4318/v1/traces).
29+ * - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: Specific URL for OTLP HTTP metrics exporter (e.g., http://localhost:4318/v1/metrics).
30+ * - OTEL_SERVICE_NAME: Name of your service (e.g., 'node-service'). Defaults to 'unknown_service:nodejs'.
31+ * - OTEL_LOG_LEVEL: Set OpenTelemetry diagnostic logging level (e.g., 'debug', 'info', 'warn', 'error').
32+ * - OTEL_EXPORTER_PROTOCOL: 'http/protobuf' (default) or 'grpc'.
33+ */
34+
35+ const process = require ( 'process' ) ;
36+ const { NodeSDK } = require ( '@opentelemetry/sdk-node' ) ;
37+ const { OTLPTraceExporter : OTLPTraceExporterHttp } = require ( '@opentelemetry/exporter-trace-otlp-http' ) ;
38+ const { OTLPTraceExporter : OTLPTraceExporterGrpc } = require ( '@opentelemetry/exporter-trace-otlp-grpc' ) ;
39+ const { OTLPMetricExporter : OTLPMetricExporterHttp } = require ( '@opentelemetry/exporter-metrics-otlp-http' ) ;
40+ const { OTLPMetricExporter : OTLPMetricExporterGrpc } = require ( '@opentelemetry/exporter-metrics-otlp-grpc' ) ;
41+ const { BatchSpanProcessor } = require ( '@opentelemetry/sdk-trace-node' ) ; // Using BatchSpanProcessor for better performance
42+ const { PeriodicExportingMetricReader } = require ( '@opentelemetry/sdk-metrics' ) ;
43+ const { Resource } = require ( '@opentelemetry/resources' ) ;
44+ const { SemanticResourceAttributes } = require ( '@opentelemetry/semantic-conventions' ) ;
45+ const { getNodeAutoInstrumentations } = require ( '@opentelemetry/auto-instrumentations-node' ) ;
46+ const { DiagConsoleLogger, DiagLogLevel, diag } = require ( '@opentelemetry/api' ) ;
47+
48+ // --- Configuration ---
49+ const SERVICE_NAME = process . env . OTEL_SERVICE_NAME || 'lowcoder-node-service' ;
50+ const OTLP_EXPORTER_PROTOCOL = process . env . OTEL_EXPORTER_PROTOCOL || 'http/protobuf' ; // 'http/protobuf' or 'grpc'
51+
52+ const DEFAULT_OTLP_HTTP_ENDPOINT = 'http://localhost:4318' ;
53+ const DEFAULT_OTLP_GRPC_ENDPOINT = 'http://localhost:4317' ;
54+
55+ const OTLP_ENDPOINT = process . env . OTEL_EXPORTER_OTLP_ENDPOINT ||
56+ ( OTLP_EXPORTER_PROTOCOL === 'grpc' ? DEFAULT_OTLP_GRPC_ENDPOINT : DEFAULT_OTLP_HTTP_ENDPOINT ) ;
57+
58+ const TRACES_ENDPOINT = process . env . OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
59+ ( OTLP_EXPORTER_PROTOCOL === 'grpc' ? OTLP_ENDPOINT : `${ OTLP_ENDPOINT } /v1/traces` ) ;
60+ const METRICS_ENDPOINT = process . env . OTEL_EXPORTER_OTLP_METRICS_ENDPOINT ||
61+ ( OTLP_EXPORTER_PROTOCOL === 'grpc' ? OTLP_ENDPOINT : `${ OTLP_ENDPOINT } /v1/metrics` ) ;
62+
63+ // Optional: Set OpenTelemetry diagnostic logging level
64+ const otelLogLevel = process . env . OTEL_LOG_LEVEL ?. toUpperCase ( ) ;
65+ if ( otelLogLevel && DiagLogLevel [ otelLogLevel ] ) {
66+ diag . setLogger ( new DiagConsoleLogger ( ) , DiagLogLevel [ otelLogLevel ] ) ;
67+ } else {
68+ diag . setLogger ( new DiagConsoleLogger ( ) , DiagLogLevel . INFO ) ; // Default to INFO
69+ }
70+
71+ diag . info ( `OpenTelemetry SDK configured for service: ${ SERVICE_NAME } ` ) ;
72+ diag . info ( `Using OTLP protocol: ${ OTLP_EXPORTER_PROTOCOL } ` ) ;
73+ diag . info ( `Traces Exporter Endpoint: ${ TRACES_ENDPOINT } ` ) ;
74+ diag . info ( `Metrics Exporter Endpoint: ${ METRICS_ENDPOINT } ` ) ;
75+
76+ // --- Resource Definition ---
77+ const resource = Resource . default ( ) . merge (
78+ new Resource ( {
79+ [ SemanticResourceAttributes . SERVICE_NAME ] : SERVICE_NAME ,
80+ // [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0', // Optional
81+ } )
82+ ) ;
83+
84+ // --- Exporter Configuration ---
85+ let traceExporter ;
86+ let metricExporter ;
87+
88+ if ( OTLP_EXPORTER_PROTOCOL === 'grpc' ) {
89+ diag . info ( 'Using gRPC Exporters' ) ;
90+ traceExporter = new OTLPTraceExporterGrpc ( { url : TRACES_ENDPOINT } ) ;
91+ metricExporter = new OTLPMetricExporterGrpc ( { url : METRICS_ENDPOINT } ) ;
92+ } else {
93+ diag . info ( 'Using HTTP/protobuf Exporters' ) ;
94+ traceExporter = new OTLPTraceExporterHttp ( { url : TRACES_ENDPOINT } ) ;
95+ metricExporter = new OTLPMetricExporterHttp ( { url : METRICS_ENDPOINT } ) ;
96+ }
97+
98+ // --- SDK Initialization ---
99+ const sdk = new NodeSDK ( {
100+ resource : resource ,
101+ traceExporter : traceExporter ,
102+ spanProcessor : new BatchSpanProcessor ( traceExporter ) , // Recommended for most cases
103+ metricReader : new PeriodicExportingMetricReader ( {
104+ exporter : metricExporter ,
105+ exportIntervalMillis : 10000 , // Export metrics every 10 seconds
106+ } ) ,
107+ instrumentations : [
108+ getNodeAutoInstrumentations ( {
109+ // Configuration for specific instrumentations can be added here if needed
110+ // Example:
111+ // '@opentelemetry/instrumentation-http': {
112+ // applyCustomAttributesOnSpan: (span, request, response) => {
113+ // span.setAttribute('custom.attribute', 'value');
114+ // },
115+ // },
116+ } ) ,
117+ ] ,
118+ } ) ;
119+
120+ // --- Start SDK and Graceful Shutdown ---
121+ try {
122+ sdk . start ( ) ;
123+ diag . info ( 'OpenTelemetry SDK started successfully for traces and metrics.' ) ;
124+ } catch ( error ) {
125+ diag . error ( 'Error starting OpenTelemetry SDK:' , error ) ;
126+ process . exit ( 1 ) ;
127+ }
128+
129+ // Graceful shutdown
130+ const shutdown = ( ) => {
131+ diag . info ( 'Shutting down OpenTelemetry SDK...' ) ;
132+ sdk . shutdown ( )
133+ . then ( ( ) => diag . info ( 'OpenTelemetry SDK shut down successfully.' ) )
134+ . catch ( error => diag . error ( 'Error shutting down OpenTelemetry SDK:' , error ) )
135+ . finally ( ( ) => process . exit ( 0 ) ) ;
136+ } ;
137+
138+ process . on ( 'SIGTERM' , shutdown ) ;
139+ process . on ( 'SIGINT' , shutdown ) ;
0 commit comments