sasai-common-utils 1.0.47 → 1.0.49
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/index.d.ts +88 -88
- package/package.json +30 -30
- package/src/features/axios-logger/axiosLogger.js +85 -85
- package/src/features/axios-logger/index.js +5 -5
- package/src/features/express-logger/expressLogger.js +46 -46
- package/src/features/express-logger/index.js +5 -5
- package/src/features/logger/config.js +6 -6
- package/src/features/logger/constants.js +36 -36
- package/src/features/logger/index.js +206 -177
- package/src/features/logger/logWorker.js +119 -119
- package/src/features/logger/redact.js +61 -61
- package/src/features/logger/winstonLogger.js +186 -186
- package/src/index.js +36 -36
- package/src/utils/index.js +11 -11
|
@@ -1,177 +1,206 @@
|
|
|
1
|
-
const jwt = require("jsonwebtoken");
|
|
2
|
-
const pino = require("pino");
|
|
3
|
-
const { HTTP_STATUS_CODES, DEBUG_MODES } = require("./constants");
|
|
4
|
-
const { redactInformation } = require("./redact");
|
|
5
|
-
|
|
6
|
-
let contextProvider = () => ({});
|
|
7
|
-
let globalConfig = {};
|
|
8
|
-
let loggerInstance;
|
|
9
|
-
let pinoLogger;
|
|
10
|
-
|
|
11
|
-
function setContextProvider(provider) {
|
|
12
|
-
contextProvider = provider;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function setGlobalConfig(config) {
|
|
16
|
-
globalConfig = {
|
|
17
|
-
SERVICE_NAME: config?.SERVICE_NAME,
|
|
18
|
-
LOG_LEVEL: config?.LOG_LEVEL || "info",
|
|
19
|
-
DEBUG_MODE: config?.DEBUG_MODE,
|
|
20
|
-
SENSITIVE_KEYS: config?.SENSITIVE_KEYS,
|
|
21
|
-
NODE_ENV: config?.NODE_ENV,
|
|
22
|
-
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: config?.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
1
|
+
const jwt = require("jsonwebtoken");
|
|
2
|
+
const pino = require("pino");
|
|
3
|
+
const { HTTP_STATUS_CODES, DEBUG_MODES } = require("./constants");
|
|
4
|
+
const { redactInformation } = require("./redact");
|
|
5
|
+
|
|
6
|
+
let contextProvider = () => ({});
|
|
7
|
+
let globalConfig = {};
|
|
8
|
+
let loggerInstance;
|
|
9
|
+
let pinoLogger;
|
|
10
|
+
|
|
11
|
+
function setContextProvider(provider) {
|
|
12
|
+
contextProvider = provider;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function setGlobalConfig(config) {
|
|
16
|
+
globalConfig = {
|
|
17
|
+
SERVICE_NAME: config?.SERVICE_NAME,
|
|
18
|
+
LOG_LEVEL: config?.LOG_LEVEL || "info",
|
|
19
|
+
DEBUG_MODE: config?.DEBUG_MODE,
|
|
20
|
+
SENSITIVE_KEYS: config?.SENSITIVE_KEYS,
|
|
21
|
+
NODE_ENV: config?.NODE_ENV,
|
|
22
|
+
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: config?.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
|
|
23
|
+
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL:
|
|
24
|
+
config?.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL || "http/protobuf",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function cleanString(str) {
|
|
29
|
+
return typeof str === "string" ? str.replace(/\\"/g, '"') : str;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function initPinoLogger() {
|
|
33
|
+
const pinoOptions = {
|
|
34
|
+
timestamp: false,
|
|
35
|
+
level:
|
|
36
|
+
globalConfig.DEBUG_MODE === DEBUG_MODES.DISABLE
|
|
37
|
+
? "silent"
|
|
38
|
+
: globalConfig.LOG_LEVEL,
|
|
39
|
+
base: {
|
|
40
|
+
service_name: globalConfig.SERVICE_NAME,
|
|
41
|
+
environment: globalConfig.NODE_ENV || "unknown",
|
|
42
|
+
},
|
|
43
|
+
formatters: {
|
|
44
|
+
level(label) {
|
|
45
|
+
return { level_name: label.toUpperCase() };
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (globalConfig.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) {
|
|
51
|
+
const stdoutTarget = {
|
|
52
|
+
target: require.resolve("pino/file"),
|
|
53
|
+
options: { destination: 1 },
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const otelTarget = {
|
|
57
|
+
target: require.resolve("pino-opentelemetry-transport"),
|
|
58
|
+
options: {
|
|
59
|
+
url: globalConfig.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
|
|
60
|
+
protocol: globalConfig.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
|
|
61
|
+
resourceAttributes: {
|
|
62
|
+
"service.name": globalConfig.SERVICE_NAME || "bff-service",
|
|
63
|
+
"deployment.environment": globalConfig.NODE_ENV || "unknown",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const transport = pino.transport({
|
|
69
|
+
targets: [stdoutTarget, otelTarget],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
transport.on("error", (err) => {
|
|
73
|
+
console.error("Pino OTEL transport error:", err);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
pinoLogger = pino(pinoOptions, transport);
|
|
77
|
+
} else {
|
|
78
|
+
let destination;
|
|
79
|
+
if (globalConfig.DEBUG_MODE === DEBUG_MODES.FILE) {
|
|
80
|
+
destination = pino.destination("app.log");
|
|
81
|
+
} else {
|
|
82
|
+
destination = pino.destination(1);
|
|
83
|
+
}
|
|
84
|
+
pinoLogger = pino(pinoOptions, destination);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function emitLog(level, data) {
|
|
89
|
+
if (!pinoLogger) return;
|
|
90
|
+
const { trace, span, message, ...rest } = data;
|
|
91
|
+
pinoLogger[level]({
|
|
92
|
+
trace_id: trace || undefined,
|
|
93
|
+
span_id: span || undefined,
|
|
94
|
+
message: cleanString(message),
|
|
95
|
+
...rest,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createLogger(config) {
|
|
100
|
+
if (loggerInstance) return loggerInstance;
|
|
101
|
+
|
|
102
|
+
setGlobalConfig(config);
|
|
103
|
+
initPinoLogger();
|
|
104
|
+
|
|
105
|
+
loggerInstance = {
|
|
106
|
+
logInfo: (message, data = {}) => {
|
|
107
|
+
const traceContext = contextProvider();
|
|
108
|
+
const authHeader =
|
|
109
|
+
data?.headers?.authorization || data?.headers?.Authorization;
|
|
110
|
+
const tokenData = jwt.decode(authHeader?.split(" ")[1], {
|
|
111
|
+
complete: true,
|
|
112
|
+
})?.payload;
|
|
113
|
+
|
|
114
|
+
const logData = {
|
|
115
|
+
trace: traceContext?.trace_id || "",
|
|
116
|
+
span: traceContext?.span_id || "",
|
|
117
|
+
thread: "",
|
|
118
|
+
class: "",
|
|
119
|
+
message: JSON.stringify({
|
|
120
|
+
step: data?.step,
|
|
121
|
+
message,
|
|
122
|
+
method: data?.method?.toUpperCase() || "",
|
|
123
|
+
parameters: data?.parameters || "",
|
|
124
|
+
path: data?.url || "",
|
|
125
|
+
headers: data?.headers || {},
|
|
126
|
+
responseHeaders: data?.responseHeaders || {},
|
|
127
|
+
api: data?.api || "",
|
|
128
|
+
tokenDetails: {
|
|
129
|
+
customerId: tokenData?.customerId || "",
|
|
130
|
+
mid: tokenData?.mid || "",
|
|
131
|
+
tenantId: tokenData?.tenantId || "",
|
|
132
|
+
},
|
|
133
|
+
responseStatus: data?.statusCode || "",
|
|
134
|
+
requestBody: data?.requestBody || {},
|
|
135
|
+
responseBody: data?.responseBody || {},
|
|
136
|
+
}),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const redactedData = redactInformation(logData, {
|
|
140
|
+
sensitiveKeys: globalConfig?.SENSITIVE_KEYS,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
emitLog("info", redactedData);
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
logError: (data = {}, req = {}) => {
|
|
147
|
+
const traceContext = contextProvider();
|
|
148
|
+
const errorResponse = data?.error?.response;
|
|
149
|
+
const authHeader =
|
|
150
|
+
data?.headers?.authorization || data?.headers?.Authorization;
|
|
151
|
+
const tokenData = jwt.decode(authHeader?.split(" ")[1], {
|
|
152
|
+
complete: true,
|
|
153
|
+
})?.payload;
|
|
154
|
+
|
|
155
|
+
const logData = {
|
|
156
|
+
trace: traceContext?.trace_id || "",
|
|
157
|
+
span: traceContext?.span_id || "",
|
|
158
|
+
thread: "",
|
|
159
|
+
class: "",
|
|
160
|
+
message: JSON.stringify({
|
|
161
|
+
step: data?.step || "",
|
|
162
|
+
message: data?.error?.message,
|
|
163
|
+
method: errorResponse?.config?.method?.toUpperCase(),
|
|
164
|
+
parameters: data?.parameters || "",
|
|
165
|
+
path: errorResponse?.config?.url || req?.originalUrl,
|
|
166
|
+
responseStatus:
|
|
167
|
+
errorResponse?.status || HTTP_STATUS_CODES.BAD_REQUEST,
|
|
168
|
+
headers: errorResponse?.config?.headers || {},
|
|
169
|
+
requestBody: errorResponse?.config?.body || req?.body,
|
|
170
|
+
responseBody: errorResponse?.data || {},
|
|
171
|
+
stack: data?.error?.stack,
|
|
172
|
+
tokenDetails: {
|
|
173
|
+
customerId: tokenData?.customerId || "",
|
|
174
|
+
mid: tokenData?.mid || "",
|
|
175
|
+
tenantId: tokenData?.tenantId || "",
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const redactedData = redactInformation(logData, {
|
|
181
|
+
sensitiveKeys: globalConfig?.SENSITIVE_KEYS,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
emitLog("error", redactedData);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
logDebug: (message, data = {}) => {
|
|
188
|
+
const traceContext = contextProvider();
|
|
189
|
+
emitLog("debug", {
|
|
190
|
+
trace: traceContext?.trace_id || "",
|
|
191
|
+
span: traceContext?.span_id || "",
|
|
192
|
+
message:
|
|
193
|
+
typeof message === "string" ? message : JSON.stringify(message),
|
|
194
|
+
...data,
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
return loggerInstance;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = {
|
|
203
|
+
createLogger,
|
|
204
|
+
setContextProvider,
|
|
205
|
+
logger: loggerInstance,
|
|
206
|
+
};
|
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
const { parentPort, workerData } = require("worker_threads");
|
|
2
|
-
const pino = require("pino");
|
|
3
|
-
const { DEBUG_MODES } = require("./constants");
|
|
4
|
-
const globalConfig = workerData.globalConfig;
|
|
5
|
-
|
|
6
|
-
// OpenTelemetry Logs SDK setup
|
|
7
|
-
let otelLoggerProvider = null;
|
|
8
|
-
try {
|
|
9
|
-
const { LoggerProvider, BatchLogRecordProcessor } = require("@opentelemetry/sdk-logs");
|
|
10
|
-
const { OTLPLogExporter } = require("@opentelemetry/exporter-logs-otlp-http");
|
|
11
|
-
const { Resource } = require("@opentelemetry/resources");
|
|
12
|
-
|
|
13
|
-
// Create OTLP Log Exporter
|
|
14
|
-
const logExporter = new OTLPLogExporter({
|
|
15
|
-
url: globalConfig.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT || 'http://localhost:4318/v1/logs'
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
// Create Logger Provider with resource attributes
|
|
19
|
-
otelLoggerProvider = new LoggerProvider({
|
|
20
|
-
resource: new Resource({
|
|
21
|
-
'service.name': globalConfig.SERVICE_NAME || 'sasai-service',
|
|
22
|
-
'deployment.environment': globalConfig.NODE_ENV || ''
|
|
23
|
-
})
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Add batch processor for efficient log export
|
|
27
|
-
otelLoggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
|
|
28
|
-
|
|
29
|
-
console.log('✅ OpenTelemetry Logs SDK initialized');
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.warn('⚠️ OpenTelemetry Logs SDK not available, logs will only write locally:', error.message);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Initialize Pino logger inside the worker
|
|
35
|
-
function cleanString(str) {
|
|
36
|
-
return typeof str === "string"
|
|
37
|
-
? str.replace(/\\"/g, '"')
|
|
38
|
-
: str;
|
|
39
|
-
}
|
|
40
|
-
const transports = []
|
|
41
|
-
if (globalConfig.DEBUG_MODE === DEBUG_MODES.CONSOLE) {
|
|
42
|
-
transports.push(pino.destination(1));
|
|
43
|
-
} else if (globalConfig.DEBUG_MODE === DEBUG_MODES.FILE) {
|
|
44
|
-
transports.push(pino.destination("app.log"));
|
|
45
|
-
} else {
|
|
46
|
-
pino.destination(1);
|
|
47
|
-
pino.destination("app.log");
|
|
48
|
-
}
|
|
49
|
-
const logger = pino({
|
|
50
|
-
timestamp: false,
|
|
51
|
-
level:globalConfig.DEBUG_MODE == DEBUG_MODES.DISABLE ? 'silent': globalConfig.LOG_LEVEL,
|
|
52
|
-
|
|
53
|
-
base: {
|
|
54
|
-
service_name: globalConfig.SERVICE_NAME
|
|
55
|
-
},
|
|
56
|
-
formatters: {
|
|
57
|
-
level() {
|
|
58
|
-
return {
|
|
59
|
-
level_name: globalConfig.LOG_LEVEL.toUpperCase()
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}, transports.length ? transports[0] : undefined)
|
|
64
|
-
|
|
65
|
-
// Helper function to convert Pino log level to OpenTelemetry severity
|
|
66
|
-
function pinoLevelToOtelSeverity(pinoLevel) {
|
|
67
|
-
const levelMap = {
|
|
68
|
-
'trace': 1, // TRACE
|
|
69
|
-
'debug': 5, // DEBUG
|
|
70
|
-
'info': 9, // INFO
|
|
71
|
-
'warn': 13, // WARN
|
|
72
|
-
'error': 17, // ERROR
|
|
73
|
-
'fatal': 21 // FATAL
|
|
74
|
-
};
|
|
75
|
-
return levelMap[pinoLevel] || 9; // Default to INFO
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Helper function to send logs to OpenTelemetry
|
|
79
|
-
function sendToOtel(level, logData) {
|
|
80
|
-
if (!otelLoggerProvider) return;
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const otelLogger = otelLoggerProvider.getLogger(globalConfig.SERVICE_NAME);
|
|
84
|
-
|
|
85
|
-
// Emit log record to OpenTelemetry
|
|
86
|
-
otelLogger.emit({
|
|
87
|
-
severityNumber: pinoLevelToOtelSeverity(level),
|
|
88
|
-
severityText: level.toUpperCase(),
|
|
89
|
-
body: logData.message || JSON.stringify(logData),
|
|
90
|
-
attributes: {
|
|
91
|
-
'trace.id': logData.trace_id,
|
|
92
|
-
'span.id': logData.span_id,
|
|
93
|
-
'service.name': globalConfig.SERVICE_NAME,
|
|
94
|
-
...logData
|
|
95
|
-
},
|
|
96
|
-
timestamp: Date.now() * 1000000, // Convert to nanoseconds
|
|
97
|
-
});
|
|
98
|
-
} catch (error) {
|
|
99
|
-
// Silently fail to avoid breaking the logging flow
|
|
100
|
-
console.error('Failed to send log to OpenTelemetry:', error.message);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Listen for log messages from the main thread
|
|
105
|
-
parentPort.on("message", ({ level, data }) => {
|
|
106
|
-
const { trace, span, message, ...rest } = data;
|
|
107
|
-
const logData = {
|
|
108
|
-
trace_id: trace || undefined,
|
|
109
|
-
span_id: span || undefined,
|
|
110
|
-
message: cleanString(message),
|
|
111
|
-
...rest
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// Write to Pino (console/file)
|
|
115
|
-
logger[level](logData);
|
|
116
|
-
|
|
117
|
-
// Also send to OpenTelemetry if available
|
|
118
|
-
sendToOtel(level, logData);
|
|
119
|
-
});
|
|
1
|
+
const { parentPort, workerData } = require("worker_threads");
|
|
2
|
+
const pino = require("pino");
|
|
3
|
+
const { DEBUG_MODES } = require("./constants");
|
|
4
|
+
const globalConfig = workerData.globalConfig;
|
|
5
|
+
|
|
6
|
+
// OpenTelemetry Logs SDK setup
|
|
7
|
+
let otelLoggerProvider = null;
|
|
8
|
+
try {
|
|
9
|
+
const { LoggerProvider, BatchLogRecordProcessor } = require("@opentelemetry/sdk-logs");
|
|
10
|
+
const { OTLPLogExporter } = require("@opentelemetry/exporter-logs-otlp-http");
|
|
11
|
+
const { Resource } = require("@opentelemetry/resources");
|
|
12
|
+
|
|
13
|
+
// Create OTLP Log Exporter
|
|
14
|
+
const logExporter = new OTLPLogExporter({
|
|
15
|
+
url: globalConfig.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT || 'http://localhost:4318/v1/logs'
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Create Logger Provider with resource attributes
|
|
19
|
+
otelLoggerProvider = new LoggerProvider({
|
|
20
|
+
resource: new Resource({
|
|
21
|
+
'service.name': globalConfig.SERVICE_NAME || 'sasai-service',
|
|
22
|
+
'deployment.environment': globalConfig.NODE_ENV || ''
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Add batch processor for efficient log export
|
|
27
|
+
otelLoggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
|
|
28
|
+
|
|
29
|
+
console.log('✅ OpenTelemetry Logs SDK initialized');
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.warn('⚠️ OpenTelemetry Logs SDK not available, logs will only write locally:', error.message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Initialize Pino logger inside the worker
|
|
35
|
+
function cleanString(str) {
|
|
36
|
+
return typeof str === "string"
|
|
37
|
+
? str.replace(/\\"/g, '"')
|
|
38
|
+
: str;
|
|
39
|
+
}
|
|
40
|
+
const transports = []
|
|
41
|
+
if (globalConfig.DEBUG_MODE === DEBUG_MODES.CONSOLE) {
|
|
42
|
+
transports.push(pino.destination(1));
|
|
43
|
+
} else if (globalConfig.DEBUG_MODE === DEBUG_MODES.FILE) {
|
|
44
|
+
transports.push(pino.destination("app.log"));
|
|
45
|
+
} else {
|
|
46
|
+
pino.destination(1);
|
|
47
|
+
pino.destination("app.log");
|
|
48
|
+
}
|
|
49
|
+
const logger = pino({
|
|
50
|
+
timestamp: false,
|
|
51
|
+
level:globalConfig.DEBUG_MODE == DEBUG_MODES.DISABLE ? 'silent': globalConfig.LOG_LEVEL,
|
|
52
|
+
|
|
53
|
+
base: {
|
|
54
|
+
service_name: globalConfig.SERVICE_NAME
|
|
55
|
+
},
|
|
56
|
+
formatters: {
|
|
57
|
+
level() {
|
|
58
|
+
return {
|
|
59
|
+
level_name: globalConfig.LOG_LEVEL.toUpperCase()
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}, transports.length ? transports[0] : undefined)
|
|
64
|
+
|
|
65
|
+
// Helper function to convert Pino log level to OpenTelemetry severity
|
|
66
|
+
function pinoLevelToOtelSeverity(pinoLevel) {
|
|
67
|
+
const levelMap = {
|
|
68
|
+
'trace': 1, // TRACE
|
|
69
|
+
'debug': 5, // DEBUG
|
|
70
|
+
'info': 9, // INFO
|
|
71
|
+
'warn': 13, // WARN
|
|
72
|
+
'error': 17, // ERROR
|
|
73
|
+
'fatal': 21 // FATAL
|
|
74
|
+
};
|
|
75
|
+
return levelMap[pinoLevel] || 9; // Default to INFO
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Helper function to send logs to OpenTelemetry
|
|
79
|
+
function sendToOtel(level, logData) {
|
|
80
|
+
if (!otelLoggerProvider) return;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const otelLogger = otelLoggerProvider.getLogger(globalConfig.SERVICE_NAME);
|
|
84
|
+
|
|
85
|
+
// Emit log record to OpenTelemetry
|
|
86
|
+
otelLogger.emit({
|
|
87
|
+
severityNumber: pinoLevelToOtelSeverity(level),
|
|
88
|
+
severityText: level.toUpperCase(),
|
|
89
|
+
body: logData.message || JSON.stringify(logData),
|
|
90
|
+
attributes: {
|
|
91
|
+
'trace.id': logData.trace_id,
|
|
92
|
+
'span.id': logData.span_id,
|
|
93
|
+
'service.name': globalConfig.SERVICE_NAME,
|
|
94
|
+
...logData
|
|
95
|
+
},
|
|
96
|
+
timestamp: Date.now() * 1000000, // Convert to nanoseconds
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
// Silently fail to avoid breaking the logging flow
|
|
100
|
+
console.error('Failed to send log to OpenTelemetry:', error.message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Listen for log messages from the main thread
|
|
105
|
+
parentPort.on("message", ({ level, data }) => {
|
|
106
|
+
const { trace, span, message, ...rest } = data;
|
|
107
|
+
const logData = {
|
|
108
|
+
trace_id: trace || undefined,
|
|
109
|
+
span_id: span || undefined,
|
|
110
|
+
message: cleanString(message),
|
|
111
|
+
...rest
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Write to Pino (console/file)
|
|
115
|
+
logger[level](logData);
|
|
116
|
+
|
|
117
|
+
// Also send to OpenTelemetry if available
|
|
118
|
+
sendToOtel(level, logData);
|
|
119
|
+
});
|