Added custom logger that logs to db
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,7 +4,7 @@
|
||||
/build
|
||||
|
||||
# Logs
|
||||
logs
|
||||
# logs # This should be allowed because of the entity called `logs`
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
@@ -8,6 +8,7 @@ import { dataSourceOptions } from './appDataSource';
|
||||
import { EntryModule } from './entries/entries.module';
|
||||
import { DataSource, EntityManager } from 'typeorm';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { LogsModule } from './logs/logs.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -15,6 +16,7 @@ import { UsersModule } from './users/users.module';
|
||||
TypeOrmModule.forRoot(dataSourceOptions),
|
||||
EntryModule,
|
||||
UsersModule,
|
||||
LogsModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Initial1770020781006 } from './migrations/1770020781006-initial';
|
||||
import { AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619 } from './migrations/1770067407619-add_create_update_delete_cols_and_rename_source_to_sourceText';
|
||||
import { User } from './users/users.entity';
|
||||
import { AddUserEntity1770147074119 } from './migrations/1770147074119-add_user_entity';
|
||||
import { Log } from './logs/logs.entity';
|
||||
import { AddLogEntity1770232943778 } from './migrations/1770232943778-add_log_entity';
|
||||
|
||||
export const dataSourceOptions: DataSourceOptions = {
|
||||
type: 'postgres',
|
||||
@@ -12,11 +14,12 @@ export const dataSourceOptions: DataSourceOptions = {
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
entities: [Entry, User],
|
||||
entities: [Entry, User, Log],
|
||||
migrations: [
|
||||
Initial1770020781006,
|
||||
AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619,
|
||||
AddUserEntity1770147074119
|
||||
AddUserEntity1770147074119,
|
||||
AddLogEntity1770232943778,
|
||||
],
|
||||
synchronize: false,
|
||||
};
|
||||
|
||||
@@ -3,9 +3,10 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Entry } from './entries.entity';
|
||||
import { EntryService } from './entries.service';
|
||||
import { EntriesController } from './entries.controller';
|
||||
import { LogsModule } from 'src/logs/logs.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Entry])],
|
||||
imports: [TypeOrmModule.forFeature([Entry]), LogsModule],
|
||||
exports: [EntryService],
|
||||
providers: [EntryService],
|
||||
controllers: [EntriesController],
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Entry } from './entries.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { EntryDTO } from './entries.dto';
|
||||
import EntryNotFoundException from './exceptions/entryNotFound.exception';
|
||||
import { LogsService } from 'src/logs/logs.service';
|
||||
|
||||
@Injectable()
|
||||
export class EntryService {
|
||||
constructor(
|
||||
@InjectRepository(Entry)
|
||||
private entryRepository: Repository<Entry>,
|
||||
private readonly logger: LogsService,
|
||||
) {}
|
||||
private readonly logger = new Logger(EntryService.name);
|
||||
|
||||
async save(data: EntryDTO): Promise<Entry> {
|
||||
this.logger.log('Creating entry');
|
||||
await this.logger.log('Creating entry', 'EntryService');
|
||||
const lEntry = new Entry();
|
||||
lEntry.description = data.description;
|
||||
lEntry.sourceText = data.sourceText;
|
||||
@@ -25,7 +26,7 @@ export class EntryService {
|
||||
}
|
||||
|
||||
async updateByUuid(uuid: string, data: EntryDTO): Promise<Entry> {
|
||||
this.logger.log(`Updating entry with UUID ${uuid}`);
|
||||
await this.logger.log(`Updating entry with UUID ${uuid}`, 'EntryService');
|
||||
const oldEntry = await this.entryRepository.findOneBy({ uuid: uuid });
|
||||
if (!oldEntry) {
|
||||
throw new EntryNotFoundException(uuid);
|
||||
@@ -44,38 +45,56 @@ export class EntryService {
|
||||
}
|
||||
|
||||
async findOneByUuid(uuid: string): Promise<Entry | null> {
|
||||
this.logger.log(`Returning entry with UUID ${uuid}`);
|
||||
await this.logger.log(`Returning entry with UUID ${uuid}`, 'EntryService');
|
||||
return this.entryRepository.findOneByOrFail({ uuid: uuid });
|
||||
}
|
||||
|
||||
async findAll(): Promise<Entry[]> {
|
||||
this.logger.log('Returning all entries');
|
||||
await this.logger.log('Returning all entries', 'EntryService');
|
||||
return this.entryRepository.find();
|
||||
}
|
||||
|
||||
async findAllAndDeleted(): Promise<Entry[]> {
|
||||
this.logger.log('Returning all entries, active and deleted');
|
||||
await this.logger.log(
|
||||
'Returning all entries, active and deleted',
|
||||
'EntryService',
|
||||
);
|
||||
return this.entryRepository.find({ withDeleted: true });
|
||||
}
|
||||
|
||||
async softDeleteByUuid(uuid: string): Promise<void> {
|
||||
this.logger.log(`Soft deleting entry with UUID ${uuid}`);
|
||||
await this.logger.log(
|
||||
`Soft deleting entry with UUID ${uuid}`,
|
||||
'EntryService',
|
||||
);
|
||||
const deleteResponse = await this.entryRepository.softDelete({
|
||||
uuid: uuid,
|
||||
});
|
||||
if (!deleteResponse.affected) {
|
||||
throw new EntryNotFoundException(uuid);
|
||||
const exception = new EntryNotFoundException(uuid);
|
||||
await this.logger.error(
|
||||
exception.message,
|
||||
exception.stack,
|
||||
'EntryService',
|
||||
);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
async restoreDeletedByUuid(uuid: string): Promise<Entry | null> {
|
||||
this.logger.log(`Restoring entry with UUID ${uuid}`);
|
||||
await this.logger.log(`Restoring entry with UUID ${uuid}`, 'EntryService');
|
||||
const restoreResponse = await this.entryRepository.restore({
|
||||
uuid: uuid,
|
||||
});
|
||||
|
||||
if (!restoreResponse.affected) {
|
||||
throw new EntryNotFoundException(uuid);
|
||||
const exception = new EntryNotFoundException(uuid);
|
||||
await this.logger.error(
|
||||
exception.message,
|
||||
exception.stack,
|
||||
'EntryService',
|
||||
);
|
||||
throw exception;
|
||||
} else {
|
||||
return this.entryRepository.findOneByOrFail({
|
||||
uuid: uuid,
|
||||
|
||||
24
src/logs/logs.entity.ts
Normal file
24
src/logs/logs.entity.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity({ name: 'logs' })
|
||||
export class Log {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
level: string; // 'log', 'error', 'warn', etc.
|
||||
|
||||
@Column({ type: String })
|
||||
message: string;
|
||||
|
||||
@Column({ type: String, nullable: true })
|
||||
context?: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
timestamp: Date;
|
||||
}
|
||||
12
src/logs/logs.module.ts
Normal file
12
src/logs/logs.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// src/logs/logs.module.ts
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Log } from './logs.entity';
|
||||
import { LogsService } from './logs.service';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Log])],
|
||||
exports: [LogsService],
|
||||
providers: [LogsService],
|
||||
})
|
||||
export class LogsModule {}
|
||||
46
src/logs/logs.service.ts
Normal file
46
src/logs/logs.service.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Injectable, Logger, LoggerService } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Log } from './logs.entity';
|
||||
|
||||
@Injectable()
|
||||
export class LogsService extends Logger implements LoggerService {
|
||||
constructor(
|
||||
@InjectRepository(Log)
|
||||
private readonly logRepository: Repository<Log>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async log(message: string, context?: string) {
|
||||
// output to console using default Logger
|
||||
super.log(message, context);
|
||||
// Save in DB using TypeORM
|
||||
await this.saveLog('log', message, context);
|
||||
}
|
||||
|
||||
async error(message: string, stack?: string, context?: string) {
|
||||
super.error(message, stack, context);
|
||||
await this.saveLog('error', `${message} - ${stack || ''}`, context);
|
||||
}
|
||||
|
||||
async warn(message: string, context?: string) {
|
||||
super.warn(message, context);
|
||||
await this.saveLog('warn', message, context);
|
||||
}
|
||||
|
||||
async debug(message: string, context?: string) {
|
||||
super.debug(message, context);
|
||||
await this.saveLog('debug', message, context);
|
||||
}
|
||||
|
||||
private async saveLog(level: string, message: string, context?: string) {
|
||||
try {
|
||||
const log = this.logRepository.create({ level, message, context });
|
||||
await this.logRepository.save(log);
|
||||
} catch (err) {
|
||||
// maybe have a backup log (local file?) to catch these
|
||||
console.error('Failed to save log to database:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/migrations/1770232943778-add_log_entity.ts
Normal file
15
src/migrations/1770232943778-add_log_entity.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddLogEntity1770232943778 implements MigrationInterface {
|
||||
name = 'AddLogEntity1770232943778';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "logs" ("id" SERIAL NOT NULL, "level" character varying NOT NULL, "message" character varying NOT NULL, "context" character varying, "timestamp" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_fb1b805f2f7795de79fa69340ba" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE "logs"`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user