@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.
- package/index.js +270 -0
- 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
|
+
|