@onlineapps/service-wrapper 2.1.5 → 2.1.10
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 +1 -1
- package/src/ServiceWrapper.js +20 -14
- package/src/index.js +95 -3
- package/src/HttpLoggingMiddleware.js +0 -295
package/package.json
CHANGED
package/src/ServiceWrapper.js
CHANGED
|
@@ -22,7 +22,6 @@ const CookbookConnector = require('@onlineapps/conn-orch-cookbook');
|
|
|
22
22
|
const CacheConnector = require('@onlineapps/conn-base-cache');
|
|
23
23
|
const ErrorHandlerConnector = require('@onlineapps/conn-infra-error-handler');
|
|
24
24
|
const { ValidationOrchestrator } = require('@onlineapps/conn-orch-validator');
|
|
25
|
-
const { createHttpLogging } = require('./HttpLoggingMiddleware');
|
|
26
25
|
|
|
27
26
|
const INFRA_QUEUE_OWNERS = {
|
|
28
27
|
'workflow.init': 'Gateway (api_gateway)',
|
|
@@ -274,6 +273,7 @@ class ServiceWrapper {
|
|
|
274
273
|
* @private
|
|
275
274
|
*/
|
|
276
275
|
_logPhase(phase, phaseName, status, error = null, duration = null) {
|
|
276
|
+
const serviceName = this.config.service?.name || 'unnamed-service';
|
|
277
277
|
const logMsg = `[FÁZE ${phase}] ${phaseName} - ${status}`;
|
|
278
278
|
const logData = {
|
|
279
279
|
phase,
|
|
@@ -282,8 +282,19 @@ class ServiceWrapper {
|
|
|
282
282
|
timestamp: new Date().toISOString(),
|
|
283
283
|
duration,
|
|
284
284
|
error: error ? { message: error.message, stack: error.stack } : null,
|
|
285
|
-
serviceName
|
|
285
|
+
serviceName
|
|
286
286
|
};
|
|
287
|
+
|
|
288
|
+
// Structured log for service wrapper phases
|
|
289
|
+
console.log(`[${serviceName}:service-wrapper:init:PHASE_${status}] ${JSON.stringify({
|
|
290
|
+
timestamp: new Date().toISOString(),
|
|
291
|
+
service: serviceName,
|
|
292
|
+
phase,
|
|
293
|
+
phase_name: phaseName,
|
|
294
|
+
status,
|
|
295
|
+
duration_ms: duration,
|
|
296
|
+
error: error ? error.message : null
|
|
297
|
+
})}`);
|
|
287
298
|
|
|
288
299
|
if (status === 'PASSED') {
|
|
289
300
|
this.logger?.info(logMsg, logData);
|
|
@@ -559,17 +570,9 @@ class ServiceWrapper {
|
|
|
559
570
|
// Logger is now available, use it
|
|
560
571
|
this.logger.info('Monitoring connector initialized', { logsDir });
|
|
561
572
|
|
|
562
|
-
//
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
const httpLogging = createHttpLogging(() => this.logger, loggingConfig);
|
|
566
|
-
|
|
567
|
-
// Store for access from outside if needed
|
|
568
|
-
this.httpLogging = httpLogging;
|
|
569
|
-
|
|
570
|
-
// Apply middlewares to Express app
|
|
571
|
-
httpLogging.applyTo(this.app);
|
|
572
|
-
}
|
|
573
|
+
// NOTE: HTTP logging middleware is applied directly in service's app.js
|
|
574
|
+
// using middlewares/httpLogging.js with lazy logger access via lib/logger.js
|
|
575
|
+
// Configuration is in conn-config/config.json -> wrapper.logging (for reference)
|
|
573
576
|
}
|
|
574
577
|
|
|
575
578
|
/**
|
|
@@ -1264,10 +1267,13 @@ class ServiceWrapper {
|
|
|
1264
1267
|
}
|
|
1265
1268
|
|
|
1266
1269
|
// Normalize message format: Gateway sends workflowId, orchestrator expects workflow_id
|
|
1270
|
+
// V2 format uses step_id, V1 used id - support both for backwards compatibility during migration
|
|
1271
|
+
const firstStep = message.cookbook?.steps?.[0];
|
|
1272
|
+
const firstStepId = firstStep?.step_id || firstStep?.id;
|
|
1267
1273
|
const normalizedMessage = {
|
|
1268
1274
|
...message,
|
|
1269
1275
|
workflow_id: message.workflow_id || message.workflowId,
|
|
1270
|
-
current_step: message.current_step ||
|
|
1276
|
+
current_step: message.current_step || firstStepId
|
|
1271
1277
|
};
|
|
1272
1278
|
|
|
1273
1279
|
// Validate normalized message has required fields
|
package/src/index.js
CHANGED
|
@@ -11,17 +11,109 @@
|
|
|
11
11
|
* @since 2.0.0
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
const path = require('path');
|
|
14
15
|
const ServiceWrapper = require('./ServiceWrapper');
|
|
15
16
|
const { ConfigLoader } = require('./ConfigLoader');
|
|
16
|
-
const HttpLoggingMiddleware = require('./HttpLoggingMiddleware');
|
|
17
17
|
|
|
18
18
|
// Note: WorkflowProcessor and ApiCaller functionality has been moved to connectors:
|
|
19
19
|
// - WorkflowProcessor -> @onlineapps/conn-orch-orchestrator
|
|
20
20
|
// - ApiCaller -> @onlineapps/conn-orch-api-mapper
|
|
21
21
|
|
|
22
|
+
// Note: HTTP logging middleware is now in each service's middlewares/httpLogging.js
|
|
23
|
+
// It uses lazy logger access via lib/logger.js (set after wrapper.initialize())
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Bootstrap a business service with standard configuration
|
|
27
|
+
* Eliminates boilerplate code in service index.js
|
|
28
|
+
*
|
|
29
|
+
* @param {string} serviceRoot - Service root directory (__dirname from index.js)
|
|
30
|
+
* @param {Object} [options] - Optional overrides
|
|
31
|
+
* @param {Object} [options.app] - Express app (default: require('./src/app'))
|
|
32
|
+
* @param {Object} [options.config] - Config (default: require('./src/config'))
|
|
33
|
+
* @param {Function} [options.setLogger] - Logger setter (default: require('./src/lib/logger').setLogger)
|
|
34
|
+
* @returns {Promise<{wrapper: ServiceWrapper, server: Object}>}
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // index.js (minimal)
|
|
38
|
+
* require('@onlineapps/service-wrapper').bootstrap(__dirname);
|
|
39
|
+
*/
|
|
40
|
+
async function bootstrap(serviceRoot, options = {}) {
|
|
41
|
+
// Load dotenv first
|
|
42
|
+
require('dotenv').config({ path: path.join(serviceRoot, '.env') });
|
|
43
|
+
|
|
44
|
+
// Load app, config, and logger from service
|
|
45
|
+
const app = options.app || require(path.join(serviceRoot, 'src', 'app'));
|
|
46
|
+
const config = options.config || require(path.join(serviceRoot, 'src', 'config'));
|
|
47
|
+
|
|
48
|
+
// Logger setter is optional - some services may not have it
|
|
49
|
+
let setLogger = options.setLogger;
|
|
50
|
+
if (!setLogger) {
|
|
51
|
+
try {
|
|
52
|
+
const loggerModule = require(path.join(serviceRoot, 'src', 'lib', 'logger'));
|
|
53
|
+
setLogger = loggerModule.setLogger;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// No logger module - that's OK
|
|
56
|
+
setLogger = () => {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(`Starting ${config.service.name} v${config.service.version}...`);
|
|
61
|
+
|
|
62
|
+
// 1. Start HTTP server
|
|
63
|
+
const PORT = config.service.port;
|
|
64
|
+
const server = app.listen(PORT, () => {
|
|
65
|
+
console.log(`✓ HTTP server listening on port ${PORT}`);
|
|
66
|
+
console.log(`✓ Health check: http://localhost:${PORT}/health`);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// 2. Initialize Service Wrapper (MQ, Registry, Monitoring, etc.)
|
|
70
|
+
const wrapper = new ServiceWrapper({
|
|
71
|
+
app,
|
|
72
|
+
server,
|
|
73
|
+
serviceRoot,
|
|
74
|
+
config: {
|
|
75
|
+
service: {
|
|
76
|
+
name: config.service.name,
|
|
77
|
+
version: config.service.version,
|
|
78
|
+
port: config.service.port,
|
|
79
|
+
url: process.env.SERVICE_URL || `http://localhost:${PORT}`
|
|
80
|
+
},
|
|
81
|
+
wrapper: config.wrapper
|
|
82
|
+
},
|
|
83
|
+
operations: config.operations,
|
|
84
|
+
validationProof: config.validationProof
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await wrapper.initialize();
|
|
88
|
+
|
|
89
|
+
// Initialize centralized logger for use throughout the service
|
|
90
|
+
if (setLogger && wrapper.logger) {
|
|
91
|
+
setLogger(wrapper.logger);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(`✓ Service Wrapper initialized (MQ, Registry, Monitoring)`);
|
|
95
|
+
console.log(`✓ Local logs: logs/app.{date}.log`);
|
|
96
|
+
console.log(`✓ ${config.service.name} ready\n`);
|
|
97
|
+
|
|
98
|
+
// 3. Setup graceful shutdown
|
|
99
|
+
const shutdown = async () => {
|
|
100
|
+
console.log('\nShutting down gracefully...');
|
|
101
|
+
await wrapper.shutdown();
|
|
102
|
+
server.close(() => {
|
|
103
|
+
console.log('Server closed');
|
|
104
|
+
process.exit(0);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
process.on('SIGTERM', shutdown);
|
|
109
|
+
process.on('SIGINT', shutdown);
|
|
110
|
+
|
|
111
|
+
return { wrapper, server };
|
|
112
|
+
}
|
|
113
|
+
|
|
22
114
|
module.exports = ServiceWrapper;
|
|
23
115
|
module.exports.ServiceWrapper = ServiceWrapper;
|
|
24
116
|
module.exports.ConfigLoader = ConfigLoader;
|
|
25
|
-
module.exports.
|
|
117
|
+
module.exports.bootstrap = bootstrap;
|
|
26
118
|
module.exports.default = ServiceWrapper;
|
|
27
|
-
module.exports.VERSION = '2.1.
|
|
119
|
+
module.exports.VERSION = '2.1.10';
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* HTTP Logging Middleware
|
|
5
|
-
*
|
|
6
|
-
* Configurable request/response logging for business services.
|
|
7
|
-
* Logs to both local files and central monitoring via wrapper.logger.
|
|
8
|
-
*
|
|
9
|
-
* Configuration in conn-config/config.json:
|
|
10
|
-
* {
|
|
11
|
-
* "wrapper": {
|
|
12
|
-
* "logging": {
|
|
13
|
-
* "enabled": true, // Master switch
|
|
14
|
-
* "level": "info", // Log level: debug, info, warn, error
|
|
15
|
-
* "requests": {
|
|
16
|
-
* "enabled": true, // Log incoming requests
|
|
17
|
-
* "bodies": true, // Include request bodies
|
|
18
|
-
* "headers": false // Include request headers (careful with auth tokens)
|
|
19
|
-
* },
|
|
20
|
-
* "responses": {
|
|
21
|
-
* "enabled": true, // Log outgoing responses
|
|
22
|
-
* "bodies": true // Include response bodies
|
|
23
|
-
* },
|
|
24
|
-
* "excludePaths": [ // Paths to exclude from logging
|
|
25
|
-
* "/health",
|
|
26
|
-
* "/status",
|
|
27
|
-
* "/metrics"
|
|
28
|
-
* ]
|
|
29
|
-
* }
|
|
30
|
-
* }
|
|
31
|
-
* }
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
const DEFAULT_CONFIG = {
|
|
35
|
-
enabled: true,
|
|
36
|
-
level: 'info',
|
|
37
|
-
requests: {
|
|
38
|
-
enabled: true,
|
|
39
|
-
bodies: true,
|
|
40
|
-
headers: false
|
|
41
|
-
},
|
|
42
|
-
responses: {
|
|
43
|
-
enabled: true,
|
|
44
|
-
bodies: true
|
|
45
|
-
},
|
|
46
|
-
excludePaths: ['/health', '/status', '/metrics', '/specification']
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Merge user config with defaults
|
|
51
|
-
*/
|
|
52
|
-
function mergeConfig(userConfig = {}) {
|
|
53
|
-
return {
|
|
54
|
-
enabled: userConfig.enabled ?? DEFAULT_CONFIG.enabled,
|
|
55
|
-
level: userConfig.level ?? DEFAULT_CONFIG.level,
|
|
56
|
-
requests: {
|
|
57
|
-
enabled: userConfig.requests?.enabled ?? DEFAULT_CONFIG.requests.enabled,
|
|
58
|
-
bodies: userConfig.requests?.bodies ?? DEFAULT_CONFIG.requests.bodies,
|
|
59
|
-
headers: userConfig.requests?.headers ?? DEFAULT_CONFIG.requests.headers
|
|
60
|
-
},
|
|
61
|
-
responses: {
|
|
62
|
-
enabled: userConfig.responses?.enabled ?? DEFAULT_CONFIG.responses.enabled,
|
|
63
|
-
bodies: userConfig.responses?.bodies ?? DEFAULT_CONFIG.responses.bodies
|
|
64
|
-
},
|
|
65
|
-
excludePaths: userConfig.excludePaths ?? DEFAULT_CONFIG.excludePaths
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Check if path should be excluded from logging
|
|
71
|
-
*/
|
|
72
|
-
function shouldExclude(path, excludePaths) {
|
|
73
|
-
return excludePaths.some(excluded => {
|
|
74
|
-
// Exact match or prefix match
|
|
75
|
-
return path === excluded || path.startsWith(excluded + '/');
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Safely stringify body for logging
|
|
81
|
-
*/
|
|
82
|
-
function safeStringify(body) {
|
|
83
|
-
if (body === undefined || body === null) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (typeof body === 'string') {
|
|
88
|
-
// Already a string - could be JSON or plain text
|
|
89
|
-
try {
|
|
90
|
-
// Try to parse and re-stringify for consistent format
|
|
91
|
-
return JSON.parse(body);
|
|
92
|
-
} catch {
|
|
93
|
-
return body;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (Buffer.isBuffer(body)) {
|
|
98
|
-
return `[Buffer: ${body.length} bytes]`;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Object - return as is (will be serialized by logger)
|
|
102
|
-
return body;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Create request logging middleware
|
|
107
|
-
* @param {Function} getLogger - Function that returns logger instance
|
|
108
|
-
* @param {Object} config - Logging configuration
|
|
109
|
-
* @returns {Function} Express middleware
|
|
110
|
-
*/
|
|
111
|
-
function createRequestLogger(getLogger, config) {
|
|
112
|
-
const cfg = mergeConfig(config);
|
|
113
|
-
|
|
114
|
-
return (req, res, next) => {
|
|
115
|
-
// Skip if disabled
|
|
116
|
-
if (!cfg.enabled || !cfg.requests.enabled) {
|
|
117
|
-
return next();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Skip excluded paths
|
|
121
|
-
if (shouldExclude(req.path, cfg.excludePaths)) {
|
|
122
|
-
return next();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const logger = getLogger();
|
|
126
|
-
if (!logger) {
|
|
127
|
-
return next();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Build log data
|
|
131
|
-
const logData = {
|
|
132
|
-
type: 'request',
|
|
133
|
-
method: req.method,
|
|
134
|
-
path: req.path,
|
|
135
|
-
url: req.originalUrl,
|
|
136
|
-
ip: req.ip || req.connection?.remoteAddress,
|
|
137
|
-
userAgent: req.get('user-agent')
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// Add query params if present
|
|
141
|
-
if (req.query && Object.keys(req.query).length > 0) {
|
|
142
|
-
logData.query = req.query;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Add request body if enabled
|
|
146
|
-
if (cfg.requests.bodies && req.body && Object.keys(req.body).length > 0) {
|
|
147
|
-
logData.body = safeStringify(req.body);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Add headers if enabled
|
|
151
|
-
if (cfg.requests.headers) {
|
|
152
|
-
// Filter out sensitive headers
|
|
153
|
-
const headers = { ...req.headers };
|
|
154
|
-
delete headers.authorization;
|
|
155
|
-
delete headers.cookie;
|
|
156
|
-
logData.headers = headers;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Store start time for response duration
|
|
160
|
-
req._loggingStartTime = Date.now();
|
|
161
|
-
|
|
162
|
-
logger.info('HTTP Request', logData);
|
|
163
|
-
next();
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Create response logging middleware
|
|
169
|
-
* @param {Function} getLogger - Function that returns logger instance
|
|
170
|
-
* @param {Object} config - Logging configuration
|
|
171
|
-
* @returns {Function} Express middleware
|
|
172
|
-
*/
|
|
173
|
-
function createResponseLogger(getLogger, config) {
|
|
174
|
-
const cfg = mergeConfig(config);
|
|
175
|
-
|
|
176
|
-
return (req, res, next) => {
|
|
177
|
-
// Skip if disabled
|
|
178
|
-
if (!cfg.enabled || !cfg.responses.enabled) {
|
|
179
|
-
return next();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Skip excluded paths
|
|
183
|
-
if (shouldExclude(req.path, cfg.excludePaths)) {
|
|
184
|
-
return next();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const logger = getLogger();
|
|
188
|
-
if (!logger) {
|
|
189
|
-
return next();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Store original methods
|
|
193
|
-
const originalSend = res.send;
|
|
194
|
-
const originalJson = res.json;
|
|
195
|
-
|
|
196
|
-
// Flag to prevent double logging
|
|
197
|
-
let logged = false;
|
|
198
|
-
|
|
199
|
-
const logResponse = (body) => {
|
|
200
|
-
if (logged) return;
|
|
201
|
-
logged = true;
|
|
202
|
-
|
|
203
|
-
const duration = req._loggingStartTime
|
|
204
|
-
? Date.now() - req._loggingStartTime
|
|
205
|
-
: null;
|
|
206
|
-
|
|
207
|
-
const logData = {
|
|
208
|
-
type: 'response',
|
|
209
|
-
method: req.method,
|
|
210
|
-
path: req.path,
|
|
211
|
-
statusCode: res.statusCode,
|
|
212
|
-
statusMessage: res.statusMessage
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
if (duration !== null) {
|
|
216
|
-
logData.durationMs = duration;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Add response body if enabled
|
|
220
|
-
if (cfg.responses.bodies && body !== undefined) {
|
|
221
|
-
logData.body = safeStringify(body);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Choose log level based on status code
|
|
225
|
-
if (res.statusCode >= 500) {
|
|
226
|
-
logger.error('HTTP Response', logData);
|
|
227
|
-
} else if (res.statusCode >= 400) {
|
|
228
|
-
logger.warn('HTTP Response', logData);
|
|
229
|
-
} else {
|
|
230
|
-
logger.info('HTTP Response', logData);
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// Override res.send
|
|
235
|
-
res.send = function(body) {
|
|
236
|
-
logResponse(body);
|
|
237
|
-
return originalSend.call(this, body);
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
// Override res.json
|
|
241
|
-
res.json = function(body) {
|
|
242
|
-
logResponse(body);
|
|
243
|
-
return originalJson.call(this, body);
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
next();
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Create combined HTTP logging middleware
|
|
252
|
-
* @param {Function} getLogger - Function that returns logger instance
|
|
253
|
-
* @param {Object} config - Logging configuration from wrapper.logging
|
|
254
|
-
* @returns {Object} Object with requestLogger and responseLogger middlewares
|
|
255
|
-
*/
|
|
256
|
-
function createHttpLogging(getLogger, config = {}) {
|
|
257
|
-
const cfg = mergeConfig(config);
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
config: cfg,
|
|
261
|
-
requestLogger: createRequestLogger(getLogger, config),
|
|
262
|
-
responseLogger: createResponseLogger(getLogger, config),
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Apply both middlewares to Express app
|
|
266
|
-
* @param {Object} app - Express application
|
|
267
|
-
*/
|
|
268
|
-
applyTo(app) {
|
|
269
|
-
if (!cfg.enabled) {
|
|
270
|
-
console.log('[HttpLogging] Disabled by configuration');
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (cfg.requests.enabled) {
|
|
275
|
-
app.use(this.requestLogger);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (cfg.responses.enabled) {
|
|
279
|
-
app.use(this.responseLogger);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
console.log(`[HttpLogging] Enabled (requests: ${cfg.requests.enabled}, responses: ${cfg.responses.enabled}, bodies: ${cfg.requests.bodies})`);
|
|
283
|
-
console.log(`[HttpLogging] Excluded paths: ${cfg.excludePaths.join(', ')}`);
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
module.exports = {
|
|
289
|
-
createHttpLogging,
|
|
290
|
-
createRequestLogger,
|
|
291
|
-
createResponseLogger,
|
|
292
|
-
mergeConfig,
|
|
293
|
-
DEFAULT_CONFIG
|
|
294
|
-
};
|
|
295
|
-
|