w-syslog 1.0.8 → 1.0.9

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.
@@ -34,20 +34,17 @@ describe('WSyslog cleanLogs', function() {
34
34
  w.fsDeleteFolder(fdLog)
35
35
  })
36
36
 
37
- afterEach(async function() {
38
- //稍候pino worker flush, 避免Windows上EBUSY
39
- await w.delay(100)
40
- //完全移除測試資料夾, 不留空殼
37
+ afterEach(function() {
38
+ //clear已保證worker收攤與fd釋放, 直接移除測試資料夾, 不留空殼
41
39
  w.fsDeleteFolder(fdLog)
42
40
  })
43
41
 
44
- after(async function() {
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
- let log = WSyslog({ fdLog: fdNoExist, numKeep: 5, interval: 'day' })
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)
@@ -28,9 +28,8 @@ describe('WSyslog timeZone', function() {
28
28
  w.fsDeleteFolder(fdLogTpe)
29
29
  })
30
30
 
31
- afterEach(async function() {
32
- //稍候pino worker flush, 避免Windows上EBUSY
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 w.delay(2000) //等pino flush
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 w.delay(2000)
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
  })