Skip to content

Commit

Permalink
refactor(*): update logger functionally to support configurable IP lo…
Browse files Browse the repository at this point in the history
…gging

1. modified the logger functions in logger.ts to accept an options parameter, allowing for the
configuration of IP logging , 2. move all files in types folder to types.ts
  • Loading branch information
PunGrumpy committed Apr 7, 2024
1 parent ed51ced commit d35ac3e
Show file tree
Hide file tree
Showing 21 changed files with 96 additions and 201 deletions.
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Elysia from 'elysia'
import { createLogger, handleHttpError } from './logger'
import startString from './utils/start'
import { Server } from 'bun'
import { Options } from './options'
import { Options } from './types'

/**
* Creates a logger.
Expand All @@ -20,7 +20,7 @@ import { Options } from './options'
* @returns {Elysia} The logger.
*/
export const logger = (options?: Options): Elysia => {
const log = createLogger()
const log = createLogger(options)

const elysia = new Elysia({
name: 'Logixlysia'
Expand Down
91 changes: 23 additions & 68 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,26 @@ import methodString from './utils/method'
import logString from './utils/log'
import pathString from './utils/path'
import statusString from './utils/status'
import { RequestInfo } from './types/RequestInfo'
import { LogData, LogLevel, Logger } from './types/Logger'
import { StoreData } from './types/StoreData'
import { HttpError } from './types/HttpError'
import { HttpError, RequestInfo } from './types'
import { LogLevel, LogData, Logger, StoreData, Options } from './types'

/**
* Asynchronously logs a message constructed from various log components.
*
* @async
* @param {LogLevel} level - The log level.
* @param {RequestInfo} request - The request information.
* @param {LogData} data - The log data.
* @param {StoreData} store - The store data.
*
* @returns {Promise<void>}
*/
async function log(
level: LogLevel,
request: RequestInfo,
data: LogData,
store: StoreData
store: StoreData,
options?: Options
): Promise<void> {
const logMessage = buildLogMessage(level, request, data, store)
try {
await writeToLogAsync(logMessage)
} catch (error) {
console.error('Error logging message:', error)
}
const logMessage = buildLogMessage(level, request, data, store, options)
console.log(logMessage)
}

/**
* Builds the log message string from given parameters.
*
* @param {LogLevel} level - The log level.
* @param {RequestInfo} request - The request information.
* @param {LogData} data - The log data.
* @param {StoreData} store - The store data.
*
* @returns {string} - The constructed log message.
*/
function buildLogMessage(
level: LogLevel,
request: RequestInfo,
data: LogData,
store: StoreData
store: StoreData,
options?: Options
): string {
const nowStr = chalk.bgYellow(chalk.black(new Date().toLocaleString()))
const levelStr = logString(level)
Expand All @@ -58,56 +33,36 @@ function buildLogMessage(
const statusStr = statusString(data.status || 200)
const messageStr = data.message || ''

return `🦊 ${nowStr} ${levelStr} ${durationStr} ${methodStr} ${pathnameStr} ${statusStr} ${messageStr}`
}
let logMessage = `🦊 ${nowStr} ${levelStr} ${durationStr} ${methodStr} ${pathnameStr} ${statusStr} ${messageStr}`

/**
* Writes a log message to the console asynchronously.
*
* @async
* @param {string} message - The message to log.
*
* @returns {Promise<void>}
* @throws {Error} - If the timeout is reached.
*/
function writeToLogAsync(message: string): Promise<void> {
return new Promise((resolve, reject) => {
console.log(message)
resolve()
if (options?.ip) {
const forwardedFor = request.headers.get('x-forwarded-for')
if (forwardedFor) {
logMessage += ` IP: ${forwardedFor}`
}
}

setTimeout(() => {
reject(new Error('Timed out while writing to log.'))
})
})
return logMessage
}

/**
* Creates a logger instance with an asynchronous log method.
*
* @returns {Logger} - The logger instance.
*/
export const createLogger = (): Logger => ({
log: (level, request, data, store) => log(level, request, data, store)
export const createLogger = (options?: Options): Logger => ({
log: (level, request, data, store) =>
log(level, request, data, store, options)
})

/**
* Handle HTTP errors and log them with the appropriate status code.
*
* @param {RequestInfo} request - The request information.
* @param {Error} error - The error object.
* @param {StoreData} store - The store data.
*/
export const handleHttpError = (
request: RequestInfo,
error: Error,
store: StoreData
store: StoreData,
options?: Options
): void => {
const statusCode = error instanceof HttpError ? error.status : 500
const logMessage = buildLogMessage(
'ERROR',
request,
{ status: statusCode },
store
store,
options
)
console.error(logMessage)
}
11 changes: 0 additions & 11 deletions src/options.ts

This file was deleted.

60 changes: 60 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
interface RequestInfo {
headers: { get: (key: string) => any }
method: string
url: string
}

interface Server {
hostname?: string
port?: number
protocol?: string
}

interface ColorMap {
[key: string]: (str: string) => string
}

type LogLevel = 'INFO' | 'WARNING' | 'ERROR' | string

interface LogData {
status?: number
message?: string
}

interface Logger {
log(
level: LogLevel,
request: RequestInfo,
data: LogData,
store: StoreData
): void
}

interface StoreData {
beforeTime: bigint
}

class HttpError extends Error {
status: number

constructor(status: number, message: string) {
super(message)
this.status = status
}
}

interface Options {
ip?: boolean
}

export {
RequestInfo,
Server,
ColorMap,
LogLevel,
LogData,
Logger,
StoreData,
HttpError,
Options
}
12 changes: 0 additions & 12 deletions src/types/ColorMap.ts

This file was deleted.

11 changes: 0 additions & 11 deletions src/types/HttpError.ts

This file was deleted.

40 changes: 0 additions & 40 deletions src/types/Logger.ts

This file was deleted.

16 changes: 0 additions & 16 deletions src/types/RequestInfo.ts

This file was deleted.

16 changes: 0 additions & 16 deletions src/types/Server.ts

This file was deleted.

12 changes: 0 additions & 12 deletions src/types/StoreData.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/utils/colorMapping.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import chalk from 'chalk'
import { ColorMap } from '~/types/ColorMap'
import { ColorMap } from '~/types'

/**
* The color map for the log levels.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/path.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RequestInfo } from '~/types/RequestInfo'
import { RequestInfo } from '~/types'

/**
* Returns the path string.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/start.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Server } from '~/types/Server'
import { Server } from '~/types'

/**
* Creates a box text.
Expand Down
2 changes: 1 addition & 1 deletion test/types/ColorMap.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'bun:test'
import { ColorMap } from '~/types/ColorMap'
import { ColorMap } from '~/types'

describe('Color Mapping Interface', () => {
it('Defines an object with string keys mapping to functions', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/types/HttpError.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'bun:test'
import { HttpError } from '~/types/HttpError'
import { HttpError } from '~/types'

describe('HttpError', () => {
it('Should create an instance with correct status and message', () => {
Expand Down
4 changes: 1 addition & 3 deletions test/types/Logger.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { beforeEach, describe, expect, it, jest } from 'bun:test'
import { LogData } from '~/types/Logger'
import { RequestInfo } from '~/types/RequestInfo'
import { StoreData } from '~/types/StoreData'
import { RequestInfo, LogData, StoreData } from '~/types'

interface Logger {
info(request: RequestInfo, data: LogData, store: StoreData): void
Expand Down
2 changes: 1 addition & 1 deletion test/types/RequestInfo.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'bun:test'
import { RequestInfo } from '~/types/RequestInfo'
import { RequestInfo } from '~/types'

describe('Request Infomation interface', () => {
it('Defines the RequestInfo interface correctly', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/types/Server.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'bun:test'
import { Server } from '~/types/Server'
import { Server } from '~/types'

describe('Server interface', () => {
it('Defines the Server interface correctly', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/types/StoreData.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'bun:test'
import { StoreData } from '~/types/StoreData'
import { StoreData } from '~/types'

describe('Store Data interface', () => {
it('Defines the StoreData interface correctly', () => {
Expand Down
Loading

0 comments on commit d35ac3e

Please sign in to comment.