logixlysia 3.6.0 → 3.6.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/CHANGELOG.md +14 -0
- package/package.json +10 -9
- package/src/core/buildLogMessage.ts +64 -0
- package/src/core/createLogger.ts +57 -0
- package/src/core/filter.ts +22 -0
- package/src/core/handleHttpError.ts +30 -0
- package/src/index.ts +10 -22
- package/src/plugins/index.ts +1 -0
- package/src/{utils/start.ts → plugins/startServer.ts} +7 -21
- package/src/transports/console.ts +22 -0
- package/src/transports/file.ts +29 -0
- package/src/transports/index.ts +2 -22
- package/src/{types.ts → types/index.ts} +29 -25
- package/src/utils/colorMapping.ts +3 -15
- package/src/utils/duration.ts +15 -33
- package/src/utils/index.ts +6 -0
- package/src/utils/log.ts +2 -12
- package/src/utils/method.ts +4 -10
- package/src/utils/path.ts +4 -10
- package/src/utils/status.ts +4 -10
- package/tests/__mocks__/fs.ts +9 -0
- package/tests/core/buildLogMessage.test.ts +45 -0
- package/tests/core/filter.test.ts +35 -0
- package/tests/helpers.ts +19 -0
- package/tests/logixlysia.test.ts +8 -0
- package/tests/transports/logToTransports.test.ts +29 -0
- package/tsconfig.json +0 -3
- package/src/logger/buildLogMessage.ts +0 -59
- package/src/logger/createLogger.ts +0 -62
- package/src/logger/filter.ts +0 -31
- package/src/logger/handleHttpError.ts +0 -28
- package/src/logger/logToFile.ts +0 -40
- /package/src/{logger → core}/index.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.6.2](https://github.com/PunGrumpy/logixlysia/compare/v3.6.1...v3.6.2) (2024-09-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **logtofile:** store error to log file ([1bb2d07](https://github.com/PunGrumpy/logixlysia/commit/1bb2d07ae0c815742a9eb7c930ac8487ac287b2c)), closes [#63](https://github.com/PunGrumpy/logixlysia/issues/63)
|
|
9
|
+
|
|
10
|
+
## [3.6.1](https://github.com/PunGrumpy/logixlysia/compare/v3.6.0...v3.6.1) (2024-08-21)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Performance Improvements
|
|
14
|
+
|
|
15
|
+
* **logger:** improve performance and fix TypeScript errors ([d3ed751](https://github.com/PunGrumpy/logixlysia/commit/d3ed751041443b9bd2ce53350994e1443df40971))
|
|
16
|
+
|
|
3
17
|
## [3.6.0](https://github.com/PunGrumpy/logixlysia/compare/v3.5.0...v3.6.0) (2024-07-24)
|
|
4
18
|
|
|
5
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "logixlysia",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.2",
|
|
4
4
|
"description": "🦊 Logixlysia is a logger for Elysia",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
},
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"scripts": {
|
|
18
|
-
"lint": "eslint
|
|
19
|
-
"lint:fix": "eslint
|
|
18
|
+
"lint": "eslint .",
|
|
19
|
+
"lint:fix": "eslint . --fix",
|
|
20
20
|
"test": "bun test --timeout 5000 --coverage",
|
|
21
|
-
"test:ci": "bun test --timeout 5000 --coverage",
|
|
21
|
+
"test:ci": "bun test --timeout 5000 --coverage --coverage-reporter=lcov",
|
|
22
22
|
"publish": "npm publish",
|
|
23
23
|
"dev": "bun run --watch example/basic.ts",
|
|
24
24
|
"prepare": "husky",
|
|
@@ -75,19 +75,20 @@
|
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@elysiajs/eden": "^1.1.1",
|
|
78
|
-
"@eslint/js": "^9.
|
|
78
|
+
"@eslint/js": "^9.8.0",
|
|
79
79
|
"@trunkio/launcher": "^1.3.1",
|
|
80
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
81
|
-
"@typescript-eslint/parser": "^
|
|
80
|
+
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
|
81
|
+
"@typescript-eslint/parser": "^8.0.1",
|
|
82
82
|
"bun-types": "^1.1.20",
|
|
83
83
|
"commitizen": "^4.3.0",
|
|
84
84
|
"cz-conventional-changelog": "^3.3.0",
|
|
85
|
-
"eslint": "
|
|
85
|
+
"eslint": "9.x",
|
|
86
86
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
87
|
+
"globals": "^15.9.0",
|
|
87
88
|
"husky": "^9.1.1",
|
|
88
89
|
"lint-staged": "^15.2.7",
|
|
89
90
|
"prettier": "^3.3.3",
|
|
90
|
-
"typescript-eslint": "^
|
|
91
|
+
"typescript-eslint": "^8.0.1"
|
|
91
92
|
},
|
|
92
93
|
"peerDependencies": {
|
|
93
94
|
"typescript": "^5.2.2"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
LogComponents,
|
|
5
|
+
LogData,
|
|
6
|
+
LogLevel,
|
|
7
|
+
Options,
|
|
8
|
+
RequestInfo,
|
|
9
|
+
StoreData
|
|
10
|
+
} from '../types'
|
|
11
|
+
import {
|
|
12
|
+
durationString,
|
|
13
|
+
logString,
|
|
14
|
+
methodString,
|
|
15
|
+
pathString,
|
|
16
|
+
statusString
|
|
17
|
+
} from '../utils'
|
|
18
|
+
|
|
19
|
+
const defaultLogFormat =
|
|
20
|
+
'🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}'
|
|
21
|
+
|
|
22
|
+
function shouldUseColors(useColors: boolean, options?: Options): boolean {
|
|
23
|
+
if (options?.config?.useColors !== undefined) {
|
|
24
|
+
return options.config.useColors && process.env.NO_COLOR === undefined
|
|
25
|
+
}
|
|
26
|
+
return useColors && process.env.NO_COLOR === undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function buildLogMessage(
|
|
30
|
+
level: LogLevel,
|
|
31
|
+
request: RequestInfo,
|
|
32
|
+
data: LogData,
|
|
33
|
+
store: StoreData,
|
|
34
|
+
options?: Options,
|
|
35
|
+
useColors: boolean = true
|
|
36
|
+
): string {
|
|
37
|
+
const actuallyUseColors = shouldUseColors(useColors, options)
|
|
38
|
+
const now = new Date()
|
|
39
|
+
const components: LogComponents = {
|
|
40
|
+
now: actuallyUseColors
|
|
41
|
+
? chalk.bgYellow(chalk.black(now.toLocaleString()))
|
|
42
|
+
: now.toLocaleString(),
|
|
43
|
+
epoch: Math.floor(now.getTime() / 1000).toString(),
|
|
44
|
+
level: logString(level, actuallyUseColors),
|
|
45
|
+
duration: durationString(store.beforeTime, actuallyUseColors),
|
|
46
|
+
method: methodString(request.method, actuallyUseColors),
|
|
47
|
+
pathname: pathString(request),
|
|
48
|
+
status: statusString(data.status || 200, actuallyUseColors),
|
|
49
|
+
message: data.message || '',
|
|
50
|
+
ip:
|
|
51
|
+
options?.config?.ip && request.headers.get('x-forwarded-for')
|
|
52
|
+
? `IP: ${request.headers.get('x-forwarded-for')}`
|
|
53
|
+
: ''
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const logFormat = options?.config?.customLogFormat || defaultLogFormat
|
|
57
|
+
|
|
58
|
+
return logFormat.replace(/{(\w+)}/g, (_, key: string) => {
|
|
59
|
+
if (key in components) {
|
|
60
|
+
return components[key as keyof LogComponents] || ''
|
|
61
|
+
}
|
|
62
|
+
return ''
|
|
63
|
+
})
|
|
64
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { logToTransports } from '../transports'
|
|
2
|
+
import { logToFile } from '../transports'
|
|
3
|
+
import {
|
|
4
|
+
LogData,
|
|
5
|
+
Logger,
|
|
6
|
+
LogLevel,
|
|
7
|
+
Options,
|
|
8
|
+
RequestInfo,
|
|
9
|
+
StoreData
|
|
10
|
+
} from '../types'
|
|
11
|
+
import { buildLogMessage } from './buildLogMessage'
|
|
12
|
+
import { filterLog } from './filter'
|
|
13
|
+
import { handleHttpError } from './handleHttpError'
|
|
14
|
+
|
|
15
|
+
async function log(
|
|
16
|
+
level: LogLevel,
|
|
17
|
+
request: RequestInfo,
|
|
18
|
+
data: LogData,
|
|
19
|
+
store: StoreData,
|
|
20
|
+
options?: Options
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
if (!filterLog(level, data.status || 200, request.method, options)) return
|
|
23
|
+
|
|
24
|
+
const logMessage = buildLogMessage(level, request, data, store, options, true)
|
|
25
|
+
console.log(logMessage)
|
|
26
|
+
|
|
27
|
+
const promises = []
|
|
28
|
+
|
|
29
|
+
if (options?.config?.logFilePath) {
|
|
30
|
+
promises.push(
|
|
31
|
+
logToFile(
|
|
32
|
+
options.config.logFilePath,
|
|
33
|
+
level,
|
|
34
|
+
request,
|
|
35
|
+
data,
|
|
36
|
+
store,
|
|
37
|
+
options
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (options?.config?.transports?.length) {
|
|
43
|
+
promises.push(logToTransports(level, request, data, store, options))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await Promise.all(promises)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createLogger(options?: Options): Logger {
|
|
50
|
+
return {
|
|
51
|
+
log: (level, request, data, store) =>
|
|
52
|
+
log(level, request, data, store, options),
|
|
53
|
+
handleHttpError: (request, error, store) =>
|
|
54
|
+
handleHttpError(request, error, store, options),
|
|
55
|
+
customLogFormat: options?.config?.customLogFormat
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LogLevel, Options } from '../types'
|
|
2
|
+
|
|
3
|
+
const checkFilter = (filterValue: any, value: any) =>
|
|
4
|
+
Array.isArray(filterValue)
|
|
5
|
+
? filterValue.includes(value)
|
|
6
|
+
: filterValue === value
|
|
7
|
+
|
|
8
|
+
export function filterLog(
|
|
9
|
+
logLevel: LogLevel,
|
|
10
|
+
status: number,
|
|
11
|
+
method: string,
|
|
12
|
+
options?: Options
|
|
13
|
+
): boolean {
|
|
14
|
+
const filter = options?.config?.logFilter
|
|
15
|
+
if (!filter) return true
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
(!filter.level || checkFilter(filter.level, logLevel)) &&
|
|
19
|
+
(!filter.status || checkFilter(filter.status, status)) &&
|
|
20
|
+
(!filter.method || checkFilter(filter.method, method))
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { logToFile } from '../transports'
|
|
2
|
+
import { HttpError, Options, RequestInfo, StoreData } from '../types'
|
|
3
|
+
import { buildLogMessage } from './buildLogMessage'
|
|
4
|
+
|
|
5
|
+
export function handleHttpError(
|
|
6
|
+
request: RequestInfo,
|
|
7
|
+
error: HttpError,
|
|
8
|
+
store: StoreData,
|
|
9
|
+
options?: Options
|
|
10
|
+
): void {
|
|
11
|
+
const statusCode = error.status || 500
|
|
12
|
+
console.error(
|
|
13
|
+
buildLogMessage('ERROR', request, { status: statusCode }, store, options)
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const promises = []
|
|
17
|
+
|
|
18
|
+
if (options?.config?.logFilePath) {
|
|
19
|
+
promises.push(
|
|
20
|
+
logToFile(
|
|
21
|
+
options.config.logFilePath,
|
|
22
|
+
'ERROR',
|
|
23
|
+
request,
|
|
24
|
+
{ status: statusCode },
|
|
25
|
+
store,
|
|
26
|
+
options
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import Elysia from 'elysia'
|
|
1
|
+
import { Elysia } from 'elysia'
|
|
3
2
|
|
|
4
|
-
import { createLogger
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
3
|
+
import { createLogger } from './core'
|
|
4
|
+
import { startServer } from './plugins'
|
|
5
|
+
import { HttpError, Options, Server } from './types'
|
|
7
6
|
|
|
8
|
-
/**
|
|
9
|
-
* Creates a logger plugin for ElysiaJS.
|
|
10
|
-
*
|
|
11
|
-
* @export
|
|
12
|
-
* @module logger
|
|
13
|
-
* @category Logger
|
|
14
|
-
* @subcategory Functions
|
|
15
|
-
*
|
|
16
|
-
* @name Logixlysia
|
|
17
|
-
* @description Logixlysia is a logger plugin for ElysiaJS.
|
|
18
|
-
* @param {Options} [options] Configuration options for the logger.
|
|
19
|
-
*
|
|
20
|
-
* @returns {Elysia} The logger plugin for ElysiaJS.
|
|
21
|
-
*/
|
|
22
7
|
export default function logixlysia(options?: Options): Elysia {
|
|
23
8
|
const log = createLogger(options)
|
|
24
9
|
|
|
@@ -33,11 +18,14 @@ export default function logixlysia(options?: Options): Elysia {
|
|
|
33
18
|
log.log('INFO', request, { status: 200 }, store as { beforeTime: bigint })
|
|
34
19
|
})
|
|
35
20
|
.onError({ as: 'global' }, ({ request, error, store }) => {
|
|
36
|
-
handleHttpError(
|
|
21
|
+
log.handleHttpError(
|
|
37
22
|
request,
|
|
38
23
|
error as HttpError,
|
|
39
|
-
store as { beforeTime: bigint }
|
|
40
|
-
options
|
|
24
|
+
store as { beforeTime: bigint }
|
|
41
25
|
)
|
|
42
26
|
})
|
|
43
27
|
}
|
|
28
|
+
|
|
29
|
+
export { createLogger } from './core'
|
|
30
|
+
export { handleHttpError } from './core'
|
|
31
|
+
export { logToTransports } from './transports'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as startServer } from './startServer'
|
|
@@ -1,25 +1,12 @@
|
|
|
1
|
-
import { Options, Server } from '
|
|
1
|
+
import { Options, Server } from '../types'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* Creates a box text.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} text The text.
|
|
7
|
-
* @param {number} width The box width.
|
|
8
|
-
* @returns {string} The box text.
|
|
9
|
-
*/
|
|
10
|
-
function createBoxText(text: string, width: number): string {
|
|
3
|
+
const createBoxText = (text: string, width: number): string => {
|
|
11
4
|
const paddingLength = Math.max(0, (width - text.length) / 2)
|
|
12
5
|
const padding = ' '.repeat(paddingLength)
|
|
13
6
|
return `${padding}${text}${padding}`.padEnd(width)
|
|
14
7
|
}
|
|
15
8
|
|
|
16
|
-
|
|
17
|
-
* Starts the server string.
|
|
18
|
-
*
|
|
19
|
-
* @param {Server} config The server configuration.
|
|
20
|
-
* @returns {void} The server string.
|
|
21
|
-
*/
|
|
22
|
-
function startServer(config: Server, options?: Options): void {
|
|
9
|
+
export default function startServer(config: Server, options?: Options): void {
|
|
23
10
|
const { hostname, port, protocol } = config
|
|
24
11
|
const showBanner = options?.config?.showBanner ?? true
|
|
25
12
|
|
|
@@ -29,19 +16,18 @@ function startServer(config: Server, options?: Options): void {
|
|
|
29
16
|
const message = `🦊 Elysia is running at ${protocol}://${hostname}:${port}`
|
|
30
17
|
const boxWidth = Math.max(title.length, message.length) + 4
|
|
31
18
|
const border = '─'.repeat(boxWidth)
|
|
19
|
+
const emptyLine = createBoxText('', boxWidth)
|
|
32
20
|
|
|
33
21
|
console.log(`
|
|
34
22
|
┌${border}┐
|
|
35
|
-
│${
|
|
23
|
+
│${emptyLine}│
|
|
36
24
|
│${createBoxText(title, boxWidth)}│
|
|
37
|
-
│${
|
|
25
|
+
│${emptyLine}│
|
|
38
26
|
│${createBoxText(message, boxWidth)}│
|
|
39
|
-
│${
|
|
27
|
+
│${emptyLine}│
|
|
40
28
|
└${border}┘
|
|
41
29
|
`)
|
|
42
30
|
} else {
|
|
43
31
|
console.log(`🦊 Elysia is running at ${protocol}://${hostname}:${port}`)
|
|
44
32
|
}
|
|
45
33
|
}
|
|
46
|
-
|
|
47
|
-
export default startServer
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { buildLogMessage } from '../core/buildLogMessage'
|
|
2
|
+
import { LogData, LogLevel, Options, RequestInfo, StoreData } from '../types'
|
|
3
|
+
|
|
4
|
+
export async function logToTransports(
|
|
5
|
+
level: LogLevel,
|
|
6
|
+
request: RequestInfo,
|
|
7
|
+
data: LogData,
|
|
8
|
+
store: StoreData,
|
|
9
|
+
options?: Options
|
|
10
|
+
): Promise<void> {
|
|
11
|
+
if (!options?.config?.transports || options.config.transports.length === 0) {
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const message = buildLogMessage(level, request, data, store, options, false)
|
|
16
|
+
|
|
17
|
+
const promises = options.config.transports.map(transport =>
|
|
18
|
+
transport.log(level, message, { request, data, store })
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
await Promise.all(promises)
|
|
22
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { dirname } from 'path'
|
|
3
|
+
|
|
4
|
+
import { buildLogMessage } from '../core/buildLogMessage'
|
|
5
|
+
import { LogData, LogLevel, Options, RequestInfo, StoreData } from '../types'
|
|
6
|
+
|
|
7
|
+
const dirCache = new Set<string>()
|
|
8
|
+
|
|
9
|
+
async function ensureDirectoryExists(filePath: string): Promise<void> {
|
|
10
|
+
const dir = dirname(filePath)
|
|
11
|
+
if (!dirCache.has(dir)) {
|
|
12
|
+
await fs.mkdir(dir, { recursive: true })
|
|
13
|
+
dirCache.add(dir)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function logToFile(
|
|
18
|
+
filePath: string,
|
|
19
|
+
level: LogLevel,
|
|
20
|
+
request: RequestInfo,
|
|
21
|
+
data: LogData,
|
|
22
|
+
store: StoreData,
|
|
23
|
+
options?: Options
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
await ensureDirectoryExists(filePath)
|
|
26
|
+
const logMessage =
|
|
27
|
+
buildLogMessage(level, request, data, store, options, false) + '\n'
|
|
28
|
+
await fs.appendFile(filePath, logMessage, { flag: 'a' })
|
|
29
|
+
}
|
package/src/transports/index.ts
CHANGED
|
@@ -1,22 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export async function logToTransports(
|
|
5
|
-
level: LogLevel,
|
|
6
|
-
request: RequestInfo,
|
|
7
|
-
data: LogData,
|
|
8
|
-
store: StoreData,
|
|
9
|
-
options?: Options
|
|
10
|
-
): Promise<void> {
|
|
11
|
-
if (!options?.config?.transports || options.config.transports.length === 0) {
|
|
12
|
-
return
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const message = buildLogMessage(level, request, data, store, options, false)
|
|
16
|
-
|
|
17
|
-
const promises = options.config.transports.map(transport =>
|
|
18
|
-
transport.log(level, message, { request, data, store })
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
await Promise.all(promises)
|
|
22
|
-
}
|
|
1
|
+
export { logToTransports } from './console'
|
|
2
|
+
export { logToFile } from './file'
|
|
@@ -1,41 +1,58 @@
|
|
|
1
|
-
interface RequestInfo {
|
|
1
|
+
export interface RequestInfo {
|
|
2
2
|
headers: { get: (key: string) => string | null }
|
|
3
3
|
method: string
|
|
4
4
|
url: string
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
interface Server {
|
|
7
|
+
export interface Server {
|
|
8
8
|
hostname?: string
|
|
9
9
|
port?: number
|
|
10
10
|
protocol?: string
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
interface ColorMap {
|
|
13
|
+
export interface ColorMap {
|
|
14
14
|
[key: string]: (str: string) => string
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
type LogLevel = 'INFO' | 'WARNING' | 'ERROR' | string
|
|
17
|
+
export type LogLevel = 'INFO' | 'WARNING' | 'ERROR' | string
|
|
18
18
|
|
|
19
|
-
interface LogData {
|
|
19
|
+
export interface LogData {
|
|
20
20
|
status?: number
|
|
21
21
|
message?: string
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
interface Logger {
|
|
24
|
+
export interface Logger {
|
|
25
25
|
log(
|
|
26
26
|
level: LogLevel,
|
|
27
27
|
request: RequestInfo,
|
|
28
28
|
data: LogData,
|
|
29
29
|
store: StoreData
|
|
30
30
|
): void
|
|
31
|
+
handleHttpError(
|
|
32
|
+
request: RequestInfo,
|
|
33
|
+
error: HttpError,
|
|
34
|
+
store: StoreData
|
|
35
|
+
): void
|
|
31
36
|
customLogFormat?: string
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
interface
|
|
39
|
+
export interface LogComponents {
|
|
40
|
+
now: string
|
|
41
|
+
epoch: string
|
|
42
|
+
level: string
|
|
43
|
+
duration: string
|
|
44
|
+
method: string
|
|
45
|
+
pathname: string | undefined
|
|
46
|
+
status: string
|
|
47
|
+
message: string
|
|
48
|
+
ip: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface StoreData {
|
|
35
52
|
beforeTime: bigint
|
|
36
53
|
}
|
|
37
54
|
|
|
38
|
-
class HttpError extends Error {
|
|
55
|
+
export class HttpError extends Error {
|
|
39
56
|
status: number
|
|
40
57
|
|
|
41
58
|
constructor(status: number, message: string) {
|
|
@@ -44,7 +61,7 @@ class HttpError extends Error {
|
|
|
44
61
|
}
|
|
45
62
|
}
|
|
46
63
|
|
|
47
|
-
interface TransportFunction {
|
|
64
|
+
export interface TransportFunction {
|
|
48
65
|
(
|
|
49
66
|
level: LogLevel,
|
|
50
67
|
message: string,
|
|
@@ -56,11 +73,11 @@ interface TransportFunction {
|
|
|
56
73
|
): Promise<void> | void
|
|
57
74
|
}
|
|
58
75
|
|
|
59
|
-
interface Transport {
|
|
76
|
+
export interface Transport {
|
|
60
77
|
log: TransportFunction
|
|
61
78
|
}
|
|
62
79
|
|
|
63
|
-
interface Options {
|
|
80
|
+
export interface Options {
|
|
64
81
|
config?: {
|
|
65
82
|
customLogFormat?: string
|
|
66
83
|
logFilePath?: string
|
|
@@ -70,21 +87,8 @@ interface Options {
|
|
|
70
87
|
status?: number | number[]
|
|
71
88
|
} | null
|
|
72
89
|
ip?: boolean
|
|
90
|
+
useColors?: boolean
|
|
73
91
|
showBanner?: boolean
|
|
74
92
|
transports?: Transport[]
|
|
75
93
|
}
|
|
76
94
|
}
|
|
77
|
-
|
|
78
|
-
export {
|
|
79
|
-
ColorMap,
|
|
80
|
-
HttpError,
|
|
81
|
-
LogData,
|
|
82
|
-
Logger,
|
|
83
|
-
LogLevel,
|
|
84
|
-
Options,
|
|
85
|
-
RequestInfo,
|
|
86
|
-
Server,
|
|
87
|
-
StoreData,
|
|
88
|
-
Transport,
|
|
89
|
-
TransportFunction
|
|
90
|
-
}
|
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
-
import { ColorMap } from '
|
|
3
|
+
import { ColorMap } from '../types'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
* The color map for the log levels.
|
|
7
|
-
*
|
|
8
|
-
* @type {ColorMap}
|
|
9
|
-
*/
|
|
10
|
-
const LogLevelColorMap: ColorMap = {
|
|
5
|
+
export const LogLevelColorMap: ColorMap = {
|
|
11
6
|
INFO: chalk.bgGreen.black,
|
|
12
7
|
WARNING: chalk.bgYellow.black,
|
|
13
8
|
ERROR: chalk.bgRed.black
|
|
14
9
|
}
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
* The color map for the HTTP methods.
|
|
18
|
-
*
|
|
19
|
-
* @type {ColorMap}
|
|
20
|
-
*/
|
|
21
|
-
const HttpMethodColorMap: ColorMap = {
|
|
11
|
+
export const HttpMethodColorMap: ColorMap = {
|
|
22
12
|
GET: chalk.green,
|
|
23
13
|
POST: chalk.yellow,
|
|
24
14
|
PUT: chalk.blue,
|
|
@@ -27,5 +17,3 @@ const HttpMethodColorMap: ColorMap = {
|
|
|
27
17
|
HEAD: chalk.cyan,
|
|
28
18
|
OPTIONS: chalk.magenta
|
|
29
19
|
}
|
|
30
|
-
|
|
31
|
-
export { HttpMethodColorMap, LogLevelColorMap }
|
package/src/utils/duration.ts
CHANGED
|
@@ -1,45 +1,27 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*/
|
|
10
|
-
function durationString(beforeTime: bigint, useColors: boolean): string {
|
|
11
|
-
const currentTime = process.hrtime.bigint()
|
|
12
|
-
const nanoseconds = Number(currentTime - beforeTime)
|
|
3
|
+
const timeUnits = [
|
|
4
|
+
{ unit: 's', threshold: 1e9, decimalPlaces: 2 },
|
|
5
|
+
{ unit: 'ms', threshold: 1e6, decimalPlaces: 0 },
|
|
6
|
+
{ unit: 'µs', threshold: 1e3, decimalPlaces: 0 },
|
|
7
|
+
{ unit: 'ns', threshold: 1, decimalPlaces: 0 }
|
|
8
|
+
]
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
export default function durationString(
|
|
11
|
+
beforeTime: bigint,
|
|
12
|
+
useColors: boolean
|
|
13
|
+
): string {
|
|
14
|
+
const nanoseconds = Number(process.hrtime.bigint() - beforeTime)
|
|
19
15
|
|
|
20
16
|
for (const { unit, threshold, decimalPlaces } of timeUnits) {
|
|
21
17
|
if (nanoseconds >= threshold) {
|
|
22
18
|
const value = (nanoseconds / threshold).toFixed(decimalPlaces)
|
|
23
|
-
|
|
19
|
+
const timeStr = `${value}${unit}`.padStart(8).padEnd(16)
|
|
20
|
+
return useColors ? chalk.gray(timeStr) : timeStr
|
|
24
21
|
}
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
return formatTime(nanoseconds.toString(), 'ns', useColors)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Formats the time value with the given unit and applies chalk styling.
|
|
32
|
-
*
|
|
33
|
-
* @param {string} value - The time value.
|
|
34
|
-
* @param {string} unit - The time unit.
|
|
35
|
-
* @param {boolean} useColors - Whether to apply colors to the output.
|
|
36
|
-
* @returns {string} Styled time string.
|
|
37
|
-
*/
|
|
38
|
-
function formatTime(value: string, unit: string, useColors: boolean): string {
|
|
39
|
-
const timeStr = `${value}${unit}`
|
|
40
24
|
return useColors
|
|
41
|
-
? chalk.gray(
|
|
42
|
-
:
|
|
25
|
+
? chalk.gray('0ns'.padStart(8).padEnd(16))
|
|
26
|
+
: '0ns'.padStart(8).padEnd(16)
|
|
43
27
|
}
|
|
44
|
-
|
|
45
|
-
export default durationString
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { HttpMethodColorMap, LogLevelColorMap } from './colorMapping'
|
|
2
|
+
export { default as durationString } from './duration'
|
|
3
|
+
export { default as logString } from './log'
|
|
4
|
+
export { default as methodString } from './method'
|
|
5
|
+
export { default as pathString } from './path'
|
|
6
|
+
export { default as statusString } from './status'
|
package/src/utils/log.ts
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
import { LogLevel } from '
|
|
2
|
-
|
|
1
|
+
import { LogLevel } from '../types'
|
|
3
2
|
import { LogLevelColorMap } from './colorMapping'
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
* Converts the log level to a string.
|
|
7
|
-
*
|
|
8
|
-
* @param {LogLevel} level The log level.
|
|
9
|
-
* @param {boolean} useColors - Whether to apply colors to the output.
|
|
10
|
-
* @returns {string} The log level as a string.
|
|
11
|
-
*/
|
|
12
|
-
function logString(level: LogLevel, useColors: boolean): string {
|
|
4
|
+
export default function logString(level: LogLevel, useColors: boolean): string {
|
|
13
5
|
const levelStr = level.toUpperCase()
|
|
14
6
|
return useColors
|
|
15
7
|
? LogLevelColorMap[levelStr]?.(levelStr.padEnd(7)) || levelStr
|
|
16
8
|
: levelStr.padEnd(7)
|
|
17
9
|
}
|
|
18
|
-
|
|
19
|
-
export default logString
|
package/src/utils/method.ts
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import { HttpMethodColorMap } from './colorMapping'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* @param {boolean} useColors - Whether to apply colors to the output.
|
|
8
|
-
* @returns {string} A string representing the method.
|
|
9
|
-
*/
|
|
10
|
-
function methodString(method: string, useColors: boolean): string {
|
|
3
|
+
export default function methodString(
|
|
4
|
+
method: string,
|
|
5
|
+
useColors: boolean
|
|
6
|
+
): string {
|
|
11
7
|
const colorFunction = HttpMethodColorMap[method]
|
|
12
8
|
return useColors && colorFunction
|
|
13
9
|
? colorFunction(method.padEnd(7))
|
|
14
10
|
: method.padEnd(7)
|
|
15
11
|
}
|
|
16
|
-
|
|
17
|
-
export default methodString
|
package/src/utils/path.ts
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import { RequestInfo } from '
|
|
1
|
+
import { RequestInfo } from '../types'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* @param {RequestInfo} requestInfo The request info.
|
|
7
|
-
* @returns {string | undefined} The path string.
|
|
8
|
-
*/
|
|
9
|
-
function pathString(requestInfo: RequestInfo): string | undefined {
|
|
3
|
+
export default function pathString(
|
|
4
|
+
requestInfo: RequestInfo
|
|
5
|
+
): string | undefined {
|
|
10
6
|
try {
|
|
11
7
|
return new URL(requestInfo.url).pathname
|
|
12
8
|
} catch {
|
|
13
9
|
return undefined
|
|
14
10
|
}
|
|
15
11
|
}
|
|
16
|
-
|
|
17
|
-
export default pathString
|
package/src/utils/status.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* @param {boolean} useColors - Whether to apply colors to the output.
|
|
8
|
-
* @returns {string} The status code as a string.
|
|
9
|
-
*/
|
|
10
|
-
function statusString(status: number, useColors: boolean): string {
|
|
3
|
+
export default function statusString(
|
|
4
|
+
status: number,
|
|
5
|
+
useColors: boolean
|
|
6
|
+
): string {
|
|
11
7
|
const color =
|
|
12
8
|
status >= 500
|
|
13
9
|
? 'red'
|
|
@@ -20,5 +16,3 @@ function statusString(status: number, useColors: boolean): string {
|
|
|
20
16
|
: 'white'
|
|
21
17
|
return useColors ? chalk[color](status.toString()) : status.toString()
|
|
22
18
|
}
|
|
23
|
-
|
|
24
|
-
export default statusString
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { buildLogMessage } from '../../src/core/buildLogMessage'
|
|
4
|
+
import { LogData, LogLevel, Options, StoreData } from '../../src/types'
|
|
5
|
+
import { createMockRequest } from '../helpers'
|
|
6
|
+
|
|
7
|
+
test('buildLogMessage', () => {
|
|
8
|
+
const level: LogLevel = 'INFO'
|
|
9
|
+
const request = createMockRequest()
|
|
10
|
+
const data: LogData = { status: 200, message: 'Test message' }
|
|
11
|
+
const store: StoreData = { beforeTime: BigInt(0) }
|
|
12
|
+
const options: Options = {
|
|
13
|
+
config: {
|
|
14
|
+
ip: true,
|
|
15
|
+
customLogFormat: '{level} {message} {ip}'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const message = buildLogMessage(level, request, data, store, options, false)
|
|
20
|
+
expect(message).toContain('INFO')
|
|
21
|
+
expect(message).toContain('Test message')
|
|
22
|
+
expect(message).toContain('127.0.0.1')
|
|
23
|
+
|
|
24
|
+
const colorMessage = buildLogMessage(
|
|
25
|
+
level,
|
|
26
|
+
request,
|
|
27
|
+
data,
|
|
28
|
+
store,
|
|
29
|
+
options,
|
|
30
|
+
true
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
expect(colorMessage).toContain('INFO')
|
|
34
|
+
expect(colorMessage).toContain('Test message')
|
|
35
|
+
expect(colorMessage).toContain('127.0.0.1')
|
|
36
|
+
|
|
37
|
+
const hasAnsiCodes = /\\x1B\[[0-9;]*m/.test(colorMessage)
|
|
38
|
+
if (hasAnsiCodes) {
|
|
39
|
+
expect(colorMessage).not.toBe(message)
|
|
40
|
+
} else {
|
|
41
|
+
console.warn(
|
|
42
|
+
'No ANSI color codes detected. Colors might be disabled in this environment.'
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { filterLog } from '../../src/core/filter'
|
|
4
|
+
import { Options } from '../../src/types'
|
|
5
|
+
|
|
6
|
+
test('filterLog', () => {
|
|
7
|
+
const options: Options = {
|
|
8
|
+
config: {
|
|
9
|
+
logFilter: {
|
|
10
|
+
level: 'ERROR',
|
|
11
|
+
method: 'POST',
|
|
12
|
+
status: 500
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
expect(filterLog('ERROR', 500, 'POST', options)).toBe(true)
|
|
18
|
+
expect(filterLog('INFO', 200, 'GET', options)).toBe(false)
|
|
19
|
+
expect(filterLog('WARNING', 400, 'PUT', options)).toBe(false)
|
|
20
|
+
|
|
21
|
+
// Test with array filters
|
|
22
|
+
const arrayOptions: Options = {
|
|
23
|
+
config: {
|
|
24
|
+
logFilter: {
|
|
25
|
+
level: ['ERROR', 'WARNING'],
|
|
26
|
+
method: ['POST', 'PUT'],
|
|
27
|
+
status: [500, 400]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
expect(filterLog('ERROR', 500, 'POST', arrayOptions)).toBe(true)
|
|
33
|
+
expect(filterLog('WARNING', 400, 'PUT', arrayOptions)).toBe(true)
|
|
34
|
+
expect(filterLog('INFO', 200, 'GET', arrayOptions)).toBe(false)
|
|
35
|
+
})
|
package/tests/helpers.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { mock } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { RequestInfo } from '../src/types'
|
|
4
|
+
|
|
5
|
+
export function createMockRequest(
|
|
6
|
+
method: string = 'GET',
|
|
7
|
+
url: string = 'http://localhost:3000/'
|
|
8
|
+
): RequestInfo {
|
|
9
|
+
return {
|
|
10
|
+
headers: {
|
|
11
|
+
get: (key: string) => (key === 'x-forwarded-for' ? '127.0.0.1' : null)
|
|
12
|
+
},
|
|
13
|
+
method,
|
|
14
|
+
url
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const mockConsoleLog = mock(() => {})
|
|
19
|
+
export const mockConsoleError = mock(() => {})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { expect, mock, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { logToTransports } from '../../src/transports'
|
|
4
|
+
import { LogData, LogLevel, Options, StoreData } from '../../src/types'
|
|
5
|
+
import { createMockRequest } from '../helpers'
|
|
6
|
+
|
|
7
|
+
test('logToTransports', async () => {
|
|
8
|
+
const level: LogLevel = 'INFO'
|
|
9
|
+
const request = createMockRequest()
|
|
10
|
+
const data: LogData = { status: 200, message: 'Test message' }
|
|
11
|
+
const store: StoreData = { beforeTime: BigInt(0) }
|
|
12
|
+
|
|
13
|
+
const mockTransport = {
|
|
14
|
+
log: mock(() => Promise.resolve())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const options: Options = {
|
|
18
|
+
config: {
|
|
19
|
+
transports: [mockTransport]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await logToTransports(level, request, data, store, options)
|
|
24
|
+
expect(mockTransport.log).toHaveBeenCalled()
|
|
25
|
+
|
|
26
|
+
// Test with no transports
|
|
27
|
+
await logToTransports(level, request, data, store, {})
|
|
28
|
+
expect(mockTransport.log).toHaveBeenCalledTimes(1) // Should not be called again
|
|
29
|
+
})
|
package/tsconfig.json
CHANGED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
|
|
3
|
-
import { LogData, LogLevel, Options, RequestInfo, StoreData } from '~/types'
|
|
4
|
-
import durationString from '~/utils/duration'
|
|
5
|
-
import logString from '~/utils/log'
|
|
6
|
-
import methodString from '~/utils/method'
|
|
7
|
-
import pathString from '~/utils/path'
|
|
8
|
-
import statusString from '~/utils/status'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Builds a log message.
|
|
12
|
-
*
|
|
13
|
-
* @param {LogLevel} level The log level.
|
|
14
|
-
* @param {RequestInfo} request The request information.
|
|
15
|
-
* @param {LogData} data The log data.
|
|
16
|
-
* @param {StoreData} store The store data.
|
|
17
|
-
* @param {Options} options The logger options.
|
|
18
|
-
* @param {boolean} useColors Whether to apply colors to the log message.
|
|
19
|
-
* @returns {string} The formatted log message.
|
|
20
|
-
*/
|
|
21
|
-
export function buildLogMessage(
|
|
22
|
-
level: LogLevel,
|
|
23
|
-
request: RequestInfo,
|
|
24
|
-
data: LogData,
|
|
25
|
-
store: StoreData,
|
|
26
|
-
options?: Options,
|
|
27
|
-
useColors: boolean = true
|
|
28
|
-
): string {
|
|
29
|
-
const now = new Date()
|
|
30
|
-
const nowStr = useColors
|
|
31
|
-
? chalk.bgYellow(chalk.black(now.toLocaleString()))
|
|
32
|
-
: now.toLocaleString()
|
|
33
|
-
const epochStr = Math.floor(now.getTime() / 1000).toString()
|
|
34
|
-
const levelStr = logString(level, useColors)
|
|
35
|
-
const durationStr = durationString(store.beforeTime, useColors)
|
|
36
|
-
const methodStr = methodString(request.method, useColors)
|
|
37
|
-
const pathnameStr = pathString(request)
|
|
38
|
-
const statusStr = statusString(data.status || 200, useColors)
|
|
39
|
-
const messageStr = data.message || ''
|
|
40
|
-
const ipStr =
|
|
41
|
-
options?.config?.ip && request.headers.get('x-forwarded-for')
|
|
42
|
-
? `IP: ${request.headers.get('x-forwarded-for')}`
|
|
43
|
-
: ''
|
|
44
|
-
|
|
45
|
-
const logFormat =
|
|
46
|
-
options?.config?.customLogFormat ||
|
|
47
|
-
'🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}'
|
|
48
|
-
|
|
49
|
-
return logFormat
|
|
50
|
-
.replace('{now}', nowStr)
|
|
51
|
-
.replace('{epoch}', epochStr)
|
|
52
|
-
.replace('{level}', levelStr)
|
|
53
|
-
.replace('{duration}', durationStr)
|
|
54
|
-
.replace('{method}', methodStr)
|
|
55
|
-
.replace('{pathname}', pathnameStr || '')
|
|
56
|
-
.replace('{status}', statusStr)
|
|
57
|
-
.replace('{message}', messageStr)
|
|
58
|
-
.replace('{ip}', ipStr || '')
|
|
59
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { logToTransports } from '~/transports'
|
|
2
|
-
import {
|
|
3
|
-
LogData,
|
|
4
|
-
Logger,
|
|
5
|
-
LogLevel,
|
|
6
|
-
Options,
|
|
7
|
-
RequestInfo,
|
|
8
|
-
StoreData
|
|
9
|
-
} from '~/types'
|
|
10
|
-
|
|
11
|
-
import { buildLogMessage } from './buildLogMessage'
|
|
12
|
-
import { filterLog } from './filter'
|
|
13
|
-
import { logToFile } from './logToFile'
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Logs a message to the console and optionally to a file.
|
|
17
|
-
*
|
|
18
|
-
* @param {LogLevel} level The log level.
|
|
19
|
-
* @param {RequestInfo} request The request information.
|
|
20
|
-
* @param {LogData} data The log data.
|
|
21
|
-
* @param {StoreData} store The store data.
|
|
22
|
-
* @param {Options} options The logger options.
|
|
23
|
-
*/
|
|
24
|
-
async function log(
|
|
25
|
-
level: LogLevel,
|
|
26
|
-
request: RequestInfo,
|
|
27
|
-
data: LogData,
|
|
28
|
-
store: StoreData,
|
|
29
|
-
options?: Options
|
|
30
|
-
): Promise<void> {
|
|
31
|
-
if (!filterLog(level, data.status || 200, request.method, options)) return
|
|
32
|
-
|
|
33
|
-
const logMessage = buildLogMessage(level, request, data, store, options, true)
|
|
34
|
-
console.log(logMessage)
|
|
35
|
-
|
|
36
|
-
if (options?.config?.logFilePath) {
|
|
37
|
-
await logToFile(
|
|
38
|
-
options.config.logFilePath,
|
|
39
|
-
level,
|
|
40
|
-
request,
|
|
41
|
-
data,
|
|
42
|
-
store,
|
|
43
|
-
options
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
await logToTransports(level, request, data, store, options)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Creates a logger instance.
|
|
52
|
-
*
|
|
53
|
-
* @param {Options} options The logger options.
|
|
54
|
-
* @returns {Logger} The logger instance.
|
|
55
|
-
*/
|
|
56
|
-
export function createLogger(options?: Options): Logger {
|
|
57
|
-
return {
|
|
58
|
-
log: async (level, request, data, store) =>
|
|
59
|
-
log(level, request, data, store, options),
|
|
60
|
-
customLogFormat: options?.config?.customLogFormat
|
|
61
|
-
}
|
|
62
|
-
}
|
package/src/logger/filter.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { LogLevel, Options } from '~/types'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Filters log messages.
|
|
5
|
-
*
|
|
6
|
-
* @param {LogLevel} logLevel The log level.
|
|
7
|
-
* @param {number} status The status code.
|
|
8
|
-
* @param {string} method The method.
|
|
9
|
-
* @param {Options} options The options.
|
|
10
|
-
* @returns {boolean} `true` if the log message should be logged, otherwise `false`.
|
|
11
|
-
*/
|
|
12
|
-
export function filterLog(
|
|
13
|
-
logLevel: LogLevel,
|
|
14
|
-
status: number,
|
|
15
|
-
method: string,
|
|
16
|
-
options?: Options
|
|
17
|
-
): boolean {
|
|
18
|
-
const filter = options?.config?.logFilter
|
|
19
|
-
if (!filter) return true
|
|
20
|
-
|
|
21
|
-
const checkFilter = (filterValue: any, value: any) =>
|
|
22
|
-
Array.isArray(filterValue)
|
|
23
|
-
? filterValue.includes(value)
|
|
24
|
-
: filterValue === value
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
(!filter.level || checkFilter(filter.level, logLevel)) &&
|
|
28
|
-
(!filter.status || checkFilter(filter.status, status)) &&
|
|
29
|
-
(!filter.method || checkFilter(filter.method, method))
|
|
30
|
-
)
|
|
31
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { HttpError, Options, RequestInfo, StoreData } from '~/types'
|
|
2
|
-
|
|
3
|
-
import { buildLogMessage } from './buildLogMessage'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Handles an HTTP error and logs it.
|
|
7
|
-
*
|
|
8
|
-
* @param {RequestInfo} request The request information.
|
|
9
|
-
* @param {HttpError} error The HTTP error.
|
|
10
|
-
* @param {StoreData} store The store data.
|
|
11
|
-
* @param {Options} options The logger options.
|
|
12
|
-
*/
|
|
13
|
-
export async function handleHttpError(
|
|
14
|
-
request: RequestInfo,
|
|
15
|
-
error: HttpError,
|
|
16
|
-
store: StoreData,
|
|
17
|
-
options?: Options
|
|
18
|
-
): Promise<void> {
|
|
19
|
-
const statusCode = error.status || 500
|
|
20
|
-
const logMessage = buildLogMessage(
|
|
21
|
-
'ERROR',
|
|
22
|
-
request,
|
|
23
|
-
{ status: statusCode },
|
|
24
|
-
store,
|
|
25
|
-
options
|
|
26
|
-
)
|
|
27
|
-
console.error(logMessage)
|
|
28
|
-
}
|
package/src/logger/logToFile.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs'
|
|
2
|
-
import { dirname } from 'path'
|
|
3
|
-
|
|
4
|
-
import { LogData, LogLevel, Options, RequestInfo, StoreData } from '~/types'
|
|
5
|
-
|
|
6
|
-
import { buildLogMessage } from './buildLogMessage'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Ensures that the directory exists. If not, it creates the directory.
|
|
10
|
-
*
|
|
11
|
-
* @param {string} filePath The path to the log file.
|
|
12
|
-
*/
|
|
13
|
-
async function ensureDirectoryExists(filePath: string): Promise<void> {
|
|
14
|
-
const dir = dirname(filePath)
|
|
15
|
-
await fs.mkdir(dir, { recursive: true })
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Logs a message to a file.
|
|
20
|
-
*
|
|
21
|
-
* @param {string} filePath The path to the log file.
|
|
22
|
-
* @param {LogLevel} level The log level.
|
|
23
|
-
* @param {RequestInfo} request The request information.
|
|
24
|
-
* @param {LogData} data The log data.
|
|
25
|
-
* @param {StoreData} store The store data.
|
|
26
|
-
* @param {Options} options The logger options.
|
|
27
|
-
*/
|
|
28
|
-
export async function logToFile(
|
|
29
|
-
filePath: string,
|
|
30
|
-
level: LogLevel,
|
|
31
|
-
request: RequestInfo,
|
|
32
|
-
data: LogData,
|
|
33
|
-
store: StoreData,
|
|
34
|
-
options?: Options
|
|
35
|
-
): Promise<void> {
|
|
36
|
-
await ensureDirectoryExists(filePath)
|
|
37
|
-
const logMessage =
|
|
38
|
-
buildLogMessage(level, request, data, store, options, false) + '\n'
|
|
39
|
-
await fs.appendFile(filePath, logMessage)
|
|
40
|
-
}
|
|
File without changes
|