w-syslog 1.0.8 → 1.0.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/README.md +1 -1
- package/dist/formatter.umd.js +2 -2
- package/dist/formatter.umd.js.map +1 -1
- package/dist/w-syslog.umd.js +2 -2
- package/dist/w-syslog.umd.js.map +1 -1
- package/docs/WSyslog.html +4 -4
- package/docs/WSyslog.mjs.html +52 -6
- package/docs/index.html +1 -1
- package/g.mjs +1 -1
- package/package.json +3 -3
- package/src/WSyslog.mjs +51 -5
- package/src/formatter.mjs +9 -0
- package/test/basic.test.mjs +55 -57
- package/test/cleanLogs.test.mjs +17 -19
- package/test/clear.test.mjs +138 -0
- package/test/fixtures/clear-exit.mjs +25 -0
- package/test/fixtures/log-after-clear.mjs +18 -0
- package/test/timeZone.test.mjs +5 -10
package/test/cleanLogs.test.mjs
CHANGED
|
@@ -34,20 +34,17 @@ describe('WSyslog cleanLogs', function() {
|
|
|
34
34
|
w.fsDeleteFolder(fdLog)
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
afterEach(
|
|
38
|
-
|
|
39
|
-
await w.delay(100)
|
|
40
|
-
//完全移除測試資料夾, 不留空殼
|
|
37
|
+
afterEach(function() {
|
|
38
|
+
//clear已保證worker收攤與fd釋放, 直接移除測試資料夾, 不留空殼
|
|
41
39
|
w.fsDeleteFolder(fdLog)
|
|
42
40
|
})
|
|
43
41
|
|
|
44
|
-
after(
|
|
42
|
+
after(function() {
|
|
45
43
|
//最終把'資料夾不存在時不崩'測試可能由pino formatter建立的資料夾也清掉
|
|
46
|
-
await w.delay(100)
|
|
47
44
|
w.fsDeleteFolder(fdNoExist)
|
|
48
45
|
})
|
|
49
46
|
|
|
50
|
-
it('day模式: 保留最新numKeep個檔, 最舊的被刪', function() {
|
|
47
|
+
it('day模式: 保留最新numKeep個檔, 最舊的被刪', async function() {
|
|
51
48
|
seedFiles(fdLog, [
|
|
52
49
|
'2020-01-01.log',
|
|
53
50
|
'2020-01-02.log',
|
|
@@ -65,10 +62,10 @@ describe('WSyslog cleanLogs', function() {
|
|
|
65
62
|
'2020-01-05.log',
|
|
66
63
|
])
|
|
67
64
|
|
|
68
|
-
log.clear()
|
|
65
|
+
await log.clear()
|
|
69
66
|
})
|
|
70
67
|
|
|
71
|
-
it('當檔數小於等於numKeep時不刪任何檔', function() {
|
|
68
|
+
it('當檔數小於等於numKeep時不刪任何檔', async function() {
|
|
72
69
|
seedFiles(fdLog, [
|
|
73
70
|
'2020-01-01.log',
|
|
74
71
|
'2020-01-02.log',
|
|
@@ -83,10 +80,10 @@ describe('WSyslog cleanLogs', function() {
|
|
|
83
80
|
'2020-01-02.log',
|
|
84
81
|
])
|
|
85
82
|
|
|
86
|
-
log.clear()
|
|
83
|
+
await log.clear()
|
|
87
84
|
})
|
|
88
85
|
|
|
89
|
-
it('hr模式: 只匹配hr命名, 不誤刪day命名', function() {
|
|
86
|
+
it('hr模式: 只匹配hr命名, 不誤刪day命名', async function() {
|
|
90
87
|
seedFiles(fdLog, [
|
|
91
88
|
//day命名(不該被hr模式cleanLogs動到)
|
|
92
89
|
'2020-01-01.log',
|
|
@@ -115,10 +112,10 @@ describe('WSyslog cleanLogs', function() {
|
|
|
115
112
|
'2020-01-01T05.log',
|
|
116
113
|
])
|
|
117
114
|
|
|
118
|
-
log.clear()
|
|
115
|
+
await log.clear()
|
|
119
116
|
})
|
|
120
117
|
|
|
121
|
-
it('刪除時觸發delete事件, payload含fp與fn', function() {
|
|
118
|
+
it('刪除時觸發delete事件, payload含fp與fn', async function() {
|
|
122
119
|
//先啟動空資料夾的logger, 此時init的cleanLogs不會刪任何檔
|
|
123
120
|
let log = WSyslog({ fdLog, numKeep: 5, interval: 'hr' })
|
|
124
121
|
|
|
@@ -159,22 +156,23 @@ describe('WSyslog cleanLogs', function() {
|
|
|
159
156
|
assert.strict.equal(d.fp, path.join(fdLog, d.fn))
|
|
160
157
|
}
|
|
161
158
|
|
|
162
|
-
log.clear()
|
|
159
|
+
await log.clear()
|
|
163
160
|
})
|
|
164
161
|
|
|
165
|
-
it('資料夾不存在時不崩', function() {
|
|
162
|
+
it('資料夾不存在時不崩', async function() {
|
|
166
163
|
//確保起始狀態為不存在
|
|
167
164
|
w.fsDeleteFolder(fdNoExist)
|
|
168
165
|
|
|
166
|
+
let log = null
|
|
169
167
|
assert.doesNotThrow(() => {
|
|
170
|
-
|
|
168
|
+
log = WSyslog({ fdLog: fdNoExist, numKeep: 5, interval: 'day' })
|
|
171
169
|
log.cleanLogs()
|
|
172
|
-
log.clear()
|
|
173
170
|
})
|
|
171
|
+
await log.clear()
|
|
174
172
|
//pino formatter可能async建立資料夾, 由describe區塊的after hook統一清理
|
|
175
173
|
})
|
|
176
174
|
|
|
177
|
-
it('忽略不符regex命名的檔', function() {
|
|
175
|
+
it('忽略不符regex命名的檔', async function() {
|
|
178
176
|
seedFiles(fdLog, [
|
|
179
177
|
'2020-01-01.log',
|
|
180
178
|
'2020-01-02.log',
|
|
@@ -209,7 +207,7 @@ describe('WSyslog cleanLogs', function() {
|
|
|
209
207
|
.sort()
|
|
210
208
|
assert.strict.deepEqual(matching, ['2020-01-03.log'])
|
|
211
209
|
|
|
212
|
-
log.clear()
|
|
210
|
+
await log.clear()
|
|
213
211
|
})
|
|
214
212
|
|
|
215
213
|
})
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import assert from 'assert'
|
|
4
|
+
import { execFile } from 'child_process'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
import _ from 'lodash-es'
|
|
7
|
+
import w from 'wsemi'
|
|
8
|
+
import WSyslog from '../src/WSyslog.mjs'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
//獨立測試資料夾, 避免與其他測試檔在parallel模式下競用
|
|
12
|
+
let fdLog = './_logs_clear_test'
|
|
13
|
+
let fdLogChild = './_logs_clear_child' //子程序fixture用, 實際為`${fdLogChild}_${i}`
|
|
14
|
+
let fdLogStress = './_logs_clear_stress'
|
|
15
|
+
let fdLogAfter = './_logs_clear_after'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
//fixture路徑錨定於本測試檔位置, 不受cwd影響
|
|
19
|
+
let fpFxExit = fileURLToPath(new URL('./fixtures/clear-exit.mjs', import.meta.url))
|
|
20
|
+
let fpFxAfter = fileURLToPath(new URL('./fixtures/log-after-clear.mjs', import.meta.url))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function runNode(args) {
|
|
24
|
+
//以子程序執行fixture, 回傳{code, stderr, ms}
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
let ts = Date.now()
|
|
27
|
+
execFile(process.execPath, args, { cwd: process.cwd() }, (err, stdout, stderr) => {
|
|
28
|
+
let code = err ? err.code : 0
|
|
29
|
+
resolve({ code, stderr, ms: Date.now() - ts })
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
function countLogLines(folder) {
|
|
36
|
+
//統計資料夾內所有log檔的非空行數
|
|
37
|
+
if (!w.fsIsFolder(folder)) {
|
|
38
|
+
return 0
|
|
39
|
+
}
|
|
40
|
+
let n = 0
|
|
41
|
+
for (let fn of fs.readdirSync(folder)) {
|
|
42
|
+
let jj = fs.readFileSync(path.join(folder, fn), 'utf8')
|
|
43
|
+
n += w.sep(jj, '\n').length
|
|
44
|
+
}
|
|
45
|
+
return n
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
function cleanAll() {
|
|
50
|
+
w.fsDeleteFolder(fdLog)
|
|
51
|
+
w.fsDeleteFolder(fdLogAfter)
|
|
52
|
+
for (let i = 0; i < 10; i++) {
|
|
53
|
+
w.fsDeleteFolder(`${fdLogChild}_${i}`)
|
|
54
|
+
w.fsDeleteFolder(`${fdLogStress}_${i}`)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
describe('WSyslog clear', function() {
|
|
60
|
+
|
|
61
|
+
before(function() {
|
|
62
|
+
cleanAll()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
after(async function() {
|
|
66
|
+
await w.delay(100)
|
|
67
|
+
cleanAll()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('clear後檔案內容立即完整, 不需delay等flush', async function() {
|
|
71
|
+
|
|
72
|
+
let log = WSyslog({ fdLog, interval: 'day' })
|
|
73
|
+
log.info({ event: 'runner', msg: 'start' })
|
|
74
|
+
log.warn({ event: 'monitor-memory', msg: 'usage-high', ratio: 85.4 })
|
|
75
|
+
log.error({ event: 'crash', msg: 'db connection', code: 500 })
|
|
76
|
+
|
|
77
|
+
await log.clear()
|
|
78
|
+
|
|
79
|
+
//clear已保證殘餘log全數寫出, 立即讀檔驗證
|
|
80
|
+
let vpfs = w.fsTreeFolder(fdLog)
|
|
81
|
+
let fp = _.get(vpfs, `0.path`, '')
|
|
82
|
+
let jj = fs.readFileSync(fp, 'utf8')
|
|
83
|
+
let ss = w.sep(jj, '\n')
|
|
84
|
+
let r = _.map(ss, (j) => {
|
|
85
|
+
let v = JSON.parse(j)
|
|
86
|
+
return { event: _.get(v, 'event'), msg: _.get(v, 'msg') }
|
|
87
|
+
})
|
|
88
|
+
assert.strict.deepEqual(r, [
|
|
89
|
+
{ event: 'runner', msg: 'start' },
|
|
90
|
+
{ event: 'monitor-memory', msg: 'usage-high' },
|
|
91
|
+
{ event: 'crash', msg: 'db connection' },
|
|
92
|
+
])
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('重複clear回傳同一Promise(冪等)', async function() {
|
|
96
|
+
|
|
97
|
+
let log = WSyslog({ fdLog, interval: 'day' })
|
|
98
|
+
log.info({ event: 'idem', msg: 'x' })
|
|
99
|
+
|
|
100
|
+
let p1 = log.clear()
|
|
101
|
+
let p2 = log.clear()
|
|
102
|
+
assert.strict.equal(p1, p2)
|
|
103
|
+
|
|
104
|
+
await p1
|
|
105
|
+
|
|
106
|
+
//clear之後再呼叫依然回傳同一Promise
|
|
107
|
+
let p3 = log.clear()
|
|
108
|
+
assert.strict.equal(p1, p3)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('子程序寫300筆log後clear, 行程於數秒內自然退出且無漏筆', async function() {
|
|
112
|
+
|
|
113
|
+
let { code, stderr, ms } = await runNode([fpFxExit, fdLogChild, '1', '300'])
|
|
114
|
+
|
|
115
|
+
assert.strict.equal(code, 0, `子程序應exit 0, 實得code=${code}, stderr=${stderr}`)
|
|
116
|
+
assert.ok(ms < 20000, `子程序應於數秒內退出, 實測${ms}ms`)
|
|
117
|
+
assert.strict.equal(countLogLines(`${fdLogChild}_0`), 300)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('壓測: 10實例各寫300筆後逐一clear, 行程自然退出且無漏筆', async function() {
|
|
121
|
+
|
|
122
|
+
let { code, stderr } = await runNode([fpFxExit, fdLogStress, '10', '300'])
|
|
123
|
+
|
|
124
|
+
assert.strict.equal(code, 0, `子程序應exit 0, 實得code=${code}, stderr=${stderr}`)
|
|
125
|
+
for (let i = 0; i < 10; i++) {
|
|
126
|
+
assert.strict.equal(countLogLines(`${fdLogStress}_${i}`), 300, `實例${i}應有300筆log`)
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('clear後再寫log, 非同步丟the worker has exited(子程序exit非0)', async function() {
|
|
131
|
+
|
|
132
|
+
let { code, stderr } = await runNode([fpFxAfter, fdLogAfter])
|
|
133
|
+
|
|
134
|
+
assert.notStrictEqual(code, 0, 'clear後再寫log的子程序應以非0退出')
|
|
135
|
+
assert.ok(stderr.includes('the worker has exited'), `stderr應含'the worker has exited', 實得: ${stderr}`)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import WSyslog from '../../src/WSyslog.mjs'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
//子程序fixture: 建nInst個實例各寫nLogs筆log後逐一clear, 用於驗證行程能自然退出(不依賴exit hook)
|
|
5
|
+
//argv: [fdLogBase, nInst, nLogs], 各實例寫入`${fdLogBase}_${i}`資料夾
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
let fdLogBase = process.argv[2]
|
|
9
|
+
let nInst = parseInt(process.argv[3], 10)
|
|
10
|
+
let nLogs = parseInt(process.argv[4], 10)
|
|
11
|
+
|
|
12
|
+
let logs = []
|
|
13
|
+
for (let i = 0; i < nInst; i++) {
|
|
14
|
+
logs.push(WSyslog({ fdLog: `${fdLogBase}_${i}`, interval: 'day' }))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
for (let log of logs) {
|
|
18
|
+
for (let k = 0; k < nLogs; k++) {
|
|
19
|
+
log.info({ event: 'stress', msg: `m${k}` })
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (let log of logs) {
|
|
24
|
+
await log.clear()
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import WSyslog from '../../src/WSyslog.mjs'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
//子程序fixture: clear後再寫log, 預期非同步丟uncaughtException(the worker has exited)致exit code非0
|
|
5
|
+
//argv: [fdLog]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
let fdLog = process.argv[2]
|
|
9
|
+
|
|
10
|
+
let log = WSyslog({ fdLog, interval: 'day' })
|
|
11
|
+
log.info({ event: 'probe', msg: 'before-clear' })
|
|
12
|
+
|
|
13
|
+
await log.clear()
|
|
14
|
+
|
|
15
|
+
log.info({ event: 'probe', msg: 'after-clear' })
|
|
16
|
+
|
|
17
|
+
//保持存活讓非同步錯誤有時間浮現; 若未丟錯則計時器到期以exit 0結束, 由測試端斷言非0
|
|
18
|
+
setTimeout(() => {}, 3000)
|
package/test/timeZone.test.mjs
CHANGED
|
@@ -28,9 +28,8 @@ describe('WSyslog timeZone', function() {
|
|
|
28
28
|
w.fsDeleteFolder(fdLogTpe)
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
afterEach(
|
|
32
|
-
|
|
33
|
-
await w.delay(200)
|
|
31
|
+
afterEach(function() {
|
|
32
|
+
//clear已保證worker收攤與fd釋放, 直接移除測試資料夾
|
|
34
33
|
w.fsDeleteFolder(fdLogUtc)
|
|
35
34
|
w.fsDeleteFolder(fdLogTpe)
|
|
36
35
|
})
|
|
@@ -40,7 +39,7 @@ describe('WSyslog timeZone', function() {
|
|
|
40
39
|
let log = WSyslog({ fdLog: fdLogUtc, interval: 'hr', timeZone: 'UTC' })
|
|
41
40
|
log.info({ event: 'tz-utc', msg: 'check' })
|
|
42
41
|
|
|
43
|
-
await
|
|
42
|
+
await log.clear() //顯式關閉, 保證殘餘log已全數寫出
|
|
44
43
|
|
|
45
44
|
let actualKey = readHrKey(fdLogUtc)
|
|
46
45
|
assert.ok(actualKey !== null, `應有1個hr log檔, 取得actualKey=${actualKey}`)
|
|
@@ -54,8 +53,6 @@ describe('WSyslog timeZone', function() {
|
|
|
54
53
|
actualKey === utcKey || actualKey === utcKeyPrev,
|
|
55
54
|
`actualKey="${actualKey}" 應為UTC當前小時"${utcKey}"或前一小時"${utcKeyPrev}"`
|
|
56
55
|
)
|
|
57
|
-
|
|
58
|
-
log.clear()
|
|
59
56
|
})
|
|
60
57
|
|
|
61
58
|
it('timeZone="UTC" 與 "Asia/Taipei" (UTC+8) 寫log應產生不同小時檔名', async function() {
|
|
@@ -67,7 +64,8 @@ describe('WSyslog timeZone', function() {
|
|
|
67
64
|
logUtc.info({ msg: 'x' })
|
|
68
65
|
logTpe.info({ msg: 'x' })
|
|
69
66
|
|
|
70
|
-
await
|
|
67
|
+
await logUtc.clear()
|
|
68
|
+
await logTpe.clear()
|
|
71
69
|
|
|
72
70
|
let keyUtc = readHrKey(fdLogUtc)
|
|
73
71
|
let keyTpe = readHrKey(fdLogTpe)
|
|
@@ -81,9 +79,6 @@ describe('WSyslog timeZone', function() {
|
|
|
81
79
|
keyTpe,
|
|
82
80
|
`UTC與Asia/Taipei檔名應不同: keyUtc="${keyUtc}", keyTpe="${keyTpe}"`
|
|
83
81
|
)
|
|
84
|
-
|
|
85
|
-
logUtc.clear()
|
|
86
|
-
logTpe.clear()
|
|
87
82
|
})
|
|
88
83
|
|
|
89
84
|
})
|