introduce the logging module

This commit is contained in:
Sven Czarnian
2022-10-22 23:24:18 +02:00
parent fced0f7ec5
commit ab623c8cb6
8 changed files with 290 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import { MongooseModule } from '@nestjs/mongoose';
import Configuration from './config/configuration'; import Configuration from './config/configuration';
import { VersioningModule } from './versioning/versioning.module'; import { VersioningModule } from './versioning/versioning.module';
import { PerformanceModule } from './performance/performance.module'; import { PerformanceModule } from './performance/performance.module';
import { LoggingModule } from './logging/logging.module';
@Module({ @Module({
imports: [ imports: [
@@ -24,6 +25,7 @@ import { PerformanceModule } from './performance/performance.module';
}), }),
VersioningModule, VersioningModule,
PerformanceModule, PerformanceModule,
LoggingModule,
], ],
}) })
export class AppModule {} export class AppModule {}

View File

@@ -0,0 +1,32 @@
import { IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class LogEntryDto {
@IsNotEmpty()
@ApiProperty({
description: 'The component which logged the message',
example: 'optimizer',
})
component: string;
@IsNotEmpty()
@ApiProperty({
description: 'The defined log level of this message',
example: 'DEBUG',
})
level: string;
@IsNotEmpty()
@ApiProperty({
description: 'The timestamp when the message was logged',
example: 'Wed, 14 Jun 2017 07:00:00 GMT',
})
timestamp: string;
@IsNotEmpty()
@ApiProperty({
description: 'The logged message',
example: 'This is a log message.',
})
message: string;
}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LoggingController } from './logging.controller';
describe('LoggingController', () => {
let controller: LoggingController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [LoggingController],
}).compile();
controller = module.get<LoggingController>(LoggingController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -0,0 +1,110 @@
import {
Controller,
Get,
Delete,
Query,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { ApiQuery, ApiResponse } from '@nestjs/swagger';
import { LogEntryDto } from './dto/logentry.dto';
import { LoggingService } from './logging.service';
import { LogEntry } from './models/logentry.model';
@Controller('logging')
export class LoggingController {
constructor(private readonly loggingService: LoggingService) {}
private static logEntryToDto(entry: LogEntry): LogEntryDto {
return {
component: entry.component,
level: entry.level,
timestamp: entry.timestamp,
message: entry.message,
};
}
@Get('/all')
@ApiResponse({
status: 201,
description: 'All log entries',
type: [LogEntryDto],
})
@ApiResponse({
status: 404,
description: 'No messages found',
})
async allMessages(): Promise<LogEntryDto[]> {
return this.loggingService.allMessages().then((response) => {
if (response === null) {
throw new HttpException(
'Unable to find the log messages',
HttpStatus.NOT_FOUND,
);
}
const messages: LogEntryDto[] = [];
response.forEach((entry) =>
messages.push(LoggingController.logEntryToDto(entry)),
);
return messages;
});
}
@Get('/component')
@ApiQuery({
name: 'component',
description: 'The component filter for the messages',
type: String,
})
@ApiResponse({
status: 201,
description: 'All log entries',
type: [LogEntryDto],
})
@ApiResponse({
status: 404,
description: 'No messages found',
})
async componentMessages(
@Query('component') component: string,
): Promise<LogEntryDto[]> {
return this.loggingService.componentMessages(component).then((response) => {
if (response === null) {
throw new HttpException(
'Unable to find the log messages',
HttpStatus.NOT_FOUND,
);
}
const messages: LogEntryDto[] = [];
response.forEach((entry) =>
messages.push(LoggingController.logEntryToDto(entry)),
);
return messages;
});
}
@Delete('/deleteAll')
@ApiResponse({
status: 200,
description: 'All log entries are deleted',
})
async deleteAll(): Promise<void> {
return this.loggingService.deleteAllMessages();
}
@Delete('/deleteComponent')
@ApiQuery({
name: 'component',
description: 'The component filter for the messages',
type: String,
})
@ApiResponse({
status: 200,
description: 'All log entries are deleted',
})
async deleteComponent(@Query('component') component: string): Promise<void> {
return this.loggingService.deleteComponentMessages(component);
}
}

View File

@@ -0,0 +1,15 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { LoggingService } from './logging.service';
import { LoggingController } from './logging.controller';
import { LogEntrySchema } from './models/logentry.model';
@Module({
imports: [
MongooseModule.forFeature([{ name: 'logging', schema: LogEntrySchema }]),
],
providers: [LoggingService],
controllers: [LoggingController],
exports: [MongooseModule, LoggingService],
})
export class LoggingModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LoggingService } from './logging.service';
describe('LoggingService', () => {
let service: LoggingService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [LoggingService],
}).compile();
service = module.get<LoggingService>(LoggingService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -0,0 +1,61 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { LogEntry, LogEntryDocument } from './models/logentry.model';
@Injectable()
export class LoggingService {
constructor(
@InjectModel('logging')
private readonly logEntryModel: Model<LogEntryDocument>,
) {}
private async addLogMessage(
component: string,
level: string,
message: string,
): Promise<void> {
await this.logEntryModel.create({
component,
level,
timestamp: new Date().toUTCString(),
message,
});
}
async addDebugMessage(component: string, message: string): Promise<void> {
await this.addLogMessage(component, 'DEBUG', message);
}
async addInfoMessage(component: string, message: string): Promise<void> {
await this.addLogMessage(component, 'INFO', message);
}
async addWarningMessage(component: string, message: string): Promise<void> {
await this.addLogMessage(component, 'WARNING', message);
}
async addErrorMessage(component: string, message: string): Promise<void> {
await this.addLogMessage(component, 'ERROR', message);
}
async addCriticalMessage(component: string, message: string): Promise<void> {
await this.addLogMessage(component, 'CRITICAL', message);
}
async deleteComponentMessages(component: string): Promise<void> {
this.logEntryModel.deleteMany({ component });
}
async deleteAllMessages(): Promise<void> {
this.logEntryModel.deleteMany({});
}
async componentMessages(component: string): Promise<LogEntry[]> {
return this.logEntryModel.find({ component });
}
async allMessages(): Promise<LogEntry[]> {
return this.logEntryModel.find({});
}
}

View File

@@ -0,0 +1,34 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type LogEntryDocument = LogEntry & Document;
@Schema()
export class LogEntry {
@Prop({
required: true,
type: String,
})
component: string;
@Prop({
required: true,
enum: ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
type: String,
})
level: string;
@Prop({
required: true,
type: String,
})
timestamp: string;
@Prop({
required: true,
type: String,
})
message: string;
}
export const LogEntrySchema = SchemaFactory.createForClass(LogEntry);