rentease-backend/utils/logger.js

158 lines
4.3 KiB
JavaScript

const OperationLog = require('../models/OperationLog');
const LoginLog = require('../models/LoginLog');
/**
* 记录操作日志
* @param {Object} options 日志选项
* @param {number} options.userId 用户ID
* @param {string} options.username 用户名
* @param {string} options.module 操作模块
* @param {string} options.action 操作类型
* @param {string} options.description 操作描述
* @param {string} options.method 请求方法
* @param {string} options.url 请求URL
* @param {string} options.ip IP地址
* @param {Object} options.params 请求参数
* @param {Object} options.result 操作结果
* @param {string} options.status 操作状态 success/fail
* @param {number} options.duration 执行时长(毫秒)
*/
async function logOperation(options) {
try {
await OperationLog.create({
userId: options.userId,
username: options.username,
module: options.module,
action: options.action,
description: options.description,
method: options.method,
url: options.url,
ip: options.ip,
params: options.params ? JSON.stringify(options.params) : null,
result: options.result ? JSON.stringify(options.result) : null,
status: options.status || 'success',
duration: options.duration
});
} catch (error) {
console.error('记录操作日志失败:', error);
}
}
/**
* 记录登录日志
* @param {Object} options 日志选项
* @param {number} options.userId 用户ID
* @param {string} options.username 用户名
* @param {string} options.loginType 登录类型 login/logout
* @param {string} options.ip IP地址
* @param {string} options.userAgent 浏览器信息
* @param {string} options.status 登录状态 success/fail
* @param {string} options.message 登录信息/失败原因
*/
async function logLogin(options) {
try {
await LoginLog.create({
userId: options.userId,
username: options.username,
loginType: options.loginType,
ip: options.ip,
userAgent: options.userAgent,
status: options.status || 'success',
message: options.message
});
} catch (error) {
console.error('记录登录日志失败:', error);
}
}
/**
* 获取客户端IP地址
* @param {Object} req Express请求对象
* @returns {string} IP地址
*/
function getClientIp(req) {
return req.headers['x-forwarded-for'] ||
req.headers['x-real-ip'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
'unknown';
}
/**
* 操作日志中间件
* 自动记录API请求日志
*/
function operationLogMiddleware(options = {}) {
const { module = '系统', excludePaths = ['/api/auth/login'] } = options;
return async (req, res, next) => {
// 排除指定路径
if (excludePaths.some(path => req.path.includes(path))) {
return next();
}
const startTime = Date.now();
const originalSend = res.send;
// 捕获响应数据
res.send = function(data) {
res.responseData = data;
return originalSend.call(this, data);
};
res.on('finish', async () => {
const duration = Date.now() - startTime;
const user = req.user || {};
try {
await OperationLog.create({
userId: user.id,
username: user.username,
module: module,
action: getActionFromMethod(req.method),
description: `${req.method} ${req.path}`,
method: req.method,
url: req.originalUrl,
ip: getClientIp(req),
params: JSON.stringify({
body: req.body,
query: req.query,
params: req.params
}),
result: res.responseData ? res.responseData.substring(0, 2000) : null,
status: res.statusCode >= 200 && res.statusCode < 300 ? 'success' : 'fail',
duration: duration
});
} catch (error) {
console.error('记录操作日志失败:', error);
}
});
next();
};
}
/**
* 根据HTTP方法获取操作类型
* @param {string} method HTTP方法
* @returns {string} 操作类型
*/
function getActionFromMethod(method) {
const actionMap = {
'GET': '查询',
'POST': '新增',
'PUT': '修改',
'PATCH': '修改',
'DELETE': '删除'
};
return actionMap[method] || '其他';
}
module.exports = {
logOperation,
logLogin,
getClientIp,
operationLogMiddleware,
getActionFromMethod
};