@pori15/logixlysia 6.0.1 → 6.0.2
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/dist/index.d.ts +36 -10
- package/dist/index.js +7 -7
- package/package.json +2 -2
- package/src/Error/errors.ts +258 -258
- package/src/Error/type.ts +51 -51
- package/src/extensions/banner.ts +26 -26
- package/src/extensions/index.ts +28 -28
- package/src/helpers/status.ts +58 -58
- package/src/index.ts +162 -141
- package/src/interfaces.ts +138 -145
- package/src/logger/create-logger.ts +240 -246
- package/src/logger/handle-http-error.ts +60 -63
- package/src/logger/index.ts +138 -139
- package/src/output/file.ts +85 -85
- package/src/output/fs.ts +5 -5
- package/src/output/index.ts +58 -58
- package/src/output/rotation-manager.ts +122 -122
- package/src/utils/error.ts +13 -13
- package/src/utils/handle-error.ts +284 -289
- package/src/utils/rotation.ts +91 -91
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs'
|
|
2
|
-
import { promisify } from 'node:util'
|
|
3
|
-
import { gzip } from 'node:zlib'
|
|
4
|
-
import type { LogRotationConfig } from '../interfaces'
|
|
5
|
-
import {
|
|
6
|
-
getRotatedFiles,
|
|
7
|
-
parseRetention,
|
|
8
|
-
parseSize,
|
|
9
|
-
shouldRotateBySize
|
|
10
|
-
} from '../utils/rotation'
|
|
11
|
-
|
|
12
|
-
const gzipAsync = promisify(gzip)
|
|
13
|
-
|
|
14
|
-
const pad2 = (value: number): string => String(value).padStart(2, '0')
|
|
15
|
-
|
|
16
|
-
export const getRotatedFileName = (filePath: string, date: Date): string => {
|
|
17
|
-
const yyyy = date.getFullYear()
|
|
18
|
-
const mm = pad2(date.getMonth() + 1)
|
|
19
|
-
const dd = pad2(date.getDate())
|
|
20
|
-
const HH = pad2(date.getHours())
|
|
21
|
-
const MM = pad2(date.getMinutes())
|
|
22
|
-
const ss = pad2(date.getSeconds())
|
|
23
|
-
return `${filePath}.${yyyy}-${mm}-${dd}-${HH}-${MM}-${ss}`
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const rotateFile = async (filePath: string): Promise<string> => {
|
|
27
|
-
try {
|
|
28
|
-
const stat = await fs.stat(filePath)
|
|
29
|
-
if (stat.size === 0) {
|
|
30
|
-
return ''
|
|
31
|
-
}
|
|
32
|
-
} catch {
|
|
33
|
-
return ''
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const rotated = getRotatedFileName(filePath, new Date())
|
|
37
|
-
await fs.rename(filePath, rotated)
|
|
38
|
-
return rotated
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const compressFile = async (filePath: string): Promise<void> => {
|
|
42
|
-
const content = await fs.readFile(filePath)
|
|
43
|
-
const compressed = await gzipAsync(content)
|
|
44
|
-
await fs.writeFile(`${filePath}.gz`, compressed)
|
|
45
|
-
await fs.rm(filePath, { force: true })
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const shouldRotate = async (
|
|
49
|
-
filePath: string,
|
|
50
|
-
config: LogRotationConfig
|
|
51
|
-
): Promise<boolean> => {
|
|
52
|
-
if (config.maxSize === undefined) {
|
|
53
|
-
return false
|
|
54
|
-
}
|
|
55
|
-
const maxSize = parseSize(config.maxSize)
|
|
56
|
-
return await shouldRotateBySize(filePath, maxSize)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const cleanupByCount = async (
|
|
60
|
-
filePath: string,
|
|
61
|
-
maxFiles: number
|
|
62
|
-
): Promise<void> => {
|
|
63
|
-
const rotated = await getRotatedFiles(filePath)
|
|
64
|
-
if (rotated.length <= maxFiles) {
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const stats = await Promise.all(
|
|
69
|
-
rotated.map(async p => ({ path: p, stat: await fs.stat(p) }))
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
stats.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
|
|
73
|
-
const toDelete = stats.slice(maxFiles)
|
|
74
|
-
await Promise.all(toDelete.map(({ path }) => fs.rm(path, { force: true })))
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const cleanupByTime = async (
|
|
78
|
-
filePath: string,
|
|
79
|
-
maxAgeMs: number
|
|
80
|
-
): Promise<void> => {
|
|
81
|
-
const rotated = await getRotatedFiles(filePath)
|
|
82
|
-
if (rotated.length === 0) {
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const now = Date.now()
|
|
87
|
-
const stats = await Promise.all(
|
|
88
|
-
rotated.map(async p => ({ path: p, stat: await fs.stat(p) }))
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
const toDelete = stats.filter(({ stat }) => now - stat.mtimeMs > maxAgeMs)
|
|
92
|
-
await Promise.all(toDelete.map(({ path }) => fs.rm(path, { force: true })))
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const performRotation = async (
|
|
96
|
-
filePath: string,
|
|
97
|
-
config: LogRotationConfig
|
|
98
|
-
): Promise<void> => {
|
|
99
|
-
const rotated = await rotateFile(filePath)
|
|
100
|
-
if (!rotated) {
|
|
101
|
-
return
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const shouldCompress = config.compress === true
|
|
105
|
-
if (shouldCompress) {
|
|
106
|
-
const algo = config.compression ?? 'gzip'
|
|
107
|
-
if (algo === 'gzip') {
|
|
108
|
-
await compressFile(rotated)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (config.maxFiles !== undefined) {
|
|
113
|
-
const retention = parseRetention(config.maxFiles)
|
|
114
|
-
if (retention.type === 'count') {
|
|
115
|
-
await cleanupByCount(filePath, retention.value)
|
|
116
|
-
} else {
|
|
117
|
-
await cleanupByTime(filePath, retention.value)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Optional interval-based rotation cleanup (create interval directories / naming) is not required by tests.
|
|
122
|
-
}
|
|
1
|
+
import { promises as fs } from 'node:fs'
|
|
2
|
+
import { promisify } from 'node:util'
|
|
3
|
+
import { gzip } from 'node:zlib'
|
|
4
|
+
import type { LogRotationConfig } from '../interfaces'
|
|
5
|
+
import {
|
|
6
|
+
getRotatedFiles,
|
|
7
|
+
parseRetention,
|
|
8
|
+
parseSize,
|
|
9
|
+
shouldRotateBySize
|
|
10
|
+
} from '../utils/rotation'
|
|
11
|
+
|
|
12
|
+
const gzipAsync = promisify(gzip)
|
|
13
|
+
|
|
14
|
+
const pad2 = (value: number): string => String(value).padStart(2, '0')
|
|
15
|
+
|
|
16
|
+
export const getRotatedFileName = (filePath: string, date: Date): string => {
|
|
17
|
+
const yyyy = date.getFullYear()
|
|
18
|
+
const mm = pad2(date.getMonth() + 1)
|
|
19
|
+
const dd = pad2(date.getDate())
|
|
20
|
+
const HH = pad2(date.getHours())
|
|
21
|
+
const MM = pad2(date.getMinutes())
|
|
22
|
+
const ss = pad2(date.getSeconds())
|
|
23
|
+
return `${filePath}.${yyyy}-${mm}-${dd}-${HH}-${MM}-${ss}`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const rotateFile = async (filePath: string): Promise<string> => {
|
|
27
|
+
try {
|
|
28
|
+
const stat = await fs.stat(filePath)
|
|
29
|
+
if (stat.size === 0) {
|
|
30
|
+
return ''
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
return ''
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const rotated = getRotatedFileName(filePath, new Date())
|
|
37
|
+
await fs.rename(filePath, rotated)
|
|
38
|
+
return rotated
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const compressFile = async (filePath: string): Promise<void> => {
|
|
42
|
+
const content = await fs.readFile(filePath)
|
|
43
|
+
const compressed = await gzipAsync(content)
|
|
44
|
+
await fs.writeFile(`${filePath}.gz`, compressed)
|
|
45
|
+
await fs.rm(filePath, { force: true })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const shouldRotate = async (
|
|
49
|
+
filePath: string,
|
|
50
|
+
config: LogRotationConfig
|
|
51
|
+
): Promise<boolean> => {
|
|
52
|
+
if (config.maxSize === undefined) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
const maxSize = parseSize(config.maxSize)
|
|
56
|
+
return await shouldRotateBySize(filePath, maxSize)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const cleanupByCount = async (
|
|
60
|
+
filePath: string,
|
|
61
|
+
maxFiles: number
|
|
62
|
+
): Promise<void> => {
|
|
63
|
+
const rotated = await getRotatedFiles(filePath)
|
|
64
|
+
if (rotated.length <= maxFiles) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const stats = await Promise.all(
|
|
69
|
+
rotated.map(async p => ({ path: p, stat: await fs.stat(p) }))
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
stats.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
|
|
73
|
+
const toDelete = stats.slice(maxFiles)
|
|
74
|
+
await Promise.all(toDelete.map(({ path }) => fs.rm(path, { force: true })))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const cleanupByTime = async (
|
|
78
|
+
filePath: string,
|
|
79
|
+
maxAgeMs: number
|
|
80
|
+
): Promise<void> => {
|
|
81
|
+
const rotated = await getRotatedFiles(filePath)
|
|
82
|
+
if (rotated.length === 0) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const now = Date.now()
|
|
87
|
+
const stats = await Promise.all(
|
|
88
|
+
rotated.map(async p => ({ path: p, stat: await fs.stat(p) }))
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
const toDelete = stats.filter(({ stat }) => now - stat.mtimeMs > maxAgeMs)
|
|
92
|
+
await Promise.all(toDelete.map(({ path }) => fs.rm(path, { force: true })))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const performRotation = async (
|
|
96
|
+
filePath: string,
|
|
97
|
+
config: LogRotationConfig
|
|
98
|
+
): Promise<void> => {
|
|
99
|
+
const rotated = await rotateFile(filePath)
|
|
100
|
+
if (!rotated) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const shouldCompress = config.compress === true
|
|
105
|
+
if (shouldCompress) {
|
|
106
|
+
const algo = config.compression ?? 'gzip'
|
|
107
|
+
if (algo === 'gzip') {
|
|
108
|
+
await compressFile(rotated)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (config.maxFiles !== undefined) {
|
|
113
|
+
const retention = parseRetention(config.maxFiles)
|
|
114
|
+
if (retention.type === 'count') {
|
|
115
|
+
await cleanupByCount(filePath, retention.value)
|
|
116
|
+
} else {
|
|
117
|
+
await cleanupByTime(filePath, retention.value)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Optional interval-based rotation cleanup (create interval directories / naming) is not required by tests.
|
|
122
|
+
}
|
package/src/utils/error.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export const parseError = (error: unknown): string => {
|
|
2
|
-
let message = 'An error occurred'
|
|
3
|
-
|
|
4
|
-
if (error instanceof Error) {
|
|
5
|
-
message = error.message
|
|
6
|
-
} else if (error && typeof error === 'object' && 'message' in error) {
|
|
7
|
-
message = error.message as string
|
|
8
|
-
} else {
|
|
9
|
-
message = String(error)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return message
|
|
13
|
-
}
|
|
1
|
+
export const parseError = (error: unknown): string => {
|
|
2
|
+
let message = 'An error occurred'
|
|
3
|
+
|
|
4
|
+
if (error instanceof Error) {
|
|
5
|
+
message = error.message
|
|
6
|
+
} else if (error && typeof error === 'object' && 'message' in error) {
|
|
7
|
+
message = error.message as string
|
|
8
|
+
} else {
|
|
9
|
+
message = String(error)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return message
|
|
13
|
+
}
|