forked from ecpy2020/AutoLogger
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodule.auto-logger.js
244 lines (194 loc) · 5.36 KB
/
module.auto-logger.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/**
* AutoLogger
*
* temp. use as a lib POC in payment-service-backend, will migrate to .ts after it is stabled and enriched features
*
*/
/**
* - logics:
* - loading config
* - override require
* - parsing desired paths
* - proxy the module tree
* - proxy callback
* - inject execution contexts
* - inject module, target types: (class | function), function names, argument
* - event streaming to logger
*/
const Rx = require('rxjs')
const { filter } = require('rxjs/operators')
const {
overrideModuleRequireCall,
restoreModuleRequireCall,
} = require('./submodule.override-module-require')
const { testGlobPattern } = require('./utils.test-glob-pattern')
const {
EVENT,
buildModuleProxy,
markModuleVisited,
} = require('./submodule.module-proxy-builder')
const { debugLog } = require('./config')
EVENT.MODULE_REQUIRED = 'MODULE_REQUIRED'
EVENT.MODULE_PROCESSED = 'MODULE_PROCESSED'
/**
* module config
*/
let _config
let _fileGlobPatterns = ['**']
let _logger = console.log
let _tagger = obj => obj
let _isLoggerEnabled = false
/**
* helpers
*/
/**
* config the main module and override the default config
* @param {Object} config
*/
function config(config) {
// default config
const defaultConfig = {
// asyncLogging: true?
logger: msg => console.log(msg),
// setLogSessionId: function?
attachTags: tagger => tagger,
}
_config = {
...defaultConfig,
..._config,
...config,
}
loadConfig(_config)
}
function loadConfig(config) {
if (!config) return
_fileGlobPatterns = config.files ? config.files : _fileGlobPatterns
_logger = config.logger ? config.logger : _logger
_tagger = config.tagger ? config.tagger : _tagger
}
function logEvent(event) {
try {
// try user defined logger
_logger(event)
} catch (err) {
console.log(err)
console.log(event)
}
}
function processTags(obj) {
_tagger(obj)
}
function serializeArguments(event) {
const serializedKeys = ['functionArguments', 'functionReturn']
serializedKeys.forEach(key => {
try {
event[key] = JSON.stringify(event[key], null, 2)
} catch (err) {
debugLog('serializeArguments', '#JSON.stringify', err)
}
})
return event
}
const _ = require('lodash')
function formatEvent(event) {
event = _.pickBy(event, _.identity)
return event
}
function processAndLogEvent(event) {
if (!_isLoggerEnabled) return
// add framework tags
event['tags'] = { framework: 'AUTO_LOGGER_2020' }
// TODO: provide hooks to set custom tags
/**
* - attach the app git versions
* - attach custom tags
* - set context id: request id, etc
*/
processTags(event['tags'])
// TODO: framework defaults
try {
event = serializeArguments(event)
event = formatEvent(event)
logEvent(event)
// hide hidden arguments
// serialize arguments
} catch (err) {
debugLog('processAndLogEvent', 'serializeArguments | formatEvent | logEvent', err)
}
}
// get locale time string
function getLocaleTimeString() {
return new Date().toString()
}
/**
* logger attach and restore
*/
const attachLoggers = () =>
// TODO: may have extra configs
{
_isLoggerEnabled = true
const moduleProxyCache = {}
overrideModuleRequireCall(({ modulePath, module, internal }) => {
if (testGlobPattern(_fileGlobPatterns, modulePath)) {
// get cache
if (moduleProxyCache[modulePath]) return moduleProxyCache[modulePath]
if(modulePath.includes('pointTransactionService')){
console.log('debug: module.auto-logger: overrideModuleRequireCall')
console.log(modulePath)
console.log(module)
}
moduleProxyCache[modulePath] = buildModuleProxy(
module,
modulePath,
processAndLogEvent
)
// @depreciated: not log require event for higher app start speed
processAndLogEvent({
eventType: [EVENT.MODULE_REQUIRED, EVENT.MODULE_PROCESSED],
eventTime: getLocaleTimeString(),
modulePath,
})
return moduleProxyCache[modulePath]
} else if (internal) {
return module
{
// TODO: disabled, as unexpected error will throw
// get cache
if (moduleProxyCache[modulePath]) return moduleProxyCache[modulePath]
// make the module's internal reference disable to proxy
moduleProxyCache[modulePath] = markModuleVisited(module)
// log require event
processAndLogEvent({
eventType: EVENT.MODULE_REQUIRED,
eventTime: getLocaleTimeString(),
nativeModulePath: modulePath,
})
}
return moduleProxyCache[modulePath]
} else {
return module
{
// TODO: disabled, as unexpected error will throw
// get cache
if (moduleProxyCache[modulePath]) return moduleProxyCache[modulePath]
// make the module's internal reference disable to proxy
moduleProxyCache[modulePath] = markModuleVisited(module)
// log require event
processAndLogEvent({
eventType: EVENT.MODULE_REQUIRED,
eventTime: getLocaleTimeString(),
modulePath,
})
}
return moduleProxyCache[modulePath]
}
})
}
const removeLoggers = () => {
_isLoggerEnabled = false
}
module.exports = {
config,
attachLoggers,
removeLoggers,
}