HM-57. Собираются два типа данные: серверные ошибки и пара запрос-ответ пользователю апи. Серверные ошибки расширены доп полями в том числе дата ошибки. Появилась возможность очищать логи
This commit is contained in:
14
package-lock.json
generated
14
package-lock.json
generated
@ -1962,6 +1962,15 @@
|
||||
"integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/moment": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/moment/-/moment-2.13.0.tgz",
|
||||
"integrity": "sha1-YE69GJvDvDShVIaJQE5hoqSqyJY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"moment": "*"
|
||||
}
|
||||
},
|
||||
"@types/mongodb": {
|
||||
"version": "3.5.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.25.tgz",
|
||||
@ -9199,6 +9208,11 @@
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.27.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
||||
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||
},
|
||||
"mongodb": {
|
||||
"version": "3.5.9",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.9.tgz",
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"@nestjs/mongoose": "^7.0.1",
|
||||
"@nestjs/platform-express": "^7.0.0",
|
||||
"@nestjs/swagger": "^4.5.12",
|
||||
"moment": "^2.27.0",
|
||||
"mongoose": "^5.9.21",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
@ -38,6 +39,7 @@
|
||||
"@nestjs/testing": "^7.0.0",
|
||||
"@types/express": "^4.17.3",
|
||||
"@types/jest": "25.2.3",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/mongoose": "^5.7.29",
|
||||
"@types/node": "^13.9.1",
|
||||
"@types/supertest": "^2.0.8",
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import {Module} from '@nestjs/common';
|
||||
import {Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
|
||||
import {MongooseModule} from '@nestjs/mongoose';
|
||||
import {MONGO_URL, DB_NAME, DB_TEST_NAME, DB_LOGGER} from './consts';
|
||||
import {MONGO_URL, DB_NAME, DB_TEST_NAME, DB_LOGGER, COLLECTION_STORE} from './consts';
|
||||
import {StoreService} from './store/store.service';
|
||||
import {Store, StoreSchema} from './store/store.schema';
|
||||
import {StoreController} from './store/store.controller';
|
||||
import {Log, LogSchema} from './logs/logs.schema';
|
||||
|
||||
import {LogsService} from './logs/logs.service';
|
||||
import {LogsController} from './logs/logs.controller';
|
||||
import {ClientLog, ClientLogSchema, ServerLog, ServerLogSchema} from './logs/logs.schema';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -26,7 +27,8 @@ import {LogsController} from './logs/logs.controller';
|
||||
{name: Store.name, schema: StoreSchema},
|
||||
], DB_TEST_NAME),
|
||||
MongooseModule.forFeature([
|
||||
{name: Log.name, schema: LogSchema},
|
||||
{name: ClientLog.name, schema: ClientLogSchema},
|
||||
{name: ServerLog.name, schema: ServerLogSchema},
|
||||
], DB_LOGGER),
|
||||
],
|
||||
controllers: [
|
||||
@ -38,4 +40,4 @@ import {LogsController} from './logs/logs.controller';
|
||||
LogsService,
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule {}
|
||||
|
||||
@ -4,6 +4,10 @@ export const DB_LOGGER = 'logger';
|
||||
export const MONGO_URL = 'mongodb://localhost:27017';
|
||||
export const COLLECTION_STORE = 'store';
|
||||
export const COLLECTION_LOGS = 'logs';
|
||||
export const LOG_TYPE = {
|
||||
CLIENT: 'client-logs',
|
||||
SERVER: 'server-logs',
|
||||
}
|
||||
export const ALLOW_ORIGIN_ALL: [string, string] = ['Access-Control-Allow-Origin', '*'];
|
||||
export const ALLOW_CREDENTIALS: [string, string] = ['Access-Control-Allow-Credentials', 'true'];
|
||||
export const CONTENT_LENGTH: [string, string] = ['Content-Length', '0'];
|
||||
|
||||
63
src/logs/logging.interceptor.ts
Normal file
63
src/logs/logging.interceptor.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import * as mongoose from 'mongoose';
|
||||
import * as moment from 'moment';
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
import {Injectable, NestInterceptor, ExecutionContext, CallHandler} from '@nestjs/common';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {tap, catchError} from 'rxjs/operators';
|
||||
import {LOG_TYPE} from 'src/consts';
|
||||
|
||||
const STATUSES = {
|
||||
OK: 'OK',
|
||||
BAD: 'BAD',
|
||||
};
|
||||
|
||||
const errorSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
request: Object,
|
||||
response: Object,
|
||||
startTime: String,
|
||||
endTime: String,
|
||||
});
|
||||
const ErrorModel = mongoose.model(LOG_TYPE.CLIENT, errorSchema);
|
||||
|
||||
const saveLog = (
|
||||
type: string,
|
||||
request: any,
|
||||
response: any,
|
||||
startTime: moment.Moment,
|
||||
) => {
|
||||
const endTime = moment();
|
||||
const error = new ErrorModel({
|
||||
type,
|
||||
request: request,
|
||||
response: lodash.isEmpty(response) ? 'Server down, look server-logs' : response,
|
||||
startTime: startTime.format(),
|
||||
endTime: endTime.format(),
|
||||
});
|
||||
error.save();
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class LoggingInterceptor implements NestInterceptor {
|
||||
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||
const start = moment();
|
||||
const args = context.getArgs()?.[0] ?? {};
|
||||
const {headers, url, method, body} = args;
|
||||
const request = {
|
||||
headers, url, method, body
|
||||
};
|
||||
return next
|
||||
.handle()
|
||||
.pipe(
|
||||
tap((response: any) => {
|
||||
saveLog(STATUSES.OK, request, response, start);
|
||||
}),
|
||||
catchError((err: any) => {
|
||||
console.log(typeof err)
|
||||
saveLog(STATUSES.BAD, request, err, start);
|
||||
throw(err);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import {Controller, Get, Header} from '@nestjs/common';
|
||||
import {ApiTags, ApiResponse} from '@nestjs/swagger';
|
||||
import {Controller, Get, Header, Req, NotFoundException, Delete, Options, HttpCode} from '@nestjs/common';
|
||||
import {ApiTags, ApiResponse, ApiParam} from '@nestjs/swagger';
|
||||
import {LogsService} from './logs.service';
|
||||
import {ALLOW_ORIGIN_ALL, COLLECTION_LOGS} from 'src/consts';
|
||||
import {Log, LogRequest} from './logs.schema';
|
||||
import {ALLOW_ORIGIN_ALL, COLLECTION_LOGS, LOG_TYPE, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS} from 'src/consts';
|
||||
import {ClienLogRequest, ClientLog, ServerLog} from './logs.schema';
|
||||
|
||||
@Controller(COLLECTION_LOGS)
|
||||
@ApiTags(COLLECTION_LOGS)
|
||||
@ -11,15 +11,48 @@ export class LogsController {
|
||||
private readonly logsService: LogsService
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@Get('/client')
|
||||
@Header(...ALLOW_ORIGIN_ALL)
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Возвращает список всех логов',
|
||||
type: [LogRequest],
|
||||
type: [ClienLogRequest]
|
||||
})
|
||||
async findAll(): Promise<Log[]> {
|
||||
const logsList = await this.logsService.findAll();
|
||||
return logsList;
|
||||
async findAllClientLogs(): Promise<ClientLog[]> {
|
||||
return await this.logsService.findAllClientLogs();
|
||||
}
|
||||
|
||||
@Delete('/client')
|
||||
@Header(...ALLOW_ORIGIN_ALL)
|
||||
async clearAllClientLogs(): Promise<[]> {
|
||||
return await this.logsService.clearLogsByType(LOG_TYPE.CLIENT);
|
||||
}
|
||||
|
||||
@Get('/server')
|
||||
@Header(...ALLOW_ORIGIN_ALL)
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Возвращает список всех логов',
|
||||
type: [ClienLogRequest]
|
||||
})
|
||||
async findAllServerLogs(): Promise<ServerLog[]> {
|
||||
return await this.logsService.findAllServerLogs();
|
||||
}
|
||||
|
||||
@Delete('/server')
|
||||
@Header(...ALLOW_ORIGIN_ALL)
|
||||
async clearAllServerLogs(): Promise<[]> {
|
||||
return await this.logsService.clearLogsByType(LOG_TYPE.SERVER);
|
||||
}
|
||||
|
||||
@Options()
|
||||
@Header(...ALLOW_ORIGIN_ALL)
|
||||
@Header(...ALLOW_METHOD)
|
||||
@Header(...ALLOW_CREDENTIALS)
|
||||
@Header(...CONTENT_LENGTH)
|
||||
@Header(...ALLOW_HEADERS)
|
||||
@HttpCode(204)
|
||||
async options(): Promise<string> {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,18 +2,70 @@ import { Document } from 'mongoose';
|
||||
import {Prop, Schema, SchemaFactory} from '@nestjs/mongoose';
|
||||
import {ApiProperty} from '@nestjs/swagger';
|
||||
|
||||
export class LogRequest {
|
||||
export class ClienLogRequest {
|
||||
@ApiProperty()
|
||||
type: string;
|
||||
|
||||
@ApiProperty()
|
||||
request: string;
|
||||
|
||||
@ApiProperty()
|
||||
response: string;
|
||||
|
||||
@ApiProperty()
|
||||
startTime: string;
|
||||
|
||||
@ApiProperty()
|
||||
endTime: string;
|
||||
}
|
||||
|
||||
export class ServerLogRequest {
|
||||
@ApiProperty()
|
||||
type: string;
|
||||
|
||||
@ApiProperty()
|
||||
message: string;
|
||||
|
||||
@ApiProperty()
|
||||
trace: string;
|
||||
|
||||
@ApiProperty()
|
||||
date: string;
|
||||
}
|
||||
|
||||
@Schema()
|
||||
export class Log extends Document {
|
||||
@Prop({
|
||||
required: true,
|
||||
type: String,
|
||||
})
|
||||
export class ServerLog extends Document {
|
||||
@Prop({required: true, type: String})
|
||||
type: string;
|
||||
|
||||
@Prop({required: true, type: String})
|
||||
message: string;
|
||||
|
||||
@Prop({required: true, type: String})
|
||||
trace: string;
|
||||
|
||||
@Prop({required: true, type: String})
|
||||
date: string;
|
||||
}
|
||||
|
||||
export const LogSchema = SchemaFactory.createForClass(Log);
|
||||
export const ServerLogSchema = SchemaFactory.createForClass(ServerLog);
|
||||
|
||||
@Schema()
|
||||
export class ClientLog extends Document {
|
||||
@Prop({required: true, type: String})
|
||||
type: string;
|
||||
|
||||
@Prop({required: true, type: String})
|
||||
request: string;
|
||||
|
||||
@Prop({required: true, type: String})
|
||||
response: string;
|
||||
|
||||
@Prop({required: true, type: String})
|
||||
startTime: string;
|
||||
|
||||
@Prop({required: true, type: String})
|
||||
endTime: string;
|
||||
}
|
||||
|
||||
export const ClientLogSchema = SchemaFactory.createForClass(ClientLog);
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {Injectable} from '@nestjs/common';
|
||||
import {InjectConnection} from '@nestjs/mongoose';
|
||||
import {DB_LOGGER, COLLECTION_LOGS} from 'src/consts';
|
||||
import {Connection, Model} from 'mongoose';
|
||||
import {LogSchema, Log} from './logs.schema';
|
||||
import {DB_LOGGER, LOG_TYPE} from 'src/consts';
|
||||
import {Connection} from 'mongoose';
|
||||
import {ClientLog, ServerLog, ClientLogSchema, ServerLogSchema} from './logs.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LogsService {
|
||||
@ -10,11 +10,27 @@ export class LogsService {
|
||||
@InjectConnection(DB_LOGGER) private dbConnection: Connection,
|
||||
) {}
|
||||
|
||||
get logModel(): Model<Log> {
|
||||
return this.dbConnection.model<Log>(COLLECTION_LOGS, LogSchema);
|
||||
async findAllClientLogs(): Promise<ClientLog[]> {
|
||||
return this.dbConnection
|
||||
.model<ClientLog>(LOG_TYPE.CLIENT, ClientLogSchema)
|
||||
.find()
|
||||
.exec();
|
||||
}
|
||||
|
||||
async findAll(): Promise<Log[]> {
|
||||
return this.logModel.find().exec();
|
||||
async findAllServerLogs(): Promise<ServerLog[]> {
|
||||
return this.dbConnection
|
||||
.model<ServerLog>(LOG_TYPE.SERVER, ServerLogSchema)
|
||||
.find()
|
||||
.exec();
|
||||
}
|
||||
|
||||
async clearLogsByType(type: string): Promise<[]> {
|
||||
const logList = type === LOG_TYPE.CLIENT
|
||||
? await this.findAllClientLogs()
|
||||
: await this.findAllServerLogs();
|
||||
if (logList.length) {
|
||||
await this.dbConnection.collection(type).drop();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,37 +1,55 @@
|
||||
import * as mongoose from 'mongoose';
|
||||
import * as moment from 'moment';
|
||||
|
||||
import {Logger as DefaultLogger} from '@nestjs/common';
|
||||
import {MONGO_URL} from 'src/consts';
|
||||
import {MONGO_URL, LOG_TYPE} from 'src/consts';
|
||||
|
||||
|
||||
mongoose.connect(`${MONGO_URL}/logger`, {useNewUrlParser: true});
|
||||
|
||||
const ERRORS = {
|
||||
LOG: 'LOG',
|
||||
ERROR: 'ERROR',
|
||||
WARN: 'WARN',
|
||||
DEBUG: 'DEBUG',
|
||||
VERBOSE: 'VERBODE',
|
||||
};
|
||||
|
||||
const errorSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
message: String,
|
||||
trace: String,
|
||||
date: String,
|
||||
});
|
||||
const ErrorModel = mongoose.model('logs', errorSchema);
|
||||
const ErrorModel = mongoose.model(LOG_TYPE.SERVER, errorSchema);
|
||||
|
||||
const saveError = (type: string, message: string, trace = '') => {
|
||||
const date = moment().format();
|
||||
const error = new ErrorModel({
|
||||
type, message, trace, date
|
||||
});
|
||||
error.save();
|
||||
};
|
||||
|
||||
export class Logger extends DefaultLogger {
|
||||
log(message: string): void {
|
||||
super.log(message);
|
||||
// saveError(ERRORS.LOG, message);
|
||||
}
|
||||
error(message: string, trace: string): void {
|
||||
super.error(message, trace);
|
||||
const error = new ErrorModel({
|
||||
message: `ERROR: message = ${message}, trace = ${trace}`
|
||||
});
|
||||
error.save();
|
||||
saveError(ERRORS.ERROR, message, trace);
|
||||
}
|
||||
warn(message: string): void {
|
||||
super.warn(message);
|
||||
const error = new ErrorModel({
|
||||
message: `WARN: message = ${message}`
|
||||
});
|
||||
error.save();
|
||||
saveError(ERRORS.WARN, message);
|
||||
}
|
||||
debug(message: string): void {
|
||||
super.debug(message);
|
||||
// saveError(ERRORS.DEBUG, message);
|
||||
}
|
||||
verbose(message: string): void {
|
||||
super.verbose(message);
|
||||
// saveError(ERRORS.VERBOSE, message);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
import {Controller, Get, Req, Post, Options, Header, Delete, HttpCode, Put} from '@nestjs/common';
|
||||
import {Controller, Get, Req, Post, Options, Header, Delete, HttpCode, Put, UseInterceptors} from '@nestjs/common';
|
||||
import {StoreService} from './store.service';
|
||||
import {Store, StoreRequest} from './store.schema';
|
||||
import {ApiResponse, ApiTags, ApiParam, ApiBody} from '@nestjs/swagger';
|
||||
import {ALLOW_ORIGIN_ALL, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS, COLLECTION_STORE} from 'src/consts';
|
||||
import {Request} from 'express';
|
||||
import {LoggingInterceptor} from 'src/logs/logging.interceptor';
|
||||
|
||||
const prepareStoreToStoreRequest = ({
|
||||
key, value, description, service_name, author
|
||||
@ -16,6 +17,7 @@ const makeApiHeader = (request: Request): string => {
|
||||
return typeof apiHeader === 'string' ? apiHeader : '';
|
||||
};
|
||||
|
||||
@UseInterceptors(LoggingInterceptor)
|
||||
@Controller(COLLECTION_STORE)
|
||||
@ApiTags(COLLECTION_STORE)
|
||||
export class StoreController {
|
||||
|
||||
@ -71,7 +71,8 @@ export class StoreService {
|
||||
}
|
||||
|
||||
async findOne(api: string, key: string): Promise<Store> {
|
||||
return this.storeModel(api).findOne({key})
|
||||
const searchStore = await this.storeModel(api).findOne({key});
|
||||
return searchStore;
|
||||
}
|
||||
|
||||
async removeOne(api: string, key: string): Promise<Store> {
|
||||
|
||||
19
store.http
19
store.http
@ -1,13 +1,12 @@
|
||||
###
|
||||
GET http://localhost:4001/store HTTP/1.1
|
||||
Api-Name: store-service-test
|
||||
|
||||
###
|
||||
|
||||
GET http://localhost:4001/store/testApi-4 HTTP/1.1
|
||||
GET http://localhost:4001/store/testApi-4345345345 HTTP/1.1
|
||||
Api-Name: store-service-test
|
||||
|
||||
###
|
||||
|
||||
POST http://localhost:4001/store HTTP/1.1
|
||||
content-type: application/json
|
||||
Api-Name: store-service-test
|
||||
@ -23,13 +22,12 @@ Api-Name: store-service-test
|
||||
}
|
||||
|
||||
### Update Request
|
||||
|
||||
PUT http://localhost:4001/store HTTP/1.1
|
||||
content-type: application/json
|
||||
Api-Name: store-service-test
|
||||
|
||||
{
|
||||
"key": "testApi-4",
|
||||
"key": "testApi-4234222",
|
||||
"value": {
|
||||
|
||||
},
|
||||
@ -39,10 +37,17 @@ Api-Name: store-service-test
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
DELETE http://localhost:4001/store/testApi-433 HTTP/1.1
|
||||
Api-Name: store-service-test
|
||||
|
||||
###
|
||||
GET http://localhost:4001/logs/client HTTP/1.1
|
||||
|
||||
GET http://localhost:4001/logs HTTP/1.1
|
||||
###
|
||||
GET http://localhost:4001/logs/server HTTP/1.1
|
||||
|
||||
###
|
||||
DELETE http://localhost:4001/logs/client HTTP/1.1
|
||||
|
||||
###
|
||||
DELETE http://localhost:4001/logs/server HTTP/1.1
|
||||
Reference in New Issue
Block a user