ac-logger 2.2.3 → 2.3.1
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/.ncurc.js +8 -0
- package/CHANGELOG.md +25 -0
- package/README.md +8 -1
- package/index.js +104 -34
- package/package.json +6 -6
package/.ncurc.js
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
<a name="2.3.1"></a>
|
|
2
|
+
|
|
3
|
+
## [2.3.1](https://github.com/admiralcloud/ac-logger/compare/v2.3.0..v2.3.1) (2024-03-10 12:31:44)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fix
|
|
7
|
+
|
|
8
|
+
* **App:** Minor improvement for backwards compatibily | MP | [462a6c38269dafccde1f18ffd8a079ac19022269](https://github.com/admiralcloud/ac-logger/commit/462a6c38269dafccde1f18ffd8a079ac19022269)
|
|
9
|
+
Minor improvement for backwards compatibility
|
|
10
|
+
Related issues: [undefined/undefined#master](undefined/browse/master)
|
|
11
|
+
<a name="2.3.0"></a>
|
|
12
|
+
|
|
13
|
+
# [2.3.0](https://github.com/admiralcloud/ac-logger/compare/v2.2.3..v2.3.0) (2024-03-10 11:09:52)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Feature
|
|
17
|
+
|
|
18
|
+
* **App:** Improved application logs | MP | [5d505c148e44045daf40909092542d7e96949f74](https://github.com/admiralcloud/ac-logger/commit/5d505c148e44045daf40909092542d7e96949f74)
|
|
19
|
+
ac-logger now supports machine readable application logs (if enabled). This way analyzing logs in Cloudwatch (or other apps) will be much easier.
|
|
20
|
+
Related issues: [undefined/undefined#master](undefined/browse/master)
|
|
21
|
+
### Chores
|
|
22
|
+
|
|
23
|
+
* **App:** Updated packages | MP | [d2fe6c8ade4f1058e0315c30a8f4c85686f03181](https://github.com/admiralcloud/ac-logger/commit/d2fe6c8ade4f1058e0315c30a8f4c85686f03181)
|
|
24
|
+
Updated packages
|
|
25
|
+
Related issues: [undefined/undefined#master](undefined/browse/master)
|
|
1
26
|
<a name="2.2.3"></a>
|
|
2
27
|
|
|
3
28
|
## [2.2.3](https://github.com/admiralcloud/ac-logger/compare/v2.2.2..v2.2.3) (2024-01-01 13:10:51)
|
package/README.md
CHANGED
|
@@ -14,12 +14,16 @@ const aclog = require('ac-logger')
|
|
|
14
14
|
const logConfig = {
|
|
15
15
|
prefixFields: [
|
|
16
16
|
{ field: 'jobId', short: 'J' },
|
|
17
|
-
]
|
|
17
|
+
],
|
|
18
|
+
applicationLogs: {
|
|
19
|
+
enabled: true // if true, JSON formatted, machine readable application logs will be created into logs directory
|
|
20
|
+
}
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
app = {} //
|
|
21
24
|
app.log = aclog({ prefixFields: logConfig.prefixFields }).acLogger
|
|
22
25
|
|
|
26
|
+
// DEPRECATED
|
|
23
27
|
let logMeta = {
|
|
24
28
|
fileName: 'UploadS3',
|
|
25
29
|
functionName: 'upload',
|
|
@@ -28,6 +32,9 @@ let logMeta = {
|
|
|
28
32
|
app.log.info('Message for %s', 'some string', { meta: logMeta })
|
|
29
33
|
// -> INFO UploadS3 | upload | J 193 | Message for some string
|
|
30
34
|
|
|
35
|
+
// MODERN APPROACH (that is best suited for machine and human readability)
|
|
36
|
+
app.log.info('Message for some string', { functionName: 'upload', ... }) // all meta data like "functionName" will be available in application logs
|
|
37
|
+
|
|
31
38
|
```
|
|
32
39
|
|
|
33
40
|
## Error logging
|
package/index.js
CHANGED
|
@@ -1,43 +1,125 @@
|
|
|
1
1
|
const _ = require('lodash')
|
|
2
2
|
const moment = require('moment')
|
|
3
3
|
const path = require('path')
|
|
4
|
+
const util = require('util')
|
|
4
5
|
|
|
5
6
|
const { createLogger, format, transports, addColors } = require('winston')
|
|
6
7
|
require('winston-daily-rotate-file')
|
|
7
8
|
|
|
8
|
-
module.exports = ({ prefixFields = [], timestampFormat = 'YYYY-MM-DD HH:mm:ss', level = (process.env.NODE_ENV === 'production' ? 'info' : 'verbose'), headLength = 80, padLength = 12, customLevels, transporters = [] } = {}) => {
|
|
9
|
+
module.exports = ({ prefixFields = [], timestampFormat = 'YYYY-MM-DD HH:mm:ss', level = (process.env.NODE_ENV === 'production' ? 'info' : 'verbose'), headLength = 80, padLength = 12, customLevels, transporters = [], applicationLogs = { enabled: false } } = {}) => {
|
|
9
10
|
const precisionMap = [
|
|
10
11
|
{ precision: 1e6, name: 'ms' },
|
|
11
12
|
{ precision: 1e3, name: 'µs' },
|
|
12
13
|
{ precision: 1, name: 'ns' },
|
|
13
14
|
]
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
const splatFormatter = format((data) => {
|
|
17
|
+
// Check if splat is available and the message contains placeholder(s)
|
|
18
|
+
if (data[Symbol.for('splat')] && /%[sjdifoO%]/.test(data.message)) {
|
|
19
|
+
data.message = util.format(data.message, ...data[Symbol.for('splat')])
|
|
20
|
+
}
|
|
21
|
+
return data
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const myFormat = format.printf((data) => {
|
|
25
|
+
const level = data[Symbol.for('level')]
|
|
26
|
+
// http log format
|
|
27
|
+
if (level === 'http' && data?.controller) {
|
|
28
|
+
// display accessKey, link (if applicable)
|
|
29
|
+
let message = data?.message ? ` ${data.message}` : ''
|
|
30
|
+
let cuid = data?.customerId ? data?.customerId : ''
|
|
31
|
+
if (data?.userId) cuid += `/${data.userId} `
|
|
32
|
+
return `${data?.timestamp} ${data?.level} ${data?.ip} ${data?.iso2 || ''} ${data?.accessKey || ''} ${cuid}${data?.controller} ${data?.action}${message} | ${data?.statusCode} | ${data?.performance?.executionTime}ms`
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const meta = data?.meta
|
|
36
|
+
//
|
|
37
|
+
let fileName = _.get(meta, 'fileName') ? _.get(meta, 'fileName') + ' | ' : ''
|
|
38
|
+
let functionName = _.get(meta, 'functionName') ? _.get(meta, 'functionName') + ' | ' : _.get(data, 'functionName') ? _.get(data, 'functionName') + ' | ' : ''
|
|
39
|
+
let subName = _.get(meta, 'sub') ? _.get(meta, 'sub') + ' | ' : _.get(data, 'sub') ? _.get(data, 'sub') + ' | ' : ''
|
|
40
|
+
|
|
41
|
+
// modern approach (fileName, functionName, sub) // TBD with team
|
|
42
|
+
let functionIdentifier = _.get(data, 'functionIdentifier') ? _.get(data, 'functionIdentifier') + ' | ' : ''
|
|
43
|
+
if (!fileName && _.get(data, 'fileName')) fileName = _.get(data, 'fileName')
|
|
44
|
+
if (!functionName && _.get(data, 'functionName')) functionName = _.get(data, 'functionName')
|
|
45
|
+
if (!subName && _.get(data, 'sub')) subName = _.get(data, 'sub')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
let message = data?.message
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
let prefix = []
|
|
52
|
+
let dataFromPrefix = []
|
|
53
|
+
_.forEach(prefixFields, item => {
|
|
54
|
+
if (_.get(meta, _.get(item, 'field'))) {
|
|
55
|
+
prefix.push(_.get(item, 'short'))
|
|
56
|
+
dataFromPrefix.push(_.get(meta, _.get(item, 'field')))
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
const prefixData = _.size(prefix) ? (_.join(prefix, '/') + ' ' + _.join(dataFromPrefix, '/') + ' | ') : ''
|
|
60
|
+
if (data?.e instanceof Error) {
|
|
61
|
+
// log the stack
|
|
62
|
+
console.error(data?.e)
|
|
63
|
+
if (data?.e?.message) message += ' | ' + ( data?.e?.message || '')
|
|
64
|
+
}
|
|
65
|
+
// TODO: for better readability, we shold use padding for fileName, functionname, etc
|
|
66
|
+
return `${data?.timestamp} ${data?.level} ${fileName}${functionName}${functionIdentifier}${subName}${prefixData}${message}`
|
|
33
67
|
}
|
|
34
|
-
return `${timestamp} ${level} ${fileName}${functionName}${subName}${prefixData}${message}`
|
|
35
68
|
})
|
|
36
69
|
|
|
70
|
+
|
|
37
71
|
const logTransports = []
|
|
38
72
|
if (!_.size(transporters)) {
|
|
39
73
|
// default behaviour
|
|
40
|
-
|
|
74
|
+
// human-readable console output
|
|
75
|
+
logTransports.push(new transports.Console({
|
|
76
|
+
format: format.combine(
|
|
77
|
+
format.timestamp({
|
|
78
|
+
format: timestampFormat
|
|
79
|
+
}),
|
|
80
|
+
format.errors({ stack: true }),
|
|
81
|
+
format(info => {
|
|
82
|
+
info.level = _.padEnd(info.level.toUpperCase(), 8)
|
|
83
|
+
return info
|
|
84
|
+
})(),
|
|
85
|
+
format.colorize(),
|
|
86
|
+
splatFormatter(),
|
|
87
|
+
myFormat
|
|
88
|
+
),
|
|
89
|
+
}))
|
|
90
|
+
|
|
91
|
+
// structured logging for analysis and querying within the CloudWatch service.
|
|
92
|
+
if (applicationLogs?.enabled) {
|
|
93
|
+
let filename = applicationLogs?.filename || 'application.log'
|
|
94
|
+
if (filename.endsWith('.log')) {
|
|
95
|
+
// If the filename ends with .log, insert -%DATE% before the extension
|
|
96
|
+
filename = filename.replace(/\.log$/, '-%DATE%.log');
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// If the filename doesn't end with .log, append -%DATE%.log
|
|
100
|
+
filename = `${filename}-%DATE%.log`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const transport = new transports.DailyRotateFile({
|
|
104
|
+
filename,
|
|
105
|
+
datePattern: 'YYYY-MM-DD',
|
|
106
|
+
zippedArchive: true, // Enable gzip compression for rotated files
|
|
107
|
+
maxSize: applicationLogs?.maxSize || '100m', // Rotate the file when it reaches 20MB
|
|
108
|
+
maxFiles: applicationLogs?.maxFiles || '7d', // Keep rotated files for 7 days
|
|
109
|
+
dirname: applicationLogs?.firname || './logs', // Specify the directory for log files
|
|
110
|
+
format: format.combine(
|
|
111
|
+
format.timestamp(),
|
|
112
|
+
splatFormatter(),
|
|
113
|
+
format.json()
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
transport.on('error', error => {
|
|
118
|
+
acLogger.error('ac-logger | ApplicationLogs | TransportError | %j', error?.message)
|
|
119
|
+
})
|
|
120
|
+
logTransports.push(transport)
|
|
121
|
+
}
|
|
122
|
+
|
|
41
123
|
if (process.env.NODE_ENV === 'test') {
|
|
42
124
|
logTransports.push(new transports.File({
|
|
43
125
|
filename: 'test.log',
|
|
@@ -55,19 +137,6 @@ module.exports = ({ prefixFields = [], timestampFormat = 'YYYY-MM-DD HH:mm:ss',
|
|
|
55
137
|
|
|
56
138
|
const logConfig = {
|
|
57
139
|
level,
|
|
58
|
-
format: format.combine(
|
|
59
|
-
format.timestamp({
|
|
60
|
-
format: timestampFormat
|
|
61
|
-
}),
|
|
62
|
-
format.errors({ stack: true }),
|
|
63
|
-
format(info => {
|
|
64
|
-
info.level = _.padEnd(info.level.toUpperCase(), 8)
|
|
65
|
-
return info
|
|
66
|
-
})(),
|
|
67
|
-
format.colorize(),
|
|
68
|
-
format.splat(),
|
|
69
|
-
myFormat
|
|
70
|
-
),
|
|
71
140
|
transports: logTransports
|
|
72
141
|
}
|
|
73
142
|
if (_.get(customLevels, 'levels')) _.set(logConfig, 'levels', _.get(customLevels, 'levels'))
|
|
@@ -76,6 +145,7 @@ module.exports = ({ prefixFields = [], timestampFormat = 'YYYY-MM-DD HH:mm:ss',
|
|
|
76
145
|
if (_.get(customLevels, 'colors')) addColors(_.get(customLevels, 'colors'))
|
|
77
146
|
|
|
78
147
|
|
|
148
|
+
|
|
79
149
|
const headline = (params) => {
|
|
80
150
|
acLogger.info('')
|
|
81
151
|
const fill = _.get(params, 'headFill', '*')
|
package/package.json
CHANGED
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
"author": "Mark Poepping (https://www.admiralcloud.com)",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": "admiralcloud/ac-logger",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.3.1",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"lodash": "^4.17.21",
|
|
9
9
|
"moment": "^2.30.1",
|
|
10
|
-
"winston": "^3.
|
|
11
|
-
"winston-daily-rotate-file": "^
|
|
10
|
+
"winston": "^3.12.0",
|
|
11
|
+
"winston-daily-rotate-file": "^5.0.0"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"ac-semantic-release": "^0.4.2",
|
|
15
|
-
"chai": "^4.
|
|
16
|
-
"eslint": "^8.
|
|
15
|
+
"chai": "^4.4.1",
|
|
16
|
+
"eslint": "^8.57.0",
|
|
17
17
|
"intercept-stdout": "^0.1.2",
|
|
18
|
-
"mocha": "^10.
|
|
18
|
+
"mocha": "^10.3.0",
|
|
19
19
|
"mocha-jenkins-reporter": "^0.4.8"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|