feat: add logging functionality with LoggerService; implement log entity and controller; enhance query processing with logging support
This commit is contained in:
@ -13,6 +13,8 @@ import { QueryHandlerService } from "../handler/query.handler.service";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { QueryExecuterService } from "../executer/query.executer.service";
|
||||
import { QueryGuard } from "src/query/guards/query.guard";
|
||||
import { LoggerService } from "../logger/logger.service";
|
||||
import { TLogType } from "../logger/logger.types";
|
||||
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export abstract class BaseQueryController {
|
||||
@ -20,7 +22,9 @@ export abstract class BaseQueryController {
|
||||
@Inject(QueryHandlerService)
|
||||
protected readonly queryHandlerService: QueryHandlerService,
|
||||
@Inject(QueryExecuterService)
|
||||
protected readonly queryExecuterService: QueryExecuterService
|
||||
protected readonly queryExecuterService: QueryExecuterService,
|
||||
@Inject(LoggerService)
|
||||
protected readonly loggerService: LoggerService
|
||||
) {}
|
||||
|
||||
protected abstract getIsCommand(): boolean;
|
||||
@ -49,9 +53,26 @@ export abstract class BaseQueryController {
|
||||
@Headers() headers: Record<string, any>,
|
||||
@Res() res: Response
|
||||
) {
|
||||
const loggerTraceId =
|
||||
headers["x-trace-id"] || LoggerService.generateTraceId();
|
||||
const log = LoggerService.log(
|
||||
{
|
||||
traceId: loggerTraceId,
|
||||
startTime: new Date().getTime(),
|
||||
payload: query,
|
||||
headers: headers,
|
||||
cookies: headers.cookie,
|
||||
url: `/run/${id}`,
|
||||
response: null,
|
||||
content: [],
|
||||
},
|
||||
{ content: "", type: TLogType.info, timeStamp: new Date().getTime() }
|
||||
);
|
||||
|
||||
const queryResult = await this.queryExecuterService.runQueryQueued(
|
||||
id,
|
||||
query,
|
||||
log,
|
||||
headers,
|
||||
headers.cookie.split("; ").reduce((acc, cookie) => {
|
||||
const [key, value] = cookie.split("=");
|
||||
@ -60,6 +81,11 @@ export abstract class BaseQueryController {
|
||||
}, {})
|
||||
);
|
||||
|
||||
if (queryResult?.log) {
|
||||
queryResult.log.endTime = new Date().getTime();
|
||||
await this.loggerService.create(queryResult.log.traceId, queryResult.log);
|
||||
}
|
||||
|
||||
res.status(queryResult?.statusCode || 200);
|
||||
|
||||
if (queryResult?.cookies) {
|
||||
|
||||
@ -2,6 +2,7 @@ import { Controller, Inject } from "@nestjs/common";
|
||||
import { QueryHandlerService } from "../handler/query.handler.service";
|
||||
import { QueryExecuterService } from "../executer/query.executer.service";
|
||||
import { BaseQueryController } from "../base/base-query.controller";
|
||||
import { LoggerService } from "../logger/logger.service";
|
||||
|
||||
@Controller("command")
|
||||
export class CommandController extends BaseQueryController {
|
||||
@ -9,9 +10,11 @@ export class CommandController extends BaseQueryController {
|
||||
@Inject(QueryHandlerService)
|
||||
queryHandlerService: QueryHandlerService,
|
||||
@Inject(QueryExecuterService)
|
||||
queryExecuterService: QueryExecuterService
|
||||
queryExecuterService: QueryExecuterService,
|
||||
@Inject(LoggerService)
|
||||
loggerService: LoggerService
|
||||
) {
|
||||
super(queryHandlerService, queryExecuterService);
|
||||
super(queryHandlerService, queryExecuterService, loggerService);
|
||||
}
|
||||
|
||||
protected getIsCommand(): boolean {
|
||||
|
||||
@ -16,6 +16,7 @@ import { QUEUE_NAMES } from "src/queue/constants";
|
||||
import { Queue, QueueEvents } from "bullmq";
|
||||
import { FunctionService } from "src/query/function/function.service";
|
||||
import { SessionService } from "../session/session.service";
|
||||
import { TLog } from "../logger/logger.types";
|
||||
|
||||
@Injectable()
|
||||
export class QueryExecuterService {
|
||||
@ -56,6 +57,7 @@ export class QueryExecuterService {
|
||||
async runQueryQueued(
|
||||
token: string,
|
||||
queryData: any,
|
||||
log: TLog,
|
||||
headers: Record<string, any> = {},
|
||||
cookies: Record<string, any> = {}
|
||||
): Promise<QueryResponse> {
|
||||
@ -66,6 +68,7 @@ export class QueryExecuterService {
|
||||
queryData,
|
||||
headers,
|
||||
cookies,
|
||||
log,
|
||||
},
|
||||
{
|
||||
removeOnComplete: true,
|
||||
@ -82,7 +85,8 @@ export class QueryExecuterService {
|
||||
token: string,
|
||||
queryData: any,
|
||||
headers: Record<string, any> = {},
|
||||
cookies: Record<string, any> = {}
|
||||
cookies: Record<string, any> = {},
|
||||
log: TLog = null
|
||||
): Promise<QueryResponse> {
|
||||
const query = await this.queryRepository.findOne({
|
||||
where: { id: token },
|
||||
@ -104,7 +108,8 @@ export class QueryExecuterService {
|
||||
const result = await vm.runScript(
|
||||
this.clearImports(query.source),
|
||||
queryData,
|
||||
headers
|
||||
headers,
|
||||
log
|
||||
);
|
||||
|
||||
if (!this.checkResponse(result)) {
|
||||
|
||||
@ -2,6 +2,7 @@ import { Controller, Inject } from "@nestjs/common";
|
||||
import { QueryHandlerService } from "./query.handler.service";
|
||||
import { QueryExecuterService } from "../executer/query.executer.service";
|
||||
import { BaseQueryController } from "../base/base-query.controller";
|
||||
import { LoggerService } from "../logger/logger.service";
|
||||
|
||||
@Controller("query")
|
||||
export class QueryController extends BaseQueryController {
|
||||
@ -9,9 +10,11 @@ export class QueryController extends BaseQueryController {
|
||||
@Inject(QueryHandlerService)
|
||||
queryHandlerService: QueryHandlerService,
|
||||
@Inject(QueryExecuterService)
|
||||
queryExecuterService: QueryExecuterService
|
||||
queryExecuterService: QueryExecuterService,
|
||||
@Inject(LoggerService)
|
||||
loggerService: LoggerService
|
||||
) {
|
||||
super(queryHandlerService, queryExecuterService);
|
||||
super(queryHandlerService, queryExecuterService, loggerService);
|
||||
}
|
||||
|
||||
protected getIsCommand(): boolean {
|
||||
|
||||
17
src/query/logger/entities/log.entity.ts
Normal file
17
src/query/logger/entities/log.entity.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { TLog } from "../logger.types";
|
||||
|
||||
@Entity()
|
||||
export class Log {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id: string;
|
||||
|
||||
@Column({ type: "varchar", length: 255 })
|
||||
traceId: string;
|
||||
|
||||
@Column({ type: "longtext" })
|
||||
content: TLog;
|
||||
|
||||
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
|
||||
createdAt: Date;
|
||||
}
|
||||
32
src/query/logger/logger.controller.ts
Normal file
32
src/query/logger/logger.controller.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Body, Controller, Get, Inject, Post, UseGuards } from "@nestjs/common";
|
||||
import { LoggerService } from "./logger.service";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
|
||||
@Controller("logger")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export class LoggerController {
|
||||
constructor(
|
||||
@Inject(LoggerService)
|
||||
private readonly loggerService: LoggerService
|
||||
) {}
|
||||
|
||||
@Get("/:traceId")
|
||||
getByTraceId(@Inject("traceId") traceId: string) {
|
||||
return this.loggerService.findByTraceId(traceId);
|
||||
}
|
||||
|
||||
@Post("/find")
|
||||
find(
|
||||
@Body()
|
||||
body: {
|
||||
traceId?: string;
|
||||
fromDate?: Date;
|
||||
toDate?: Date;
|
||||
url?: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}
|
||||
) {
|
||||
return this.loggerService.find(body);
|
||||
}
|
||||
}
|
||||
66
src/query/logger/logger.service.ts
Normal file
66
src/query/logger/logger.service.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Log } from "./entities/log.entity";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { TLog, TLogLine } from "./logger.types";
|
||||
|
||||
@Injectable()
|
||||
export class LoggerService {
|
||||
constructor(
|
||||
@InjectRepository(Log)
|
||||
private readonly logRepository: Repository<Log>
|
||||
) {}
|
||||
|
||||
async create(traceId: string, content: TLog): Promise<Log> {
|
||||
const log = this.logRepository.create({ traceId, content });
|
||||
return await this.logRepository.save(log);
|
||||
}
|
||||
|
||||
async findByTraceId(traceId: string): Promise<Log[]> {
|
||||
return await this.logRepository.find({ where: { traceId } });
|
||||
}
|
||||
|
||||
async find(data: {
|
||||
traceId?: string;
|
||||
fromDate?: Date;
|
||||
toDate?: Date;
|
||||
url?: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}): Promise<Log[]> {
|
||||
const query = this.logRepository.createQueryBuilder("log");
|
||||
|
||||
if (data.traceId) {
|
||||
query.andWhere("log.traceId = :traceId", { traceId: data.traceId });
|
||||
}
|
||||
|
||||
if (data.fromDate) {
|
||||
query.andWhere("log.createdAt >= :fromDate", { fromDate: data.fromDate });
|
||||
}
|
||||
|
||||
if (data.toDate) {
|
||||
query.andWhere("log.createdAt <= :toDate", { toDate: data.toDate });
|
||||
}
|
||||
|
||||
if (data.url) {
|
||||
query.andWhere("log.content LIKE :url", { url: `%${data.url}%` });
|
||||
}
|
||||
|
||||
query.skip(data.offset).take(data.limit).orderBy("log.createdAt", "DESC");
|
||||
|
||||
return await query.getMany();
|
||||
}
|
||||
|
||||
static log(logStack: TLog, log: TLog | TLogLine): TLog {
|
||||
logStack.content.push(log);
|
||||
return logStack;
|
||||
}
|
||||
|
||||
static generateTraceId(): string {
|
||||
return (
|
||||
Date.now().toString(36) +
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15)
|
||||
);
|
||||
}
|
||||
}
|
||||
26
src/query/logger/logger.types.ts
Normal file
26
src/query/logger/logger.types.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { QueryResponse } from "src/vm/vm.constants";
|
||||
|
||||
export enum TLogType {
|
||||
info = "info",
|
||||
error = "error",
|
||||
debug = "debug",
|
||||
warn = "warn",
|
||||
}
|
||||
|
||||
export type TLogLine = {
|
||||
content: string;
|
||||
type: TLogType;
|
||||
timeStamp: number;
|
||||
};
|
||||
|
||||
export interface TLog {
|
||||
traceId: string;
|
||||
content: (TLogLine | TLog)[];
|
||||
payload?: any;
|
||||
headers: Record<string, string | string[] | undefined>;
|
||||
cookies: Record<string, string>;
|
||||
url: string;
|
||||
response: QueryResponse | null;
|
||||
startTime: number;
|
||||
endTime?: number;
|
||||
}
|
||||
@ -15,6 +15,9 @@ import { FunctionController } from "src/query/function/function.controller";
|
||||
import { RedisManagerModule } from "src/redisManager/redisManager.module";
|
||||
import { RedisModule } from "src/redis/redis.module";
|
||||
import { SessionService } from "./session/session.service";
|
||||
import { Log } from "./logger/entities/log.entity";
|
||||
import { LoggerService } from "./logger/logger.service";
|
||||
import { LoggerController } from "./logger/logger.controller";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -24,15 +27,26 @@ import { SessionService } from "./session/session.service";
|
||||
forwardRef(() => QueueModule),
|
||||
forwardRef(() => RedisModule),
|
||||
forwardRef(() => RedisManagerModule),
|
||||
TypeOrmModule.forFeature([Query, FunctionEntity]),
|
||||
TypeOrmModule.forFeature([Query, FunctionEntity, Log]),
|
||||
],
|
||||
controllers: [
|
||||
QueryController,
|
||||
CommandController,
|
||||
FunctionController,
|
||||
LoggerController,
|
||||
],
|
||||
controllers: [QueryController, CommandController, FunctionController],
|
||||
providers: [
|
||||
QueryExecuterService,
|
||||
SessionService,
|
||||
LoggerService,
|
||||
QueryHandlerService,
|
||||
FunctionService,
|
||||
],
|
||||
exports: [QueryExecuterService, TypeOrmModule, QueryHandlerService],
|
||||
exports: [
|
||||
QueryExecuterService,
|
||||
TypeOrmModule,
|
||||
QueryHandlerService,
|
||||
LoggerService,
|
||||
],
|
||||
})
|
||||
export class QueryModule {}
|
||||
|
||||
Reference in New Issue
Block a user