feat: WWJCloud 企业级全栈框架 v0.3.5 完整更新
🚀 核心更新: - ✅ 完善 NestJS 企业级架构设计 - ✅ 优化配置中心和基础设施层 - ✅ 增强第三方服务集成能力 - ✅ 完善多租户架构支持 - 🎯 对标 Java Spring Boot 和 PHP ThinkPHP 📦 新增文件: - wwjcloud-nest 完整框架结构 - Docker 容器化配置 - 管理后台界面 - 数据库迁移脚本 🔑 Key: ebb38b43ec39f355f071294fd1cf9c42
This commit is contained in:
542
wwjcloud-nest/src/common/logging/logging.service.ts
Normal file
542
wwjcloud-nest/src/common/logging/logging.service.ts
Normal file
@@ -0,0 +1,542 @@
|
||||
import { Injectable, Inject, Logger } from '@nestjs/common';
|
||||
import type {
|
||||
LoggingInterface,
|
||||
StructuredLoggingInterface,
|
||||
StructuredLogData,
|
||||
RequestLogData,
|
||||
ResponseLogData,
|
||||
DatabaseQueryLogData,
|
||||
CacheOperationLogData,
|
||||
ExternalApiCallLogData,
|
||||
BusinessEventLogData,
|
||||
UserLogData,
|
||||
ErrorLogData,
|
||||
LoggingOptions,
|
||||
} from './logging.interface';
|
||||
import { LogLevel } from './logging.interface';
|
||||
|
||||
/**
|
||||
* 日志服务
|
||||
* 基于 NestJS 官方示例实现
|
||||
* 参考: https://docs.nestjs.cn/fundamentals/dependency-injection
|
||||
* 对应 Java: 日志服务
|
||||
*/
|
||||
@Injectable()
|
||||
export class LoggingService
|
||||
implements LoggingInterface, StructuredLoggingInterface
|
||||
{
|
||||
private readonly logger = new Logger(LoggingService.name);
|
||||
private currentLevel: LogLevel = LogLevel.INFO;
|
||||
|
||||
constructor(
|
||||
@Inject('LOGGING_PROVIDER')
|
||||
private readonly loggingProvider: LoggingInterface,
|
||||
@Inject('STRUCTURED_LOGGING_PROVIDER')
|
||||
private readonly structuredLoggingProvider: StructuredLoggingInterface,
|
||||
) {}
|
||||
|
||||
// ==================== 基础日志接口 ====================
|
||||
|
||||
/**
|
||||
* 记录调试日志
|
||||
*/
|
||||
debug(message: string, context?: string, meta?: Record<string, any>): void {
|
||||
try {
|
||||
this.loggingProvider.debug(message, context, meta);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log debug message: ${message}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录信息日志
|
||||
*/
|
||||
info(message: string, context?: string, meta?: Record<string, any>): void {
|
||||
try {
|
||||
this.loggingProvider.info(message, context, meta);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log info message: ${message}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录警告日志
|
||||
*/
|
||||
warn(message: string, context?: string, meta?: Record<string, any>): void {
|
||||
try {
|
||||
this.loggingProvider.warn(message, context, meta);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log warn message: ${message}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误日志
|
||||
*/
|
||||
error(message: string, context?: string, meta?: Record<string, any>): void {
|
||||
try {
|
||||
this.loggingProvider.error(message, context, meta);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log error message: ${message}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录致命错误日志
|
||||
*/
|
||||
fatal(message: string, context?: string, meta?: Record<string, any>): void {
|
||||
try {
|
||||
this.loggingProvider.fatal(message, context, meta);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log fatal message: ${message}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录日志
|
||||
*/
|
||||
log(
|
||||
level: LogLevel,
|
||||
message: string,
|
||||
context?: string,
|
||||
meta?: Record<string, any>,
|
||||
): void {
|
||||
try {
|
||||
this.loggingProvider.log(level, message, context, meta);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log message: ${message}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日志级别
|
||||
*/
|
||||
setLevel(level: LogLevel): void {
|
||||
try {
|
||||
this.currentLevel = level;
|
||||
this.loggingProvider.setLevel(level);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to set log level: ${level}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日志级别
|
||||
*/
|
||||
getLevel(): LogLevel {
|
||||
return this.currentLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建子日志器
|
||||
*/
|
||||
child(context: string): LoggingInterface {
|
||||
try {
|
||||
return this.loggingProvider.child(context);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to create child logger: ${context}`, error);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 结构化日志接口 ====================
|
||||
|
||||
/**
|
||||
* 记录结构化日志
|
||||
*/
|
||||
logStructured(
|
||||
level: LogLevel,
|
||||
message: string,
|
||||
structuredData: StructuredLogData,
|
||||
): void {
|
||||
try {
|
||||
this.structuredLoggingProvider.logStructured(
|
||||
level,
|
||||
message,
|
||||
structuredData,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log structured message: ${message}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录请求日志
|
||||
*/
|
||||
logRequest(
|
||||
request: RequestLogData,
|
||||
response: ResponseLogData,
|
||||
duration: number,
|
||||
): void {
|
||||
try {
|
||||
this.structuredLoggingProvider.logRequest(request, response, duration);
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to log request', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录数据库查询日志
|
||||
*/
|
||||
logDatabaseQuery(
|
||||
query: DatabaseQueryLogData,
|
||||
duration: number,
|
||||
result?: any,
|
||||
): void {
|
||||
try {
|
||||
this.structuredLoggingProvider.logDatabaseQuery(query, duration, result);
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to log database query', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录缓存操作日志
|
||||
*/
|
||||
logCacheOperation(
|
||||
operation: CacheOperationLogData,
|
||||
hit: boolean,
|
||||
duration: number,
|
||||
): void {
|
||||
try {
|
||||
this.structuredLoggingProvider.logCacheOperation(
|
||||
operation,
|
||||
hit,
|
||||
duration,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to log cache operation', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录外部API调用日志
|
||||
*/
|
||||
logExternalApiCall(
|
||||
apiCall: ExternalApiCallLogData,
|
||||
response: any,
|
||||
duration: number,
|
||||
): void {
|
||||
try {
|
||||
this.structuredLoggingProvider.logExternalApiCall(
|
||||
apiCall,
|
||||
response,
|
||||
duration,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to log external API call', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录业务事件日志
|
||||
*/
|
||||
logBusinessEvent(
|
||||
event: BusinessEventLogData,
|
||||
user?: UserLogData,
|
||||
meta?: Record<string, any>,
|
||||
): void {
|
||||
try {
|
||||
this.structuredLoggingProvider.logBusinessEvent(event, user, meta);
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to log business event', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 装饰器支持 ====================
|
||||
|
||||
/**
|
||||
* 日志装饰器实现
|
||||
*/
|
||||
async logMethod<T>(
|
||||
options: LoggingOptions,
|
||||
fn: () => T | Promise<T>,
|
||||
args: any[] = [],
|
||||
): Promise<T> {
|
||||
const {
|
||||
level = LogLevel.INFO,
|
||||
context = 'Method',
|
||||
logArgs = false,
|
||||
logResult = false,
|
||||
logDuration = true,
|
||||
logError = true,
|
||||
enabled = true,
|
||||
message,
|
||||
meta = {},
|
||||
} = options;
|
||||
|
||||
if (!enabled) {
|
||||
return await fn();
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
const logMessage = message || `${context} execution`;
|
||||
const logMeta = { ...meta };
|
||||
|
||||
try {
|
||||
// 记录方法开始
|
||||
if (logArgs) {
|
||||
logMeta.args = args;
|
||||
}
|
||||
|
||||
this.log(level, `${logMessage} started`, context, logMeta);
|
||||
|
||||
// 执行方法
|
||||
const result = await fn();
|
||||
|
||||
// 记录方法完成
|
||||
const duration = Date.now() - startTime;
|
||||
const successMeta = { ...logMeta };
|
||||
|
||||
if (logResult) {
|
||||
successMeta.result = result;
|
||||
}
|
||||
|
||||
if (logDuration) {
|
||||
successMeta.duration = duration;
|
||||
}
|
||||
|
||||
this.log(level, `${logMessage} completed`, context, successMeta);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime;
|
||||
const errorMeta = { ...logMeta };
|
||||
|
||||
if (logDuration) {
|
||||
errorMeta.duration = duration;
|
||||
}
|
||||
|
||||
if (logError) {
|
||||
errorMeta.error = this.serializeError(error);
|
||||
}
|
||||
|
||||
this.error(`${logMessage} failed`, context, errorMeta);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
/**
|
||||
* 序列化错误
|
||||
*/
|
||||
private serializeError(error: any): ErrorLogData {
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
code: (error as any).code,
|
||||
cause: (error as any).cause,
|
||||
context: (error as any).context,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'UnknownError',
|
||||
message: String(error),
|
||||
context: { originalError: error },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录HTTP请求
|
||||
*/
|
||||
logHttpRequest(
|
||||
method: string,
|
||||
url: string,
|
||||
statusCode: number,
|
||||
duration: number,
|
||||
userAgent?: string,
|
||||
ip?: string,
|
||||
userId?: string,
|
||||
): void {
|
||||
const request: RequestLogData = {
|
||||
method,
|
||||
url,
|
||||
headers: {},
|
||||
ip,
|
||||
userAgent,
|
||||
userId,
|
||||
};
|
||||
|
||||
const response: ResponseLogData = {
|
||||
statusCode,
|
||||
headers: {},
|
||||
};
|
||||
|
||||
this.logRequest(request, response, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录数据库操作
|
||||
*/
|
||||
logDatabaseOperation(
|
||||
operation: string,
|
||||
table: string,
|
||||
query: string,
|
||||
duration: number,
|
||||
params?: any[],
|
||||
result?: any,
|
||||
): void {
|
||||
const queryData: DatabaseQueryLogData = {
|
||||
operation,
|
||||
table,
|
||||
query,
|
||||
params,
|
||||
};
|
||||
|
||||
this.logDatabaseQuery(queryData, duration, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录缓存操作
|
||||
*/
|
||||
logCacheOperationSimple(
|
||||
operation: string,
|
||||
key: string,
|
||||
hit: boolean,
|
||||
duration: number,
|
||||
ttl?: number,
|
||||
size?: number,
|
||||
): void {
|
||||
const operationData: CacheOperationLogData = {
|
||||
operation,
|
||||
key,
|
||||
ttl,
|
||||
size,
|
||||
};
|
||||
|
||||
this.logCacheOperation(operationData, hit, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录外部API调用
|
||||
*/
|
||||
logExternalApiCallSimple(
|
||||
service: string,
|
||||
endpoint: string,
|
||||
method: string,
|
||||
statusCode: number,
|
||||
duration: number,
|
||||
headers?: Record<string, string>,
|
||||
body?: any,
|
||||
): void {
|
||||
const apiCall: ExternalApiCallLogData = {
|
||||
service,
|
||||
endpoint,
|
||||
method,
|
||||
headers: headers || {},
|
||||
body,
|
||||
};
|
||||
|
||||
const response = { statusCode };
|
||||
this.logExternalApiCall(apiCall, response, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录业务事件
|
||||
*/
|
||||
logBusinessEventSimple(
|
||||
event: string,
|
||||
action: string,
|
||||
resource: string,
|
||||
resourceId?: string,
|
||||
result?: 'success' | 'failure' | 'partial',
|
||||
data?: Record<string, any>,
|
||||
userId?: string,
|
||||
): void {
|
||||
const eventData: BusinessEventLogData = {
|
||||
event,
|
||||
action,
|
||||
resource,
|
||||
resourceId,
|
||||
result,
|
||||
data,
|
||||
};
|
||||
|
||||
const userData: UserLogData | undefined = userId
|
||||
? { id: userId }
|
||||
: undefined;
|
||||
this.logBusinessEvent(eventData, userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录性能指标
|
||||
*/
|
||||
logPerformance(
|
||||
operation: string,
|
||||
duration: number,
|
||||
context?: string,
|
||||
meta?: Record<string, any>,
|
||||
): void {
|
||||
const performanceMeta = {
|
||||
operation,
|
||||
duration,
|
||||
...meta,
|
||||
};
|
||||
|
||||
if (duration > 1000) {
|
||||
this.warn(`Slow operation: ${operation}`, context, performanceMeta);
|
||||
} else if (duration > 500) {
|
||||
this.info(`Operation: ${operation}`, context, performanceMeta);
|
||||
} else {
|
||||
this.debug(`Operation: ${operation}`, context, performanceMeta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录安全事件
|
||||
*/
|
||||
logSecurityEvent(
|
||||
event: string,
|
||||
severity: 'low' | 'medium' | 'high' | 'critical',
|
||||
context?: string,
|
||||
meta?: Record<string, any>,
|
||||
): void {
|
||||
const securityMeta = {
|
||||
event,
|
||||
severity,
|
||||
...meta,
|
||||
};
|
||||
|
||||
switch (severity) {
|
||||
case 'critical':
|
||||
this.fatal(`Security event: ${event}`, context, securityMeta);
|
||||
break;
|
||||
case 'high':
|
||||
this.error(`Security event: ${event}`, context, securityMeta);
|
||||
break;
|
||||
case 'medium':
|
||||
this.warn(`Security event: ${event}`, context, securityMeta);
|
||||
break;
|
||||
case 'low':
|
||||
this.info(`Security event: ${event}`, context, securityMeta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录审计日志
|
||||
*/
|
||||
logAudit(
|
||||
action: string,
|
||||
resource: string,
|
||||
resourceId?: string,
|
||||
userId?: string,
|
||||
result?: 'success' | 'failure',
|
||||
meta?: Record<string, any>,
|
||||
): void {
|
||||
const auditMeta = {
|
||||
action,
|
||||
resource,
|
||||
resourceId,
|
||||
userId,
|
||||
result,
|
||||
...meta,
|
||||
};
|
||||
|
||||
this.info(`Audit: ${action} on ${resource}`, 'audit', auditMeta);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user