sasai-common-utils 1.0.48 → 1.0.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sasai-common-utils",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "description": "Reusable utility library for common logging and other shared features.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -7,9 +7,10 @@ let contextProvider = () => ({});
7
7
  let globalConfig = {};
8
8
  let loggerInstance;
9
9
  let pinoLogger;
10
+ let pinoTransport;
10
11
 
11
12
  function setContextProvider(provider) {
12
- contextProvider = provider;
13
+ contextProvider = typeof provider === "function" ? provider : () => ({});
13
14
  }
14
15
 
15
16
  function setGlobalConfig(config) {
@@ -29,6 +30,54 @@ function cleanString(str) {
29
30
  return typeof str === "string" ? str.replace(/\\"/g, '"') : str;
30
31
  }
31
32
 
33
+ function safeJsonStringify(value) {
34
+ try {
35
+ return JSON.stringify(value);
36
+ } catch (error) {
37
+ return JSON.stringify({
38
+ message: "Failed to stringify log payload",
39
+ error: error?.message || "Unknown stringify error",
40
+ });
41
+ }
42
+ }
43
+
44
+ function decodeTokenDetails(headers = {}) {
45
+ try {
46
+ const authHeader = headers?.authorization || headers?.Authorization;
47
+ const token = authHeader?.split(" ")[1];
48
+ const payload = jwt.decode(token, { complete: true })?.payload;
49
+
50
+ return {
51
+ customerId: payload?.customerId || "",
52
+ mid: payload?.mid || "",
53
+ tenantId: payload?.tenantId || "",
54
+ };
55
+ } catch (error) {
56
+ return {
57
+ customerId: "",
58
+ mid: "",
59
+ tenantId: "",
60
+ };
61
+ }
62
+ }
63
+
64
+ function getTraceContext() {
65
+ try {
66
+ const traceContext = contextProvider?.() || {};
67
+ return {
68
+ trace_id: traceContext?.trace_id || "",
69
+ span_id: traceContext?.span_id || "",
70
+ parent_span_id: traceContext?.parent_span_id || "",
71
+ };
72
+ } catch (error) {
73
+ return {
74
+ trace_id: "",
75
+ span_id: "",
76
+ parent_span_id: "",
77
+ };
78
+ }
79
+ }
80
+
32
81
  function initPinoLogger() {
33
82
  const pinoOptions = {
34
83
  timestamp: false,
@@ -37,8 +86,8 @@ function initPinoLogger() {
37
86
  ? "silent"
38
87
  : globalConfig.LOG_LEVEL,
39
88
  base: {
40
- service_name: globalConfig.SERVICE_NAME,
41
- environment: globalConfig.NODE_ENV || "unknown",
89
+ "service.name": globalConfig.SERVICE_NAME,
90
+ "deployment.environment": globalConfig.NODE_ENV,
42
91
  },
43
92
  formatters: {
44
93
  level(label) {
@@ -59,21 +108,25 @@ function initPinoLogger() {
59
108
  url: globalConfig.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
60
109
  protocol: globalConfig.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
61
110
  resourceAttributes: {
62
- "service.name": globalConfig.SERVICE_NAME || "bff-service",
63
- "deployment.environment": globalConfig.NODE_ENV || "unknown",
111
+ "service.name": globalConfig.SERVICE_NAME,
112
+ "deployment.environment": globalConfig.NODE_ENV,
64
113
  },
65
114
  },
66
115
  };
67
116
 
68
- const transport = pino.transport({
117
+ pinoTransport = pino.transport({
69
118
  targets: [stdoutTarget, otelTarget],
70
119
  });
71
120
 
72
- transport.on("error", (err) => {
121
+ pinoTransport.on("error", (err) => {
73
122
  console.error("Pino OTEL transport error:", err);
74
123
  });
75
124
 
76
- pinoLogger = pino(pinoOptions, transport);
125
+ pinoTransport.on("close", () => {
126
+ console.warn("Pino OTEL transport closed");
127
+ });
128
+
129
+ pinoLogger = pino(pinoOptions, pinoTransport);
77
130
  } else {
78
131
  let destination;
79
132
  if (globalConfig.DEBUG_MODE === DEBUG_MODES.FILE) {
@@ -83,14 +136,29 @@ function initPinoLogger() {
83
136
  }
84
137
  pinoLogger = pino(pinoOptions, destination);
85
138
  }
139
+
140
+ try {
141
+ pinoLogger.info({
142
+ message: "Pino logger initialized",
143
+ "service.name": globalConfig.SERVICE_NAME,
144
+ "deployment.environment": globalConfig.NODE_ENV,
145
+ otel_logs_endpoint: globalConfig.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT || "",
146
+ otel_logs_protocol: globalConfig.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL || "",
147
+ });
148
+ } catch (error) {
149
+ console.error("Failed to write startup log:", error);
150
+ }
86
151
  }
87
152
 
88
153
  function emitLog(level, data) {
89
154
  if (!pinoLogger) return;
90
- const { trace, span, message, ...rest } = data;
155
+
156
+ const { trace, span, parentSpan, message, ...rest } = data;
157
+
91
158
  pinoLogger[level]({
92
159
  trace_id: trace || undefined,
93
160
  span_id: span || undefined,
161
+ parent_span_id: parentSpan || undefined,
94
162
  message: cleanString(message),
95
163
  ...rest,
96
164
  });
@@ -104,20 +172,17 @@ function createLogger(config) {
104
172
 
105
173
  loggerInstance = {
106
174
  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;
175
+ const traceContext = getTraceContext();
176
+ const tokenDetails = decodeTokenDetails(data?.headers || {});
113
177
 
114
178
  const logData = {
115
- trace: traceContext?.trace_id || "",
116
- span: traceContext?.span_id || "",
179
+ trace: traceContext.trace_id,
180
+ span: traceContext.span_id,
181
+ parentSpan: traceContext.parent_span_id,
117
182
  thread: "",
118
183
  class: "",
119
- message: JSON.stringify({
120
- step: data?.step,
184
+ message: safeJsonStringify({
185
+ step: data?.step || "",
121
186
  message,
122
187
  method: data?.method?.toUpperCase() || "",
123
188
  parameters: data?.parameters || "",
@@ -125,11 +190,7 @@ function createLogger(config) {
125
190
  headers: data?.headers || {},
126
191
  responseHeaders: data?.responseHeaders || {},
127
192
  api: data?.api || "",
128
- tokenDetails: {
129
- customerId: tokenData?.customerId || "",
130
- mid: tokenData?.mid || "",
131
- tenantId: tokenData?.tenantId || "",
132
- },
193
+ tokenDetails,
133
194
  responseStatus: data?.statusCode || "",
134
195
  requestBody: data?.requestBody || {},
135
196
  responseBody: data?.responseBody || {},
@@ -144,36 +205,38 @@ function createLogger(config) {
144
205
  },
145
206
 
146
207
  logError: (data = {}, req = {}) => {
147
- const traceContext = contextProvider();
208
+ const traceContext = getTraceContext();
148
209
  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;
210
+ const tokenDetails = decodeTokenDetails(
211
+ data?.headers ||
212
+ errorResponse?.config?.headers ||
213
+ req?.headers ||
214
+ {}
215
+ );
154
216
 
155
217
  const logData = {
156
- trace: traceContext?.trace_id || "",
157
- span: traceContext?.span_id || "",
218
+ trace: traceContext.trace_id,
219
+ span: traceContext.span_id,
220
+ parentSpan: traceContext.parent_span_id,
158
221
  thread: "",
159
222
  class: "",
160
- message: JSON.stringify({
223
+ message: safeJsonStringify({
161
224
  step: data?.step || "",
162
- message: data?.error?.message,
163
- method: errorResponse?.config?.method?.toUpperCase(),
225
+ message: data?.error?.message || "",
226
+ method: errorResponse?.config?.method?.toUpperCase() || "",
164
227
  parameters: data?.parameters || "",
165
- path: errorResponse?.config?.url || req?.originalUrl,
228
+ path: errorResponse?.config?.url || req?.originalUrl || "",
166
229
  responseStatus:
167
230
  errorResponse?.status || HTTP_STATUS_CODES.BAD_REQUEST,
168
- headers: errorResponse?.config?.headers || {},
169
- requestBody: errorResponse?.config?.body || req?.body,
231
+ headers: errorResponse?.config?.headers || req?.headers || {},
232
+ requestBody:
233
+ errorResponse?.config?.data ||
234
+ errorResponse?.config?.body ||
235
+ req?.body ||
236
+ {},
170
237
  responseBody: errorResponse?.data || {},
171
- stack: data?.error?.stack,
172
- tokenDetails: {
173
- customerId: tokenData?.customerId || "",
174
- mid: tokenData?.mid || "",
175
- tenantId: tokenData?.tenantId || "",
176
- },
238
+ stack: data?.error?.stack || "",
239
+ tokenDetails,
177
240
  }),
178
241
  };
179
242
 
@@ -185,15 +248,27 @@ function createLogger(config) {
185
248
  },
186
249
 
187
250
  logDebug: (message, data = {}) => {
188
- const traceContext = contextProvider();
251
+ const traceContext = getTraceContext();
252
+
189
253
  emitLog("debug", {
190
- trace: traceContext?.trace_id || "",
191
- span: traceContext?.span_id || "",
254
+ trace: traceContext.trace_id,
255
+ span: traceContext.span_id,
256
+ parentSpan: traceContext.parent_span_id,
192
257
  message:
193
- typeof message === "string" ? message : JSON.stringify(message),
258
+ typeof message === "string" ? message : safeJsonStringify(message),
194
259
  ...data,
195
260
  });
196
261
  },
262
+
263
+ flush: async () => {
264
+ try {
265
+ if (pinoTransport && typeof pinoTransport.flush === "function") {
266
+ await pinoTransport.flush();
267
+ }
268
+ } catch (error) {
269
+ console.error("Failed to flush pino transport:", error);
270
+ }
271
+ },
197
272
  };
198
273
 
199
274
  return loggerInstance;