logixlysia 5.3.0 → 6.0.0
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 +63 -91
- package/dist/index.js +5 -12
- package/package.json +26 -74
- package/src/extensions/banner.ts +26 -0
- package/src/extensions/index.ts +28 -0
- package/src/helpers/status.ts +58 -0
- package/src/index.ts +106 -0
- package/src/interfaces.ts +117 -0
- package/src/logger/create-logger.ts +246 -0
- package/src/logger/handle-http-error.ts +51 -0
- package/src/logger/index.ts +126 -0
- package/src/output/file.ts +85 -0
- package/src/output/fs.ts +5 -0
- package/src/output/index.ts +58 -0
- package/src/output/rotation-manager.ts +122 -0
- package/src/utils/error.ts +15 -0
- package/src/utils/rotation.ts +91 -0
- package/LICENSE +0 -21
- package/README.md +0 -57
- package/dist/index.cjs +0 -13
- package/dist/index.d.cts +0 -99
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import pino from 'pino'
|
|
2
|
+
import type {
|
|
3
|
+
Logger,
|
|
4
|
+
LogLevel,
|
|
5
|
+
Options,
|
|
6
|
+
Pino,
|
|
7
|
+
RequestInfo,
|
|
8
|
+
StoreData
|
|
9
|
+
} from '../interfaces'
|
|
10
|
+
import { logToTransports } from '../output'
|
|
11
|
+
import { logToFile } from '../output/file'
|
|
12
|
+
import { formatLine } from './create-logger'
|
|
13
|
+
import { handleHttpError } from './handle-http-error'
|
|
14
|
+
|
|
15
|
+
export const createLogger = (options: Options = {}): Logger => {
|
|
16
|
+
const config = options.config
|
|
17
|
+
|
|
18
|
+
const pinoConfig = config?.pino
|
|
19
|
+
const { prettyPrint, ...pinoOptions } = pinoConfig ?? {}
|
|
20
|
+
|
|
21
|
+
const shouldPrettyPrint =
|
|
22
|
+
prettyPrint === true && pinoOptions.transport === undefined
|
|
23
|
+
|
|
24
|
+
const transport = shouldPrettyPrint
|
|
25
|
+
? pino.transport({
|
|
26
|
+
target: 'pino-pretty',
|
|
27
|
+
options: {
|
|
28
|
+
colorize: process.stdout?.isTTY === true,
|
|
29
|
+
translateTime: config?.timestamp?.translateTime,
|
|
30
|
+
messageKey: pinoOptions.messageKey,
|
|
31
|
+
errorKey: pinoOptions.errorKey
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
: pinoOptions.transport
|
|
35
|
+
|
|
36
|
+
const pinoLogger: Pino = pino({
|
|
37
|
+
...pinoOptions,
|
|
38
|
+
level: pinoOptions.level ?? 'info',
|
|
39
|
+
messageKey: pinoOptions.messageKey,
|
|
40
|
+
errorKey: pinoOptions.errorKey,
|
|
41
|
+
transport
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const log = (
|
|
45
|
+
level: LogLevel,
|
|
46
|
+
request: RequestInfo,
|
|
47
|
+
data: Record<string, unknown>,
|
|
48
|
+
store: StoreData
|
|
49
|
+
): void => {
|
|
50
|
+
logToTransports({ level, request, data, store, options })
|
|
51
|
+
|
|
52
|
+
const useTransportsOnly = config?.useTransportsOnly === true
|
|
53
|
+
const disableInternalLogger = config?.disableInternalLogger === true
|
|
54
|
+
const disableFileLogging = config?.disableFileLogging === true
|
|
55
|
+
|
|
56
|
+
if (!(useTransportsOnly || disableFileLogging)) {
|
|
57
|
+
const filePath = config?.logFilePath
|
|
58
|
+
if (filePath) {
|
|
59
|
+
logToFile({ filePath, level, request, data, store, options }).catch(
|
|
60
|
+
() => {
|
|
61
|
+
// Ignore errors
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (useTransportsOnly || disableInternalLogger) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const message = formatLine({ level, request, data, store, options })
|
|
72
|
+
|
|
73
|
+
switch (level) {
|
|
74
|
+
case 'DEBUG': {
|
|
75
|
+
console.debug(message)
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
case 'INFO': {
|
|
79
|
+
console.info(message)
|
|
80
|
+
break
|
|
81
|
+
}
|
|
82
|
+
case 'WARNING': {
|
|
83
|
+
console.warn(message)
|
|
84
|
+
break
|
|
85
|
+
}
|
|
86
|
+
case 'ERROR': {
|
|
87
|
+
console.error(message)
|
|
88
|
+
break
|
|
89
|
+
}
|
|
90
|
+
default: {
|
|
91
|
+
console.log(message)
|
|
92
|
+
break
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const logWithContext = (
|
|
98
|
+
level: LogLevel,
|
|
99
|
+
request: RequestInfo,
|
|
100
|
+
message: string,
|
|
101
|
+
context?: Record<string, unknown>
|
|
102
|
+
): void => {
|
|
103
|
+
const store: StoreData = { beforeTime: process.hrtime.bigint() }
|
|
104
|
+
log(level, request, { message, context }, store)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
pino: pinoLogger,
|
|
109
|
+
log,
|
|
110
|
+
handleHttpError: (request, error, store) => {
|
|
111
|
+
handleHttpError(request, error, store, options)
|
|
112
|
+
},
|
|
113
|
+
debug: (request, message, context) => {
|
|
114
|
+
logWithContext('DEBUG', request, message, context)
|
|
115
|
+
},
|
|
116
|
+
info: (request, message, context) => {
|
|
117
|
+
logWithContext('INFO', request, message, context)
|
|
118
|
+
},
|
|
119
|
+
warn: (request, message, context) => {
|
|
120
|
+
logWithContext('WARNING', request, message, context)
|
|
121
|
+
},
|
|
122
|
+
error: (request, message, context) => {
|
|
123
|
+
logWithContext('ERROR', request, message, context)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { appendFile } from 'node:fs/promises'
|
|
2
|
+
import { dirname } from 'node:path'
|
|
3
|
+
import type { LogLevel, Options, RequestInfo, StoreData } from '../interfaces'
|
|
4
|
+
import { ensureDir } from './fs'
|
|
5
|
+
import { performRotation, shouldRotate } from './rotation-manager'
|
|
6
|
+
|
|
7
|
+
type LogToFileInput = {
|
|
8
|
+
filePath: string
|
|
9
|
+
level: LogLevel
|
|
10
|
+
request: RequestInfo
|
|
11
|
+
data: Record<string, unknown>
|
|
12
|
+
store: StoreData
|
|
13
|
+
options: Options
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const logToFile = async (
|
|
17
|
+
...args:
|
|
18
|
+
| [LogToFileInput]
|
|
19
|
+
| [
|
|
20
|
+
string,
|
|
21
|
+
LogLevel,
|
|
22
|
+
RequestInfo,
|
|
23
|
+
Record<string, unknown>,
|
|
24
|
+
StoreData,
|
|
25
|
+
Options
|
|
26
|
+
]
|
|
27
|
+
): Promise<void> => {
|
|
28
|
+
const input: LogToFileInput =
|
|
29
|
+
typeof args[0] === 'string'
|
|
30
|
+
? (() => {
|
|
31
|
+
const [
|
|
32
|
+
filePathArg,
|
|
33
|
+
levelArg,
|
|
34
|
+
requestArg,
|
|
35
|
+
dataArg,
|
|
36
|
+
storeArg,
|
|
37
|
+
optionsArg
|
|
38
|
+
] = args as [
|
|
39
|
+
string,
|
|
40
|
+
LogLevel,
|
|
41
|
+
RequestInfo,
|
|
42
|
+
Record<string, unknown>,
|
|
43
|
+
StoreData,
|
|
44
|
+
Options
|
|
45
|
+
]
|
|
46
|
+
return {
|
|
47
|
+
filePath: filePathArg,
|
|
48
|
+
level: levelArg,
|
|
49
|
+
request: requestArg,
|
|
50
|
+
data: dataArg,
|
|
51
|
+
store: storeArg,
|
|
52
|
+
options: optionsArg
|
|
53
|
+
}
|
|
54
|
+
})()
|
|
55
|
+
: args[0]
|
|
56
|
+
|
|
57
|
+
const { filePath, level, request, data, store, options } = input
|
|
58
|
+
const config = options.config
|
|
59
|
+
const useTransportsOnly = config?.useTransportsOnly === true
|
|
60
|
+
const disableFileLogging = config?.disableFileLogging === true
|
|
61
|
+
if (useTransportsOnly || disableFileLogging) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const message = typeof data.message === 'string' ? data.message : ''
|
|
66
|
+
const durationMs =
|
|
67
|
+
store.beforeTime === BigInt(0)
|
|
68
|
+
? 0
|
|
69
|
+
: Number(process.hrtime.bigint() - store.beforeTime) / 1_000_000
|
|
70
|
+
|
|
71
|
+
const line = `${level} ${durationMs.toFixed(2)}ms ${request.method} ${new URL(request.url).pathname} ${message}\n`
|
|
72
|
+
|
|
73
|
+
await ensureDir(dirname(filePath))
|
|
74
|
+
await appendFile(filePath, line, { encoding: 'utf-8' })
|
|
75
|
+
|
|
76
|
+
const rotation = config?.logRotation
|
|
77
|
+
if (!rotation) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const should = await shouldRotate(filePath, rotation)
|
|
82
|
+
if (should) {
|
|
83
|
+
await performRotation(filePath, rotation)
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/output/fs.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { LogLevel, Options, RequestInfo, StoreData } from '../interfaces'
|
|
2
|
+
|
|
3
|
+
type LogToTransportsInput = {
|
|
4
|
+
level: LogLevel
|
|
5
|
+
request: RequestInfo
|
|
6
|
+
data: Record<string, unknown>
|
|
7
|
+
store: StoreData
|
|
8
|
+
options: Options
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const logToTransports = (
|
|
12
|
+
...args:
|
|
13
|
+
| [LogToTransportsInput]
|
|
14
|
+
| [LogLevel, RequestInfo, Record<string, unknown>, StoreData, Options]
|
|
15
|
+
): void => {
|
|
16
|
+
const input: LogToTransportsInput =
|
|
17
|
+
typeof args[0] === 'string'
|
|
18
|
+
? {
|
|
19
|
+
level: args[0],
|
|
20
|
+
request: args[1],
|
|
21
|
+
data: args[2],
|
|
22
|
+
store: args[3],
|
|
23
|
+
options: args[4]
|
|
24
|
+
}
|
|
25
|
+
: args[0]
|
|
26
|
+
|
|
27
|
+
const { level, request, data, store, options } = input
|
|
28
|
+
const transports = options.config?.transports ?? []
|
|
29
|
+
if (transports.length === 0) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const message = typeof data.message === 'string' ? data.message : ''
|
|
34
|
+
const meta: Record<string, unknown> = {
|
|
35
|
+
request: {
|
|
36
|
+
method: request.method,
|
|
37
|
+
url: request.url
|
|
38
|
+
},
|
|
39
|
+
...data,
|
|
40
|
+
beforeTime: store.beforeTime
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const transport of transports) {
|
|
44
|
+
try {
|
|
45
|
+
const result = transport.log(level, message, meta)
|
|
46
|
+
if (
|
|
47
|
+
result &&
|
|
48
|
+
typeof (result as { catch?: unknown }).catch === 'function'
|
|
49
|
+
) {
|
|
50
|
+
;(result as Promise<void>).catch(() => {
|
|
51
|
+
// Ignore errors
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
// Transport failures must never crash application logging.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
console.error(`Parsing error: ${message}`)
|
|
13
|
+
|
|
14
|
+
return message
|
|
15
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs'
|
|
2
|
+
import { basename, dirname } from 'node:path'
|
|
3
|
+
|
|
4
|
+
const SIZE_REGEX = /^(\d+(?:\.\d+)?)(k|kb|m|mb|g|gb)$/i
|
|
5
|
+
const INTERVAL_REGEX = /^(\d+)(h|d|w)$/i
|
|
6
|
+
const ROTATED_REGEX = /\.(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})(?:\.gz)?$/
|
|
7
|
+
|
|
8
|
+
export const parseSize = (value: number | string): number => {
|
|
9
|
+
if (typeof value === 'number') {
|
|
10
|
+
return value
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const trimmed = value.trim()
|
|
14
|
+
const asNumber = Number(trimmed)
|
|
15
|
+
if (Number.isFinite(asNumber)) {
|
|
16
|
+
return asNumber
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const match = trimmed.match(SIZE_REGEX)
|
|
20
|
+
if (!match) {
|
|
21
|
+
throw new Error(`Invalid size format: ${value}`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const amount = Number(match[1])
|
|
25
|
+
const unit = match[2].toLowerCase()
|
|
26
|
+
|
|
27
|
+
let base = 1024
|
|
28
|
+
if (unit.startsWith('m')) {
|
|
29
|
+
base = 1024 * 1024
|
|
30
|
+
} else if (unit.startsWith('g')) {
|
|
31
|
+
base = 1024 * 1024 * 1024
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return Math.floor(amount * base)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const parseInterval = (value: string): number => {
|
|
38
|
+
const match = value.trim().match(INTERVAL_REGEX)
|
|
39
|
+
if (!match) {
|
|
40
|
+
throw new Error(`Invalid interval format: ${value}`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const amount = Number(match[1])
|
|
44
|
+
const unit = match[2].toLowerCase()
|
|
45
|
+
|
|
46
|
+
let ms = 60 * 60 * 1000
|
|
47
|
+
if (unit === 'd') {
|
|
48
|
+
ms = 24 * 60 * 60 * 1000
|
|
49
|
+
} else if (unit === 'w') {
|
|
50
|
+
ms = 7 * 24 * 60 * 60 * 1000
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return amount * ms
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const parseRetention = (
|
|
57
|
+
value: number | string
|
|
58
|
+
): { type: 'count' | 'time'; value: number } => {
|
|
59
|
+
if (typeof value === 'number') {
|
|
60
|
+
return { type: 'count', value }
|
|
61
|
+
}
|
|
62
|
+
return { type: 'time', value: parseInterval(value) }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const shouldRotateBySize = async (
|
|
66
|
+
filePath: string,
|
|
67
|
+
maxSizeBytes: number
|
|
68
|
+
): Promise<boolean> => {
|
|
69
|
+
try {
|
|
70
|
+
const stat = await fs.stat(filePath)
|
|
71
|
+
return stat.size > maxSizeBytes
|
|
72
|
+
} catch {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const getRotatedFiles = async (filePath: string): Promise<string[]> => {
|
|
78
|
+
const dir = dirname(filePath)
|
|
79
|
+
const base = basename(filePath)
|
|
80
|
+
|
|
81
|
+
let entries: string[]
|
|
82
|
+
try {
|
|
83
|
+
entries = await fs.readdir(dir)
|
|
84
|
+
} catch {
|
|
85
|
+
return []
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return entries
|
|
89
|
+
.filter(name => name.startsWith(`${base}.`) && ROTATED_REGEX.test(name))
|
|
90
|
+
.map(name => `${dir}/${name}`)
|
|
91
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 PunGrumpy
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
<h1><code>🦊</code> Logixlysia</h1>
|
|
3
|
-
<strong>Logixlysia is a logging library for ElysiaJS</strong>
|
|
4
|
-
<img src="./website/app/opengraph-image.png" alt="Logixlysia" width="100%" height="auto" />
|
|
5
|
-
</div>
|
|
6
|
-
|
|
7
|
-
## `📩` Installation
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
bun add logixlysia
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## `📝` Usage
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
import { Elysia } from 'elysia'
|
|
17
|
-
import logixlysia from 'logixlysia'
|
|
18
|
-
|
|
19
|
-
const app = new Elysia({
|
|
20
|
-
name: 'Logixlysia Example'
|
|
21
|
-
}).use(
|
|
22
|
-
logixlysia({
|
|
23
|
-
config: {
|
|
24
|
-
showStartupMessage: true,
|
|
25
|
-
startupMessageFormat: 'simple',
|
|
26
|
-
timestamp: {
|
|
27
|
-
translateTime: 'yyyy-mm-dd HH:MM:ss'
|
|
28
|
-
},
|
|
29
|
-
ip: true,
|
|
30
|
-
logFilePath: './logs/example.log',
|
|
31
|
-
logRotation: {
|
|
32
|
-
maxSize: '10m',
|
|
33
|
-
interval: '1d',
|
|
34
|
-
maxFiles: '7d',
|
|
35
|
-
compress: true
|
|
36
|
-
},
|
|
37
|
-
customLogFormat:
|
|
38
|
-
'🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip} {epoch}',
|
|
39
|
-
logFilter: {
|
|
40
|
-
level: ['ERROR', 'WARNING'],
|
|
41
|
-
status: [500, 404],
|
|
42
|
-
method: 'GET'
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
app.listen(3000)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## `📚` Documentation
|
|
52
|
-
|
|
53
|
-
Check out the [website](https://logixlysia.vercel.app) for more detailed documentation and examples.
|
|
54
|
-
|
|
55
|
-
## `📄` License
|
|
56
|
-
|
|
57
|
-
Licensed under the [MIT License](LICENSE).
|