w-syslog 1.0.6 → 1.0.8
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/.github/workflows/ci-test.yml +3 -3
- package/README.md +2 -0
- 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 +81 -3
- package/docs/WSyslog.mjs.html +106 -5
- package/docs/index.html +1 -1
- package/g.mjs +8 -3
- package/package.json +5 -7
- package/script.txt +0 -1
- package/src/WSyslog.mjs +104 -3
- package/src/formatter.mjs +15 -6
- package/test/{WSyslog.test.mjs → basic.test.mjs} +7 -0
- package/test/cleanLogs.test.mjs +215 -0
- package/test/timeZone.test.mjs +89 -0
- package/_logs/2025-07-07T21.log +0 -3
package/docs/WSyslog.html
CHANGED
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
|
|
81
81
|
<dt class="tag-source">Source:</dt>
|
|
82
82
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
83
|
-
<a href="WSyslog.mjs.html">WSyslog.mjs</a>, <a href="WSyslog.mjs.html#
|
|
83
|
+
<a href="WSyslog.mjs.html">WSyslog.mjs</a>, <a href="WSyslog.mjs.html#line55">line 55</a>
|
|
84
84
|
</li></ul></dd>
|
|
85
85
|
|
|
86
86
|
|
|
@@ -301,6 +301,84 @@
|
|
|
301
301
|
</tr>
|
|
302
302
|
|
|
303
303
|
|
|
304
|
+
|
|
305
|
+
<tr>
|
|
306
|
+
|
|
307
|
+
<td class="name"><code>timeZone</code></td>
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
<td class="type">
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
<span class="param-type">String</span>
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
</td>
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
<td class="attributes">
|
|
322
|
+
|
|
323
|
+
<optional><br>
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
</td>
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
<td class="default">
|
|
334
|
+
|
|
335
|
+
<code>null</code>
|
|
336
|
+
|
|
337
|
+
</td>
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
<td class="description last"><p>輸入時區字串(IANA格式如'Asia/Taipei'、'UTC'),用於決定切檔邊界與檔名,預設null代表使用系統時區</p></td>
|
|
341
|
+
</tr>
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
<tr>
|
|
346
|
+
|
|
347
|
+
<td class="name"><code>numKeep</code></td>
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
<td class="type">
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
<span class="param-type">Number</span>
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
</td>
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
<td class="attributes">
|
|
362
|
+
|
|
363
|
+
<optional><br>
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
</td>
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
<td class="default">
|
|
374
|
+
|
|
375
|
+
</td>
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
<td class="description last"><p>輸入保留log檔案數量整數,超出最舊檔將被自動刪除,預設於day模式為365、hr模式為365*24(約等同保留一年)</p></td>
|
|
379
|
+
</tr>
|
|
380
|
+
|
|
381
|
+
|
|
304
382
|
</tbody>
|
|
305
383
|
</table>
|
|
306
384
|
|
|
@@ -330,7 +408,7 @@
|
|
|
330
408
|
|
|
331
409
|
|
|
332
410
|
<div class="param-desc">
|
|
333
|
-
<p>回傳log物件,提供
|
|
411
|
+
<p>回傳log物件,提供fatal、error、warn、info、debug、trace紀錄函數,cleanLogs手動清理函數,及clear清理計時器函數</p>
|
|
334
412
|
</div>
|
|
335
413
|
|
|
336
414
|
|
|
@@ -389,7 +467,7 @@
|
|
|
389
467
|
<br class="clear">
|
|
390
468
|
|
|
391
469
|
<footer>
|
|
392
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.
|
|
470
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri May 08 2026 16:56:20 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
393
471
|
</footer>
|
|
394
472
|
|
|
395
473
|
<script>prettyPrint();</script>
|
package/docs/WSyslog.mjs.html
CHANGED
|
@@ -45,10 +45,15 @@
|
|
|
45
45
|
|
|
46
46
|
<section>
|
|
47
47
|
<article>
|
|
48
|
-
<pre class="prettyprint source linenums"><code>import
|
|
48
|
+
<pre class="prettyprint source linenums"><code>import fs from 'fs'
|
|
49
|
+
import path from 'path'
|
|
49
50
|
import { fileURLToPath } from 'url'
|
|
50
51
|
import get from 'lodash-es/get.js'
|
|
52
|
+
import evem from 'wsemi/src/evem.mjs'
|
|
51
53
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
54
|
+
import ispint from 'wsemi/src/ispint.mjs'
|
|
55
|
+
import cint from 'wsemi/src/cint.mjs'
|
|
56
|
+
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
|
|
52
57
|
import pino from 'pino'
|
|
53
58
|
|
|
54
59
|
|
|
@@ -64,7 +69,9 @@ let __dirname = path.dirname(__filename)
|
|
|
64
69
|
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
65
70
|
* @param {String} [opt.fdLog='./_logs'] 輸入儲存log資料夾字串,預設'./_logs'
|
|
66
71
|
* @param {String} [opt.interval='day'] 輸入儲存log時分檔模式字串,可選'day'、'hr',分別代表每日或每時分檔,預設'day'
|
|
67
|
-
* @
|
|
72
|
+
* @param {String} [opt.timeZone=null] 輸入時區字串(IANA格式如'Asia/Taipei'、'UTC'),用於決定切檔邊界與檔名,預設null代表使用系統時區
|
|
73
|
+
* @param {Number} [opt.numKeep] 輸入保留log檔案數量整數,超出最舊檔將被自動刪除,預設於day模式為365、hr模式為365*24(約等同保留一年)
|
|
74
|
+
* @returns {Object} 回傳log物件,提供fatal、error、warn、info、debug、trace紀錄函數,cleanLogs手動清理函數,及clear清理計時器函數
|
|
68
75
|
* @example
|
|
69
76
|
* import fs from 'fs'
|
|
70
77
|
* import _ from 'lodash-es'
|
|
@@ -94,6 +101,12 @@ let __dirname = path.dirname(__filename)
|
|
|
94
101
|
*/
|
|
95
102
|
function WSyslog(opt = {}) {
|
|
96
103
|
|
|
104
|
+
//ev
|
|
105
|
+
let ev = evem()
|
|
106
|
+
|
|
107
|
+
//timeZone, 'Asia/Taipei'
|
|
108
|
+
let timeZone = get(opt, 'timeZone', null)
|
|
109
|
+
|
|
97
110
|
//fdLog
|
|
98
111
|
let fdLog = get(opt, 'fdLog')
|
|
99
112
|
if (!isestr(fdLog)) {
|
|
@@ -106,6 +119,18 @@ function WSyslog(opt = {}) {
|
|
|
106
119
|
interval = 'day'
|
|
107
120
|
}
|
|
108
121
|
|
|
122
|
+
//numKeep
|
|
123
|
+
let numKeep = get(opt, 'numKeep')
|
|
124
|
+
if (!ispint(numKeep)) {
|
|
125
|
+
if (interval === 'day') {
|
|
126
|
+
numKeep = 365
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
numKeep = 365 * 24
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
numKeep = cint(numKeep)
|
|
133
|
+
|
|
109
134
|
// 建立 logger,使用自訂 transport
|
|
110
135
|
let transport = pino.transport({
|
|
111
136
|
targets: [
|
|
@@ -123,7 +148,8 @@ function WSyslog(opt = {}) {
|
|
|
123
148
|
|
|
124
149
|
options: {
|
|
125
150
|
interval,
|
|
126
|
-
|
|
151
|
+
fdLog,
|
|
152
|
+
timeZone,
|
|
127
153
|
},
|
|
128
154
|
|
|
129
155
|
},
|
|
@@ -134,7 +160,82 @@ function WSyslog(opt = {}) {
|
|
|
134
160
|
let log = pino(transport)
|
|
135
161
|
// console.log('log', log)
|
|
136
162
|
|
|
137
|
-
|
|
163
|
+
//cleanLogs, 依numKeep保留最新N個檔, 超出的最舊檔刪除
|
|
164
|
+
let cleanLogs = () => {
|
|
165
|
+
try {
|
|
166
|
+
if (!fsIsFolder(fdLog)) {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
//pattern, day模式為'YYYY-MM-DD.log', hr模式為'YYYY-MM-DDTHH.log'
|
|
170
|
+
let pattern = (interval === 'hr')
|
|
171
|
+
? /^\d{4}-\d{2}-\d{2}T\d{2}\.log$/
|
|
172
|
+
: /^\d{4}-\d{2}-\d{2}\.log$/
|
|
173
|
+
let files = fs.readdirSync(fdLog).filter((fn) => pattern.test(fn))
|
|
174
|
+
//ISO日期格式字典序等同時間序, 排序後最舊在前
|
|
175
|
+
files.sort()
|
|
176
|
+
let nDel = files.length - numKeep
|
|
177
|
+
if (nDel <= 0) {
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
let toDelete = files.slice(0, nDel)
|
|
181
|
+
for (let fn of toDelete) {
|
|
182
|
+
let fp = path.join(fdLog, fn)
|
|
183
|
+
try {
|
|
184
|
+
fs.unlinkSync(fp)
|
|
185
|
+
ev.emit('delete', { fp, fn })
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
//容忍其他process同時刪除或檔案被佔用之情況
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
console.error('WSyslog cleanLogs error:', err)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//啟動時立刻清理一次, 確保短命腳本也能執行清理
|
|
198
|
+
cleanLogs()
|
|
199
|
+
|
|
200
|
+
//之後每小時清理一次, 足以跟上day/hr切檔節奏
|
|
201
|
+
let t = setInterval(cleanLogs, 60 * 60 * 1000)
|
|
202
|
+
//unref不阻塞process結束
|
|
203
|
+
t.unref()
|
|
204
|
+
|
|
205
|
+
//clear
|
|
206
|
+
let clear = () => {
|
|
207
|
+
clearInterval(t)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
//wrap, 包裝pino各等級method, 於寫log前先觸發'log'事件, 使外部可監聽進行console輸出或轉送其他記錄器
|
|
211
|
+
// fatal: 60
|
|
212
|
+
// error: 50
|
|
213
|
+
// warn: 40
|
|
214
|
+
// info: 30
|
|
215
|
+
// debug: 20
|
|
216
|
+
// trace: 10
|
|
217
|
+
let wrap = (level) => {
|
|
218
|
+
let fn = log[level].bind(log)
|
|
219
|
+
return (...args) => {
|
|
220
|
+
try {
|
|
221
|
+
ev.emit(level, { args })
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
//容忍listener內部錯誤, 避免影響pino寫log
|
|
225
|
+
}
|
|
226
|
+
fn(...args)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
ev.fatal = wrap('fatal')
|
|
230
|
+
ev.error = wrap('error')
|
|
231
|
+
ev.warn = wrap('warn')
|
|
232
|
+
ev.info = wrap('info')
|
|
233
|
+
ev.debug = wrap('debug')
|
|
234
|
+
ev.trace = wrap('trace')
|
|
235
|
+
ev.cleanLogs = cleanLogs
|
|
236
|
+
ev.clear = clear
|
|
237
|
+
|
|
238
|
+
return ev
|
|
138
239
|
}
|
|
139
240
|
|
|
140
241
|
|
|
@@ -153,7 +254,7 @@ export default WSyslog
|
|
|
153
254
|
<br class="clear">
|
|
154
255
|
|
|
155
256
|
<footer>
|
|
156
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.
|
|
257
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri May 08 2026 16:56:20 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
157
258
|
</footer>
|
|
158
259
|
|
|
159
260
|
<script>prettyPrint();</script>
|
package/docs/index.html
CHANGED
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
<br class="clear">
|
|
72
72
|
|
|
73
73
|
<footer>
|
|
74
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.
|
|
74
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri May 08 2026 16:56:20 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
75
75
|
</footer>
|
|
76
76
|
|
|
77
77
|
<script>prettyPrint();</script>
|
package/g.mjs
CHANGED
|
@@ -4,16 +4,20 @@ import w from 'wsemi'
|
|
|
4
4
|
import WSyslog from './src/WSyslog.mjs'
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
let fdLog = './_logs' //'./_pjs/_logs'
|
|
8
|
+
w.fsCleanFolder(fdLog)
|
|
8
9
|
|
|
9
|
-
let log = WSyslog({
|
|
10
|
+
let log = WSyslog({
|
|
11
|
+
fdLog,
|
|
12
|
+
interval: 'hr',
|
|
13
|
+
})
|
|
10
14
|
log.info({ event: 'runner', msg: 'start' })
|
|
11
15
|
log.warn({ event: 'monitor-memory', msg: 'usage-high', ratio: 85.4 })
|
|
12
16
|
log.error({ event: 'crash', msg: 'db connection', code: 500 })
|
|
13
17
|
|
|
14
18
|
await w.delay(2000) //等待2秒讓pino能flush數據
|
|
15
19
|
|
|
16
|
-
let vpfs = w.fsTreeFolder(
|
|
20
|
+
let vpfs = w.fsTreeFolder(fdLog)
|
|
17
21
|
// console.log('vpfs', vpfs)
|
|
18
22
|
|
|
19
23
|
let fp = _.get(vpfs, `0.path`, '')
|
|
@@ -24,4 +28,5 @@ console.log(jj)
|
|
|
24
28
|
// {"level":40,"time":1751780174415,"pid":24144,"hostname":"DESKTOP-6R7USAO","event":"monitor-memory","msg":"usage-high"}
|
|
25
29
|
// {"level":50,"time":1751780174415,"pid":24144,"hostname":"DESKTOP-6R7USAO","event":"crash","msg":"db connection","code":500}
|
|
26
30
|
|
|
31
|
+
|
|
27
32
|
//node g.mjs
|
package/package.json
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w-syslog",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"main": "dist/w-syslog.umd.js",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"lodash-es": "^4.
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"pino": "^9.7.0",
|
|
10
|
-
"wsemi": "^1.8.11"
|
|
6
|
+
"lodash-es": "^4.18.1",
|
|
7
|
+
"pino": "^10.3.1",
|
|
8
|
+
"wsemi": "^1.8.51"
|
|
11
9
|
},
|
|
12
10
|
"devDependencies": {
|
|
13
|
-
"w-package-tools": "^1.1.
|
|
11
|
+
"w-package-tools": "^1.1.5"
|
|
14
12
|
},
|
|
15
13
|
"scripts": {
|
|
16
14
|
"test": "mocha --parallel --timeout 60000",
|
package/script.txt
CHANGED
package/src/WSyslog.mjs
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
1
2
|
import path from 'path'
|
|
2
3
|
import { fileURLToPath } from 'url'
|
|
3
4
|
import get from 'lodash-es/get.js'
|
|
5
|
+
import evem from 'wsemi/src/evem.mjs'
|
|
4
6
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
7
|
+
import ispint from 'wsemi/src/ispint.mjs'
|
|
8
|
+
import cint from 'wsemi/src/cint.mjs'
|
|
9
|
+
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
|
|
5
10
|
import pino from 'pino'
|
|
6
11
|
|
|
7
12
|
|
|
@@ -17,7 +22,9 @@ let __dirname = path.dirname(__filename)
|
|
|
17
22
|
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
18
23
|
* @param {String} [opt.fdLog='./_logs'] 輸入儲存log資料夾字串,預設'./_logs'
|
|
19
24
|
* @param {String} [opt.interval='day'] 輸入儲存log時分檔模式字串,可選'day'、'hr',分別代表每日或每時分檔,預設'day'
|
|
20
|
-
* @
|
|
25
|
+
* @param {String} [opt.timeZone=null] 輸入時區字串(IANA格式如'Asia/Taipei'、'UTC'),用於決定切檔邊界與檔名,預設null代表使用系統時區
|
|
26
|
+
* @param {Number} [opt.numKeep] 輸入保留log檔案數量整數,超出最舊檔將被自動刪除,預設於day模式為365、hr模式為365*24(約等同保留一年)
|
|
27
|
+
* @returns {Object} 回傳log物件,提供fatal、error、warn、info、debug、trace紀錄函數,cleanLogs手動清理函數,及clear清理計時器函數
|
|
21
28
|
* @example
|
|
22
29
|
* import fs from 'fs'
|
|
23
30
|
* import _ from 'lodash-es'
|
|
@@ -47,6 +54,12 @@ let __dirname = path.dirname(__filename)
|
|
|
47
54
|
*/
|
|
48
55
|
function WSyslog(opt = {}) {
|
|
49
56
|
|
|
57
|
+
//ev
|
|
58
|
+
let ev = evem()
|
|
59
|
+
|
|
60
|
+
//timeZone, 'Asia/Taipei'
|
|
61
|
+
let timeZone = get(opt, 'timeZone', null)
|
|
62
|
+
|
|
50
63
|
//fdLog
|
|
51
64
|
let fdLog = get(opt, 'fdLog')
|
|
52
65
|
if (!isestr(fdLog)) {
|
|
@@ -59,6 +72,18 @@ function WSyslog(opt = {}) {
|
|
|
59
72
|
interval = 'day'
|
|
60
73
|
}
|
|
61
74
|
|
|
75
|
+
//numKeep
|
|
76
|
+
let numKeep = get(opt, 'numKeep')
|
|
77
|
+
if (!ispint(numKeep)) {
|
|
78
|
+
if (interval === 'day') {
|
|
79
|
+
numKeep = 365
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
numKeep = 365 * 24
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
numKeep = cint(numKeep)
|
|
86
|
+
|
|
62
87
|
// 建立 logger,使用自訂 transport
|
|
63
88
|
let transport = pino.transport({
|
|
64
89
|
targets: [
|
|
@@ -76,7 +101,8 @@ function WSyslog(opt = {}) {
|
|
|
76
101
|
|
|
77
102
|
options: {
|
|
78
103
|
interval,
|
|
79
|
-
|
|
104
|
+
fdLog,
|
|
105
|
+
timeZone,
|
|
80
106
|
},
|
|
81
107
|
|
|
82
108
|
},
|
|
@@ -87,7 +113,82 @@ function WSyslog(opt = {}) {
|
|
|
87
113
|
let log = pino(transport)
|
|
88
114
|
// console.log('log', log)
|
|
89
115
|
|
|
90
|
-
|
|
116
|
+
//cleanLogs, 依numKeep保留最新N個檔, 超出的最舊檔刪除
|
|
117
|
+
let cleanLogs = () => {
|
|
118
|
+
try {
|
|
119
|
+
if (!fsIsFolder(fdLog)) {
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
//pattern, day模式為'YYYY-MM-DD.log', hr模式為'YYYY-MM-DDTHH.log'
|
|
123
|
+
let pattern = (interval === 'hr')
|
|
124
|
+
? /^\d{4}-\d{2}-\d{2}T\d{2}\.log$/
|
|
125
|
+
: /^\d{4}-\d{2}-\d{2}\.log$/
|
|
126
|
+
let files = fs.readdirSync(fdLog).filter((fn) => pattern.test(fn))
|
|
127
|
+
//ISO日期格式字典序等同時間序, 排序後最舊在前
|
|
128
|
+
files.sort()
|
|
129
|
+
let nDel = files.length - numKeep
|
|
130
|
+
if (nDel <= 0) {
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
let toDelete = files.slice(0, nDel)
|
|
134
|
+
for (let fn of toDelete) {
|
|
135
|
+
let fp = path.join(fdLog, fn)
|
|
136
|
+
try {
|
|
137
|
+
fs.unlinkSync(fp)
|
|
138
|
+
ev.emit('delete', { fp, fn })
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
//容忍其他process同時刪除或檔案被佔用之情況
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
console.error('WSyslog cleanLogs error:', err)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
//啟動時立刻清理一次, 確保短命腳本也能執行清理
|
|
151
|
+
cleanLogs()
|
|
152
|
+
|
|
153
|
+
//之後每小時清理一次, 足以跟上day/hr切檔節奏
|
|
154
|
+
let t = setInterval(cleanLogs, 60 * 60 * 1000)
|
|
155
|
+
//unref不阻塞process結束
|
|
156
|
+
t.unref()
|
|
157
|
+
|
|
158
|
+
//clear
|
|
159
|
+
let clear = () => {
|
|
160
|
+
clearInterval(t)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//wrap, 包裝pino各等級method, 於寫log前先觸發'log'事件, 使外部可監聽進行console輸出或轉送其他記錄器
|
|
164
|
+
// fatal: 60
|
|
165
|
+
// error: 50
|
|
166
|
+
// warn: 40
|
|
167
|
+
// info: 30
|
|
168
|
+
// debug: 20
|
|
169
|
+
// trace: 10
|
|
170
|
+
let wrap = (level) => {
|
|
171
|
+
let fn = log[level].bind(log)
|
|
172
|
+
return (...args) => {
|
|
173
|
+
try {
|
|
174
|
+
ev.emit(level, { args })
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
//容忍listener內部錯誤, 避免影響pino寫log
|
|
178
|
+
}
|
|
179
|
+
fn(...args)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
ev.fatal = wrap('fatal')
|
|
183
|
+
ev.error = wrap('error')
|
|
184
|
+
ev.warn = wrap('warn')
|
|
185
|
+
ev.info = wrap('info')
|
|
186
|
+
ev.debug = wrap('debug')
|
|
187
|
+
ev.trace = wrap('trace')
|
|
188
|
+
ev.cleanLogs = cleanLogs
|
|
189
|
+
ev.clear = clear
|
|
190
|
+
|
|
191
|
+
return ev
|
|
91
192
|
}
|
|
92
193
|
|
|
93
194
|
|
package/src/formatter.mjs
CHANGED
|
@@ -2,9 +2,16 @@ import fs from 'fs'
|
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { Writable } from 'stream'
|
|
4
4
|
import ot from 'dayjs'
|
|
5
|
+
import utc from 'dayjs/plugin/utc.js'
|
|
6
|
+
import timezone from 'dayjs/plugin/timezone.js'
|
|
5
7
|
import get from 'lodash-es/get.js'
|
|
6
8
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
7
9
|
import strleft from 'wsemi/src/strleft.mjs'
|
|
10
|
+
import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
ot.extend(utc)
|
|
14
|
+
ot.extend(timezone)
|
|
8
15
|
|
|
9
16
|
|
|
10
17
|
async function formatter(opt) {
|
|
@@ -22,17 +29,19 @@ async function formatter(opt) {
|
|
|
22
29
|
interval = 'day'
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
//timeZone, IANA時區字串如'Asia/Taipei', 為null時使用系統時區
|
|
33
|
+
let timeZone = get(opt, 'timeZone', null)
|
|
34
|
+
|
|
35
|
+
//建立logs資料夾, 已存在則視為成功, 不存在則自動建立
|
|
36
|
+
fsCreateFolder(fdLog)
|
|
29
37
|
|
|
30
38
|
let streamCurr = null
|
|
31
39
|
let keyCurr = ''
|
|
32
40
|
|
|
33
41
|
let getTimeKey = () => {
|
|
34
|
-
|
|
35
|
-
let
|
|
42
|
+
//依timeZone決定時區基準, 確保跨機器切檔邊界與檔名一致
|
|
43
|
+
let d = isestr(timeZone) ? ot().tz(timeZone) : ot()
|
|
44
|
+
let t = d.format('YYYY-MM-DDTHH:mm:ss.SSSZ')
|
|
36
45
|
if (interval === 'hr') {
|
|
37
46
|
// return d.toISOString().slice(0, 13).replace(/:/g, '-') //例如2025-07-06T14
|
|
38
47
|
t = strleft(t, 13)
|