@onlineapps/infra-logger 1.0.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.
Files changed (2) hide show
  1. package/index.js +270 -0
  2. package/package.json +16 -0
package/index.js ADDED
@@ -0,0 +1,270 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Structured logging helper for infrastructure services
5
+ *
6
+ * Format: [context_id:service:layer:action] {JSON}
7
+ *
8
+ * Context can be:
9
+ * - workflow_id for workflow-related logs
10
+ * - serviceName for lifecycle logs
11
+ *
12
+ * NOTE: Health checks are NOT logged (too verbose). All other infrastructure
13
+ * operations should be logged with appropriate detail (input, output, process, fail).
14
+ *
15
+ * Usage:
16
+ * const { createLogger } = require('@onlineapps/infra-logger');
17
+ * const log = createLogger('api_gateway', 'router');
18
+ *
19
+ * // Workflow logs
20
+ * log.input('wf-123', 'WORKFLOW_RECEIVED', {
21
+ * handler: 'POST /api/workflow/start',
22
+ * function: 'initWorkflowRoutes',
23
+ * input: { method: 'POST', path: '/api/workflow/start', cookbook_summary: {...} }
24
+ * });
25
+ *
26
+ * log.output('wf-123', 'WORKFLOW_PUBLISHED', {
27
+ * handler: 'publishToQueue',
28
+ * function: 'mqClient.publish',
29
+ * input: { queue: 'workflow.init', message_size: 1234 },
30
+ * output: { workflow_id: 'wf-123', status: 'published' }
31
+ * });
32
+ *
33
+ * // Lifecycle logs
34
+ * log.lifecycle('hello-service', 'HEARTBEAT_RECEIVED', {
35
+ * handler: 'processHeartbeatMessage',
36
+ * function: 'registryModel.touchHeartbeat',
37
+ * input: { serviceName: 'hello-service', version: '1.0.0' }
38
+ * });
39
+ */
40
+
41
+ /**
42
+ * Sanitize input/output for logging (keep it concise, not full payload)
43
+ * @param {*} value - Value to sanitize
44
+ * @param {number} maxDepth - Maximum depth for nested objects
45
+ * @returns {*} Sanitized value
46
+ */
47
+ function sanitizeValue(value, maxDepth = 2) {
48
+ if (maxDepth <= 0) {
49
+ return { _truncated: true };
50
+ }
51
+
52
+ if (value === null || value === undefined) {
53
+ return value;
54
+ }
55
+
56
+ if (typeof value === 'string') {
57
+ // Truncate long strings
58
+ if (value.length > 500) {
59
+ return value.substring(0, 500) + '... (truncated)';
60
+ }
61
+ return value;
62
+ }
63
+
64
+ if (typeof value === 'number' || typeof value === 'boolean') {
65
+ return value;
66
+ }
67
+
68
+ if (Buffer.isBuffer(value)) {
69
+ return { type: 'Buffer', size: value.length };
70
+ }
71
+
72
+ if (Array.isArray(value)) {
73
+ if (value.length > 10) {
74
+ return { type: 'array', length: value.length, first_3: value.slice(0, 3).map(v => sanitizeValue(v, maxDepth - 1)) };
75
+ }
76
+ return value.map(v => sanitizeValue(v, maxDepth - 1));
77
+ }
78
+
79
+ if (typeof value === 'object') {
80
+ const keys = Object.keys(value);
81
+ if (keys.length > 20) {
82
+ return { type: 'object', keys_count: keys.length, keys: keys.slice(0, 10) };
83
+ }
84
+ const result = {};
85
+ for (const key of keys) {
86
+ result[key] = sanitizeValue(value[key], maxDepth - 1);
87
+ }
88
+ return result;
89
+ }
90
+
91
+ return String(value);
92
+ }
93
+
94
+ /**
95
+ * Create a structured logger for an infrastructure service
96
+ * @param {string} service - Service name (e.g., 'api_gateway', 'api_monitoring')
97
+ * @param {string} layer - Layer name (e.g., 'router', 'consumer', 'dispatcher')
98
+ * @returns {Object} Logger with input, output, process, fail, lifecycle methods
99
+ */
100
+ function createLogger(service, layer) {
101
+ const formatLog = (contextId, action, data = {}) => {
102
+ const logEntry = {
103
+ timestamp: new Date().toISOString(),
104
+ workflow_id: contextId, // For workflow logs
105
+ context: contextId, // Generic context (can be workflow_id or serviceName)
106
+ service,
107
+ layer,
108
+ action,
109
+ ...data
110
+ };
111
+ const prefix = `[${contextId}:${service}:${layer}:${action}]`;
112
+ return { prefix, logEntry };
113
+ };
114
+
115
+ return {
116
+ /**
117
+ * Log input (what was received)
118
+ * @param {string} contextId - Workflow ID or service name
119
+ * @param {string} action - Action name
120
+ * @param {Object} data - Data with handler, function, input
121
+ */
122
+ input: (contextId, action, data = {}) => {
123
+ const { handler, function: fn, input, ...rest } = data;
124
+ const { prefix, logEntry } = formatLog(contextId, action, {
125
+ handler,
126
+ function: fn,
127
+ input: sanitizeValue(input),
128
+ ...rest
129
+ });
130
+ console.log(prefix, JSON.stringify(logEntry));
131
+ },
132
+
133
+ /**
134
+ * Log output (what was returned/sent)
135
+ * @param {string} contextId - Workflow ID or service name
136
+ * @param {string} action - Action name
137
+ * @param {Object} data - Data with handler, function, input, output
138
+ */
139
+ output: (contextId, action, data = {}) => {
140
+ const { handler, function: fn, input, output, ...rest } = data;
141
+ const { prefix, logEntry } = formatLog(contextId, action, {
142
+ handler,
143
+ function: fn,
144
+ input: sanitizeValue(input),
145
+ output: sanitizeValue(output),
146
+ ...rest
147
+ });
148
+ console.log(prefix, JSON.stringify(logEntry));
149
+ },
150
+
151
+ /**
152
+ * Log process (transformation/processing)
153
+ * @param {string} contextId - Workflow ID or service name
154
+ * @param {string} action - Action name
155
+ * @param {Object} data - Data with handler, function, input, output
156
+ */
157
+ process: (contextId, action, data = {}) => {
158
+ const { handler, function: fn, input, output, ...rest } = data;
159
+ const { prefix, logEntry } = formatLog(contextId, action, {
160
+ handler,
161
+ function: fn,
162
+ input: sanitizeValue(input),
163
+ output: sanitizeValue(output),
164
+ ...rest
165
+ });
166
+ console.log(prefix, JSON.stringify(logEntry));
167
+ },
168
+
169
+ /**
170
+ * Log failure (errors)
171
+ * @param {string} contextId - Workflow ID or service name
172
+ * @param {string} action - Action name
173
+ * @param {Object} data - Data with handler, function, error, expected, received, input
174
+ */
175
+ fail: (contextId, action, data = {}) => {
176
+ const { handler, function: fn, error, expected, received, input, ...rest } = data;
177
+ const { prefix, logEntry } = formatLog(contextId, action, {
178
+ handler,
179
+ function: fn,
180
+ error: error?.message || error || 'Unknown error',
181
+ error_type: error?.constructor?.name || typeof error,
182
+ expected,
183
+ received,
184
+ input: sanitizeValue(input),
185
+ ...rest
186
+ });
187
+ console.error(prefix, JSON.stringify(logEntry));
188
+ },
189
+
190
+ /**
191
+ * Log lifecycle events (service registration, heartbeats, etc.)
192
+ * @param {string} serviceName - Service name (context)
193
+ * @param {string} action - Action name
194
+ * @param {Object} data - Data with handler, function, input, output
195
+ */
196
+ lifecycle: (serviceName, action, data = {}) => {
197
+ const { handler, function: fn, input, output, ...rest } = data;
198
+ const logEntry = {
199
+ timestamp: new Date().toISOString(),
200
+ context: serviceName,
201
+ service,
202
+ layer,
203
+ action,
204
+ handler,
205
+ function: fn,
206
+ input: sanitizeValue(input),
207
+ output: sanitizeValue(output),
208
+ ...rest
209
+ };
210
+ const prefix = `[${serviceName}:${service}:${layer}:${action}]`;
211
+ console.log(prefix, JSON.stringify(logEntry));
212
+ },
213
+
214
+ /**
215
+ * Generic log (backward compatibility)
216
+ * @param {string} contextId - Workflow ID or service name
217
+ * @param {string} action - Action name
218
+ * @param {Object} data - Additional data
219
+ */
220
+ info: (contextId, action, data = {}) => {
221
+ const { prefix, logEntry } = formatLog(contextId, action, data);
222
+ console.log(prefix, JSON.stringify(logEntry));
223
+ },
224
+
225
+ warn: (contextId, action, data = {}) => {
226
+ const { prefix, logEntry } = formatLog(contextId, action, data);
227
+ console.warn(prefix, JSON.stringify(logEntry));
228
+ },
229
+
230
+ error: (contextId, action, data = {}) => {
231
+ const { prefix, logEntry } = formatLog(contextId, action, data);
232
+ console.error(prefix, JSON.stringify(logEntry));
233
+ }
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Standalone structured log function (for inline use without creating logger)
239
+ * @param {string} workflowId - Workflow ID
240
+ * @param {string} service - Service name
241
+ * @param {string} layer - Layer name
242
+ * @param {string} action - Action name
243
+ * @param {Object} data - Additional data
244
+ * @param {string} level - Log level (info, warn, error)
245
+ */
246
+ function structuredLog(workflowId, service, layer, action, data = {}, level = 'info') {
247
+ const logEntry = {
248
+ timestamp: new Date().toISOString(),
249
+ workflow_id: workflowId,
250
+ service,
251
+ layer,
252
+ action,
253
+ ...data
254
+ };
255
+ const prefix = `[${workflowId}:${service}:${layer}:${action}]`;
256
+
257
+ if (level === 'error') {
258
+ console.error(prefix, JSON.stringify(logEntry));
259
+ } else if (level === 'warn') {
260
+ console.warn(prefix, JSON.stringify(logEntry));
261
+ } else {
262
+ console.log(prefix, JSON.stringify(logEntry));
263
+ }
264
+ }
265
+
266
+ module.exports = {
267
+ createLogger,
268
+ structuredLog
269
+ };
270
+
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@onlineapps/infra-logger",
3
+ "version": "1.0.0",
4
+ "description": "Structured logging helper for infrastructure services",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"No tests yet\""
8
+ },
9
+ "keywords": ["logging", "infrastructure", "structured"],
10
+ "author": "OnlineApps",
11
+ "license": "UNLICENSED",
12
+ "publishConfig": {
13
+ "registry": "https://registry.npmjs.org/"
14
+ }
15
+ }
16
+