Skip to content

Commit

Permalink
fix(logger): add api-logger with new router options
Browse files Browse the repository at this point in the history
  • Loading branch information
Howard86 committed Sep 9, 2022
1 parent 4b02cf1 commit d237e3c
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
58 changes: 58 additions & 0 deletions src/lib/api-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export interface ApiLogger {
debug: LogFunction;
info: LogFunction;
warn: LogFunction;
error: LogFunction;
}

type LogFunction = (message?: string) => void;

enum LoggerLevelMap {
debug,
info,
warn,
error,
}

export type LoggerLevel = keyof typeof LoggerLevelMap;

const DEFAULT_LOGGER_LEVEL: LoggerLevel =
process.env.NODE_ENV === 'development' ? 'info' : 'error';
const DEFAULT_CONTEXT = '[next-api-handler]';

export interface DefaultApiLoggerOption {
context?: string;
loggerLevel?: LoggerLevel;
}

export class DefaultApiLogger implements ApiLogger {
private readonly context: string;
private readonly level: number;
private readonly DEFAULT_MESSAGE = '';

constructor({
context = DEFAULT_CONTEXT,
loggerLevel = DEFAULT_LOGGER_LEVEL,
}: DefaultApiLoggerOption = {}) {
this.context = context;
this.level = LoggerLevelMap[loggerLevel];
}

debug = this.logMessage('debug');
info = this.logMessage('info');
warn = this.logMessage('warn');
error = this.logMessage('error');

private logMessage(level: LoggerLevel) {
if (this.level > LoggerLevelMap[level]) return this.emptyFunction;

return (message = this.DEFAULT_MESSAGE) => {
console[level](
`${this.context} ${level} ${new Date().toISOString()} ${message}`
);
};
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
private emptyFunction(_message?: string) {}
}
29 changes: 28 additions & 1 deletion src/lib/router-builder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';

import { ApiLogger, DefaultApiLogger } from './api-logger';
import { DEFAULT_MIDDLEWARE_ROUTER_METHOD } from './constants';
import { makeErrorHandler } from './error-handler';
import { ExpressLikeRouter } from './express-like-router';
Expand Down Expand Up @@ -41,19 +42,25 @@ import {
*/
export class RouterBuilder extends ExpressLikeRouter {
private readonly routerOptions = {} as Required<RouterBuilderOptions>;
private readonly logger: ApiLogger;

constructor(options: RouterBuilderOptions = {}) {
super();
this.applyErrorHandler(options);
this.logger = options.logger || new DefaultApiLogger(options.loggerOption);
}

public build(): NextApiHandler {
return async (req: NextApiRequest, res: NextApiResponse<ApiResponse>) => {
const initiatedTime = Date.now();
const routerMethod = (req.method || 'GET') as RouterMethod;

try {
const routerMethod = (req.method || 'GET') as RouterMethod;
const handler = this.routeHandlerMap[routerMethod];
this.logger.debug(`Initiated ${routerMethod} ${req.url}`);

if (!handler) {
this.logger.debug(`Missed handler on ${routerMethod} ${req.url}`);
res.setHeader('Allow', Object.keys(this.routeHandlerMap));
throw new MethodNotAllowedException(
`Method ${routerMethod} Not Allowed`
Expand All @@ -69,8 +76,16 @@ export class RouterBuilder extends ExpressLikeRouter {
const data = await handler(req as NextApiRequestWithMiddleware, res);

this.handleSendSuccessResponse(res, data);
this.logger.info(
`Successfully handled ${routerMethod} ${req.url} with ${
Date.now() - initiatedTime
}ms`
);
} catch (error) {
this.routerOptions.error(req, res, error as Error);
`Caught errors from ${routerMethod} ${req.url} with ${
Date.now() - initiatedTime
}ms`;
}
};
}
Expand Down Expand Up @@ -127,6 +142,12 @@ export class RouterBuilder extends ExpressLikeRouter {
): Promise<void>[] {
if (!Array.isArray(this.middlewareParallelListMap[method])) return [];

this.logger.debug(
`Resolved ${
this.middlewareParallelListMap[method]!.length
} ${method} middleware list`
);

return this.middlewareParallelListMap[method]!.map(async (middleware) => {
const middlewareValue = await Promise.resolve(middleware(req, res));

Expand All @@ -143,6 +164,12 @@ export class RouterBuilder extends ExpressLikeRouter {
): Promise<void> {
if (!Array.isArray(this.middlewareQueueMap[method])) return;

this.logger.debug(
`Resolved ${
this.middlewareQueueMap[method]!.length
} ${method} middleware queue`
);

for (const middleware of this.middlewareQueueMap[method]!) {
const middlewareValue = await Promise.resolve(middleware(req, res));

Expand Down
3 changes: 3 additions & 0 deletions src/lib/type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';

import { ApiLogger, DefaultApiLoggerOption } from './api-logger';
import { DEFAULT_MIDDLEWARE_ROUTER_METHOD } from './constants';
import { ApiErrorHandler } from './error-handler';

Expand Down Expand Up @@ -50,6 +51,8 @@ export type ErrorApiResponse = { success: false; message: string };
export type RouterBuilderOptions = Partial<{
error: ApiErrorHandler;
showMessage: boolean;
logger: ApiLogger;
loggerOption: DefaultApiLoggerOption;
}>;

export type InternalMiddlewareMap = Partial<
Expand Down

0 comments on commit d237e3c

Please sign in to comment.