Skip to content

Commit

Permalink
Logger works well
Browse files Browse the repository at this point in the history
  • Loading branch information
devbenho committed Jun 13, 2024
1 parent d098202 commit 68081b8
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 73 deletions.
6 changes: 6 additions & 0 deletions server/nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"exec": "node --inspect=0.0.0.0:9229 -r ts-node/register -r tsconfig-paths/register ./src/index.ts",
"ext": "ts,json,yml",
"ignore": ["dist", "node_modules"],
"watch": ["src"]
}
9 changes: 5 additions & 4 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@
"email": "Name",
"url": "url"
},

"version": "1.0.0",
"main": "index.js",
"scripts": {
"start:dev": "nodemon src/web/rest/app.ts",
"dev": "cross-env NODE_ENV=development nodemon",
"typeorm": "typeorm-ts-node-esm",
"typeorm:generate": "npm run typeorm -- migration:generate -n",
"db:seed": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed"
},
"keywords": [],
"license": "ISC",
"dependencies": {
"pino": "^8.19.0",
"pino-http": "^9.0.0",
"pino-pretty": "^10.3.1",
"@tsed/ajv": "^7.62.2",
"@tsed/common": "^7.62.2",
"@tsed/components-scan": "^7.69.4",
Expand Down Expand Up @@ -55,9 +59,6 @@
"method-override": "^3.0.0",
"node-emoji": "^2.1.3",
"nodemon": "^3.0.3",
"pino": "^8.19.0",
"pino-http": "^10.1.0",
"pino-pretty": "^10.3.1",
"read-pkg": "5.2.0",
"rxjs": "^7.8.1",
"sqlite3": "^5.1.7",
Expand Down
2 changes: 0 additions & 2 deletions server/src/application/post/create/create-post.usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { BaseUseCase, UseCase } from '@application/shared';
import { inject, injectable } from 'inversify';
import { TYPES } from '@infrastructure/shared/ioc/types';
import { PostRepository } from '@domain/entities/posts/post.repository';
import { LOGGER } from '@/web/rest/logger';
import { PostDetailsResponseDto } from '@contracts/dtos/posts/post-details.response';
import { log } from 'console';
import { CreatePostRequest } from './create-post.request';
Expand All @@ -22,7 +21,6 @@ class CreatePostUseCase extends BaseUseCase<
public async performOperation(
request: CreatePostRequest,
): Promise<PostDetailsResponseDto> {

const post = Post.create(
null,
request.title,
Expand Down
2 changes: 0 additions & 2 deletions server/src/contracts/dtos/posts/post-details.response.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { LOGGER } from '@/web/rest/logger';
import { Post } from '@domain/entities';
import { Nullable } from '@domain/shared/types';
import { UserResponseDto } from '@dtos/users';
import { log } from 'console';

export class PostDetailsResponseDto {
constructor(
Expand Down
35 changes: 35 additions & 0 deletions server/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'reflect-metadata';
import 'source-map-support/register';

import { PlatformExpress } from '@tsed/platform-express';
import * as emoji from 'node-emoji';

import { Logger } from '@domain/shared';
import { bootstrap } from '@infrastructure/shared';
import { Server } from '@web/rest/server';

const start = async (): Promise<void> => {
await bootstrap();

const platform = await PlatformExpress.bootstrap(Server, {
...(await Server.getConfiguration()),
});
await platform.listen();

process
.on('SIGINT', () => {
platform.stop();
Logger.info(`${emoji.get('zap')} Server gracefully shut down!`);
})
.on('unhandledRejection', error => {
Logger.error(
`${emoji.get('skull')} uncaughtException captured: ${error}`,
);
})
.on('uncaughtException', error => {
Logger.error(
`${emoji.get('skull')} uncaughtException captured: ${error}`,
);
});
};
start();
66 changes: 43 additions & 23 deletions server/src/infrastructure/shared/logger/pino-logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { getContext } from '@tsed/di';
import { NextFunction, Request, Response } from 'express';
import pino, { Level, Logger as PinoLoggerType, TransportTargetOptions } from 'pino';
import pino, {
Level,
Logger as PinoLoggerType,
TransportTargetOptions,
} from 'pino';
import pinoHttp from 'pino-http';

import { LoggerDomainService } from '@domain/shared/services';
Expand All @@ -10,7 +14,7 @@ enum LogLevel {
DEBUG = 'debug',
INFO = 'info',
WARN = 'warn',
ERROR = 'error'
ERROR = 'error',
}

const PinoLoggerConfig = Object.freeze({
Expand All @@ -20,7 +24,7 @@ const PinoLoggerConfig = Object.freeze({
LOG_HTTP: true,
LOG_HTTP_IN_ALL_MESSAGES: false,
LOG_ROTATION_AUDIT_FILE: 'log-audit.json',
ROTATE_FILE_TRANSPORT_PATH: `${__dirname}/pino-rotate-file.transport`
ROTATE_FILE_TRANSPORT_PATH: `/Users/mbanhawy/dev/projects/bug-zone/server/src/infrastructure/shared/logger/pino-rotate-file.transport.ts`,
});

class PinoLogger implements LoggerDomainService {
Expand All @@ -31,7 +35,9 @@ class PinoLogger implements LoggerDomainService {
}

public get logger(): PinoLoggerType {
return getContext()?.get(GlobalConfig.PINO_LOGGER_KEY) || this.defaultLogger;
return (
getContext()?.get(GlobalConfig.PINO_LOGGER_KEY) || this.defaultLogger
);
}

public debug(message: any, ...optionalParameters: any[]): void {
Expand All @@ -50,7 +56,11 @@ class PinoLogger implements LoggerDomainService {
this.call(LogLevel.ERROR, message, ...optionalParameters);
}

public createPinoHttpMiddleware(): (request: Request, response: Response, next?: NextFunction) => void {
public createPinoHttpMiddleware(): (
request: Request,
response: Response,
next?: NextFunction,
) => void {
return pinoHttp({
logger: this.defaultLogger,
autoLogging: PinoLoggerConfig.LOG_HTTP,
Expand All @@ -60,14 +70,16 @@ class PinoLogger implements LoggerDomainService {
res: 'response',
err: 'error',
reqId: 'requestId',
responseTime: 'timeTaken'
responseTime: 'timeTaken',
},
customSuccessMessage: (_request, response) => {
return response.writableEnded ? 'Request completed!' : 'Request aborted!';
return response.writableEnded
? 'Request completed!'
: 'Request aborted!';
},
customErrorMessage: (_request, response) => {
return `Request failed! An error ${response.statusCode} occurred during the HTTP request.`;
}
},
});
}

Expand All @@ -87,7 +99,9 @@ class PinoLogger implements LoggerDomainService {
Object.assign(objectArgument, message);
}
this.logger[level](objectArgument, ...parameters);
} else if (this.isWrongExceptionsHandlerContract(level, message, parameters)) {
} else if (
this.isWrongExceptionsHandlerContract(level, message, parameters)
) {
const error = new Error(message);
const [errorStack] = parameters;

Expand All @@ -100,7 +114,11 @@ class PinoLogger implements LoggerDomainService {
}
}

private isWrongExceptionsHandlerContract(level: Level, message: any, parameters: any[]): parameters is [string] {
private isWrongExceptionsHandlerContract(
level: Level,
message: any,
parameters: any[],
): parameters is [string] {
return (
level === LogLevel.ERROR &&
typeof message === 'string' &&
Expand All @@ -117,44 +135,46 @@ class PinoLogger implements LoggerDomainService {
options: {
folder: PinoLoggerConfig.LOGS_FOLDER,
filename: 'all',
extension: 'log'
}
extension: 'log',
},
};

const errorRotateFileTarget = {
level: LogLevel.ERROR,
// target: PinoLoggerConfig.ROTATE_FILE_TRANSPORT_PATH,
target: PinoLoggerConfig.ROTATE_FILE_TRANSPORT_PATH,
options: {
folder: PinoLoggerConfig.LOGS_FOLDER,
filename: 'error',
extension: 'log'
}
extension: 'log',
},
};

const pinoPrettyTarget = {
level: LogLevel.DEBUG,
target: 'pino-pretty',
options: { colorize: true, messageKey: 'message' }
options: { colorize: true, messageKey: 'message' },
};

const standardOutputTarget = {
level: LogLevel.DEBUG,
target: 'pino/file',
options: { destination: 1, append: true }
options: { destination: 1, append: true },
};

// const targets: TransportTargetOptions[] = [
// ...(GlobalConfig.IS_TEST ? [] : [errorRotateFileTarget, rotateFileTarget]),
// GlobalConfig.IS_DEVELOPMENT ? pinoPrettyTarget : standardOutputTarget
// ];
const targets: TransportTargetOptions[] = [
...(GlobalConfig.IS_TEST
? []
: [errorRotateFileTarget, rotateFileTarget]),
GlobalConfig.IS_DEVELOPMENT ? pinoPrettyTarget : standardOutputTarget,
];

return pino(
{
enabled: !GlobalConfig.IS_TEST && GlobalConfig.LOGS_ENABLED,
level: PinoLoggerConfig.LOG_LEVEL,
messageKey: 'message'
messageKey: 'message',
},
// pino.transport({ targets, dedupe: false })
pino.transport({ targets, dedupe: false }), // Fixed uncommented line
);
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ interface PinoRotateFileOptions {
extension: string;
}

export default async (options: PinoRotateFileOptions): Promise<EventEmitter> => {
export default async (
options: PinoRotateFileOptions,
): Promise<EventEmitter> => {
const stream = FileStreamRotator.getStream({
filename: `${options.folder}/${options.filename}.%DATE%`,
frequency: 'date',
extension: `.${options.extension}`,
utc: true,
verbose: false,
date_format: 'YYYYMM',
audit_file: `${options.folder}/log-audit.json`
audit_file: `${options.folder}/log-audit.json`,
});
await once(stream, 'open');
return stream;
Expand Down
8 changes: 4 additions & 4 deletions server/src/infrastructure/users/user.repository.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import { Repository } from 'typeorm'; // Import EntityManager
import { RepositoryDec } from '@infrastructure/shared/persistence/repository.decorator';
import { UserRepository } from '@domain/entities/users';
import { appDataSource } from '@infrastructure/shared/persistence/data-source';
import { Logger } from '@domain/shared';
// Import your preferred logging library (e.g., import { logger } from 'your-logger';)

@RepositoryDec({ type: UserRepository })
class UserRepositoryImp implements UserRepository {
private _repository: Repository<UserPersistence> = appDataSource.getRepository(
UserPersistence,
);

private _repository: Repository<UserPersistence> =
appDataSource.getRepository(UserPersistence);

delete(user: User): Promise<boolean> {
throw new Error('Method not implemented.');
Expand All @@ -29,6 +28,7 @@ class UserRepositoryImp implements UserRepository {
}

async findByUsername(username: string): Promise<User | null> {
Logger.info('findByUsername From userRepositoryImp');
const user = await this._repository.findOne({ where: { username } });
return user ? UserMapper.toDomain(user) : null;
}
Expand Down
5 changes: 4 additions & 1 deletion server/src/web/rest/config/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ const AppInfo = Object.freeze({
const AppConfig = Object.freeze({
PORT: getEnvironmentNumber('PORT', 5000),
BASE_PATH: getEnvironmentString('BASE_PATH', '/api'),
LOGGER_PATH: getEnvironmentString('LOGGER_PATH', '../../../../logs/'),
LOGGER_PATH: getEnvironmentString(
'LOGGER_PATH',
'/Users/mbanhawy/dev/projects/bug-zone/server/logs',
),
AUTHORIZATION_ACCESS_TOKEN_HEADER_NAME: 'authorization',
ACCESS_TOKEN_HEADER_NAME: 'access-token',
ACCESS_TOKEN_COOKIE_NAME: 'access-token',
Expand Down
20 changes: 12 additions & 8 deletions server/src/web/rest/controllers/authentication/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ import {
Title,
} from '@tsed/schema';
import { StatusCodes } from 'http-status-codes';
import { BodyParams, Context, Res } from '@tsed/common';
import { BodyParams } from '@tsed/common';
import { RestController } from '@web/rest/infrastructure/rest-controller.decorator';
import { log } from 'console';
import { LOGGER } from '@web/rest/logger';
import { UserSuccessfullyAuthenticatedApiResponse } from './user-successfully-authenticated.api-response';

@RestController('/auth')
@Tags({ name: 'Authentication', description: 'Login and register users' })
Expand All @@ -35,19 +34,24 @@ class AuthController {
@Description(
'Endpoint to perform a user login to obtain access token and refresh token',
)
@Returns(StatusCodes.OK, AuthResponseDto)
@Status(StatusCodes.OK, AuthResponseDto)
@Returns(StatusCodes.OK, UserSuccessfullyAuthenticatedApiResponse)
@Status(StatusCodes.OK, UserSuccessfullyAuthenticatedApiResponse)
public async authenticateUser(
@Example('janedoe') @BodyParams('username') username: string,
@Example('123456') @BodyParams('password') password: string,
): Promise<AuthResponseDto> {
LOGGER.info('Login key is ', username);
): Promise<UserSuccessfullyAuthenticatedApiResponse> {
let triggeredBy = new TriggeredByUser(username, []);
const authenticatedUser = await this._loginUseCase.execute(
AuthRequest.create(triggeredBy, username, password),
);

return authenticatedUser;
return UserSuccessfullyAuthenticatedApiResponse.create(
authenticatedUser.userDetails.id as string,
username,
authenticatedUser.userDetails.email,
authenticatedUser.userDetails.roles,
authenticatedUser.token,
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ class UserSuccessfullyAuthenticatedApiResponse {
@Property()
readonly token: string;

@Property()
readonly refreshToken: string;

constructor(
uuid: string,
username: string,
Expand Down
Loading

0 comments on commit 68081b8

Please sign in to comment.