新增:新增系统监控/系统日志功能,优化日志表结构
This commit is contained in:
parent
c57383abad
commit
d8debf5481
@ -252,7 +252,9 @@ continew-admin
|
|||||||
│ │ ├─ login # 登录模块
|
│ │ ├─ login # 登录模块
|
||||||
│ │ ├─ monitor # 系统监控模块
|
│ │ ├─ monitor # 系统监控模块
|
||||||
│ │ │ └─ log # 日志管理
|
│ │ │ └─ log # 日志管理
|
||||||
│ │ │ └─ operation # 操作日志
|
│ │ │ ├─ login # 登录日志
|
||||||
|
│ │ │ ├─ operation # 操作日志
|
||||||
|
│ │ │ └─ system # 系统日志
|
||||||
│ │ └─ system # 系统管理模块
|
│ │ └─ system # 系统管理模块
|
||||||
│ │ └─ user # 用户模块
|
│ │ └─ user # 用户模块
|
||||||
│ │ └─ center # 个人中心
|
│ │ └─ center # 个人中心
|
||||||
|
@ -175,7 +175,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
|||||||
sysLog.setRequestUrl(StrUtil.isBlank(request.getQueryString()) ? request.getRequestURL().toString()
|
sysLog.setRequestUrl(StrUtil.isBlank(request.getQueryString()) ? request.getRequestURL().toString()
|
||||||
: request.getRequestURL().append("?").append(request.getQueryString()).toString());
|
: request.getRequestURL().append("?").append(request.getQueryString()).toString());
|
||||||
sysLog.setRequestMethod(request.getMethod());
|
sysLog.setRequestMethod(request.getMethod());
|
||||||
sysLog.setRequestHeader(this.desensitize(ServletUtil.getHeaderMap(request)));
|
sysLog.setRequestHeaders(this.desensitize(ServletUtil.getHeaderMap(request)));
|
||||||
String requestBody = this.getRequestBody(request);
|
String requestBody = this.getRequestBody(request);
|
||||||
if (StrUtil.isNotBlank(requestBody)) {
|
if (StrUtil.isNotBlank(requestBody)) {
|
||||||
sysLog.setRequestBody(this.desensitize(
|
sysLog.setRequestBody(this.desensitize(
|
||||||
@ -198,7 +198,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
|||||||
private void logResponse(SysLog sysLog, HttpServletResponse response) {
|
private void logResponse(SysLog sysLog, HttpServletResponse response) {
|
||||||
int status = response.getStatus();
|
int status = response.getStatus();
|
||||||
sysLog.setStatusCode(status);
|
sysLog.setStatusCode(status);
|
||||||
sysLog.setResponseHeader(this.desensitize(ServletUtil.getHeadersMap(response)));
|
sysLog.setResponseHeaders(this.desensitize(ServletUtil.getHeadersMap(response)));
|
||||||
// 响应体(不记录非 JSON 响应数据)
|
// 响应体(不记录非 JSON 响应数据)
|
||||||
String responseBody = this.getResponseBody(response);
|
String responseBody = this.getResponseBody(response);
|
||||||
if (StrUtil.isNotBlank(responseBody) && JSONUtil.isTypeJSON(responseBody)) {
|
if (StrUtil.isNotBlank(responseBody) && JSONUtil.isTypeJSON(responseBody)) {
|
||||||
|
@ -62,7 +62,7 @@ public class SysLog implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 请求头
|
* 请求头
|
||||||
*/
|
*/
|
||||||
private String requestHeader;
|
private String requestHeaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求体
|
* 请求体
|
||||||
@ -77,7 +77,7 @@ public class SysLog implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 响应头
|
* 响应头
|
||||||
*/
|
*/
|
||||||
private String responseHeader;
|
private String responseHeaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 响应体
|
* 响应体
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.cnadmin.monitor.model.query;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
import org.springdoc.api.annotations.ParameterObject;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import top.charles7c.cnadmin.common.annotation.Query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统日志查询条件
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 2023/1/17 23:31
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ParameterObject
|
||||||
|
@Schema(description = "系统日志查询条件")
|
||||||
|
public class SystemLogQuery implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@Query(type = Query.Type.BETWEEN)
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private List<Date> createTime;
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.cnadmin.monitor.model.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统日志详情信息
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 2023/1/18 20:19
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "系统日志详情信息")
|
||||||
|
public class SystemLogDetailVO extends LogVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志ID")
|
||||||
|
private Long logId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志描述
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求URL
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求URL")
|
||||||
|
private String requestUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求方式
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求方式")
|
||||||
|
private String requestMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求头
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求头")
|
||||||
|
private String requestHeaders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求体
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求体")
|
||||||
|
private String requestBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态码
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态码")
|
||||||
|
private Integer statusCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应头
|
||||||
|
*/
|
||||||
|
@Schema(description = "响应头")
|
||||||
|
private String responseHeaders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应体
|
||||||
|
*/
|
||||||
|
@Schema(description = "响应体")
|
||||||
|
private String responseBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求耗时(ms)
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求耗时(ms)")
|
||||||
|
private Long elapsedTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端IP
|
||||||
|
*/
|
||||||
|
@Schema(description = "客户端IP")
|
||||||
|
private String clientIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP归属地
|
||||||
|
*/
|
||||||
|
@Schema(description = "IP归属地")
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览器
|
||||||
|
*/
|
||||||
|
@Schema(description = "浏览器")
|
||||||
|
private String browser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.cnadmin.monitor.model.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统日志信息
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 2023/1/17 23:29
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "系统日志信息")
|
||||||
|
public class SystemLogVO extends LogVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志ID")
|
||||||
|
private Long logId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志描述
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态码
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态码")
|
||||||
|
private Integer statusCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求方式
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求方式")
|
||||||
|
private String requestMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求URL
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求URL")
|
||||||
|
private String requestUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求耗时(ms)
|
||||||
|
*/
|
||||||
|
@Schema(description = "请求耗时(ms)")
|
||||||
|
private Long elapsedTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端IP
|
||||||
|
*/
|
||||||
|
@Schema(description = "客户端IP")
|
||||||
|
private String clientIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP归属地
|
||||||
|
*/
|
||||||
|
@Schema(description = "IP归属地")
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览器
|
||||||
|
*/
|
||||||
|
@Schema(description = "浏览器")
|
||||||
|
private String browser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
@Schema(description = "错误信息")
|
||||||
|
private String errorMsg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常详情
|
||||||
|
*/
|
||||||
|
@Schema(description = "异常详情")
|
||||||
|
private String exceptionDetail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
@ -20,8 +20,11 @@ import top.charles7c.cnadmin.common.model.query.PageQuery;
|
|||||||
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||||
import top.charles7c.cnadmin.monitor.model.query.LoginLogQuery;
|
import top.charles7c.cnadmin.monitor.model.query.LoginLogQuery;
|
||||||
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
||||||
|
import top.charles7c.cnadmin.monitor.model.query.SystemLogQuery;
|
||||||
import top.charles7c.cnadmin.monitor.model.vo.LoginLogVO;
|
import top.charles7c.cnadmin.monitor.model.vo.LoginLogVO;
|
||||||
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
||||||
|
import top.charles7c.cnadmin.monitor.model.vo.SystemLogDetailVO;
|
||||||
|
import top.charles7c.cnadmin.monitor.model.vo.SystemLogVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统日志业务接口
|
* 系统日志业务接口
|
||||||
@ -52,4 +55,24 @@ public interface LogService {
|
|||||||
* @return 登录日志分页信息
|
* @return 登录日志分页信息
|
||||||
*/
|
*/
|
||||||
PageInfo<LoginLogVO> list(LoginLogQuery query, PageQuery pageQuery);
|
PageInfo<LoginLogVO> list(LoginLogQuery query, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询系统日志列表
|
||||||
|
*
|
||||||
|
* @param query
|
||||||
|
* 查询条件
|
||||||
|
* @param pageQuery
|
||||||
|
* 分页查询条件
|
||||||
|
* @return 系统日志分页信息
|
||||||
|
*/
|
||||||
|
PageInfo<SystemLogVO> list(SystemLogQuery query, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看系统日志详情
|
||||||
|
*
|
||||||
|
* @param logId
|
||||||
|
* 日志ID
|
||||||
|
* @return 系统日志详情
|
||||||
|
*/
|
||||||
|
SystemLogDetailVO detail(Long logId);
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import top.charles7c.cnadmin.common.model.query.PageQuery;
|
import top.charles7c.cnadmin.common.model.query.PageQuery;
|
||||||
@ -37,13 +38,13 @@ import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
|||||||
import top.charles7c.cnadmin.common.util.ExceptionUtils;
|
import top.charles7c.cnadmin.common.util.ExceptionUtils;
|
||||||
import top.charles7c.cnadmin.common.util.ReflectUtils;
|
import top.charles7c.cnadmin.common.util.ReflectUtils;
|
||||||
import top.charles7c.cnadmin.common.util.helper.QueryHelper;
|
import top.charles7c.cnadmin.common.util.helper.QueryHelper;
|
||||||
|
import top.charles7c.cnadmin.common.util.validate.ValidationUtils;
|
||||||
import top.charles7c.cnadmin.monitor.mapper.LogMapper;
|
import top.charles7c.cnadmin.monitor.mapper.LogMapper;
|
||||||
import top.charles7c.cnadmin.monitor.model.entity.SysLog;
|
import top.charles7c.cnadmin.monitor.model.entity.SysLog;
|
||||||
import top.charles7c.cnadmin.monitor.model.query.LoginLogQuery;
|
import top.charles7c.cnadmin.monitor.model.query.LoginLogQuery;
|
||||||
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
||||||
import top.charles7c.cnadmin.monitor.model.vo.LogVO;
|
import top.charles7c.cnadmin.monitor.model.query.SystemLogQuery;
|
||||||
import top.charles7c.cnadmin.monitor.model.vo.LoginLogVO;
|
import top.charles7c.cnadmin.monitor.model.vo.*;
|
||||||
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
|
||||||
import top.charles7c.cnadmin.monitor.service.LogService;
|
import top.charles7c.cnadmin.monitor.service.LogService;
|
||||||
import top.charles7c.cnadmin.system.service.UserService;
|
import top.charles7c.cnadmin.system.service.UserService;
|
||||||
|
|
||||||
@ -94,7 +95,8 @@ public class LogServiceImpl implements LogService {
|
|||||||
@Override
|
@Override
|
||||||
public PageInfo<LoginLogVO> list(LoginLogQuery query, PageQuery pageQuery) {
|
public PageInfo<LoginLogVO> list(LoginLogQuery query, PageQuery pageQuery) {
|
||||||
QueryWrapper<SysLog> queryWrapper = QueryHelper.build(query);
|
QueryWrapper<SysLog> queryWrapper = QueryHelper.build(query);
|
||||||
queryWrapper.and(qw -> qw.like("request_url", "/auth/login").or().like("request_url", "/auth/logout"));
|
queryWrapper.lambda()
|
||||||
|
.and(qw -> qw.like(SysLog::getRequestUrl, "/auth/login").or().like(SysLog::getRequestUrl, "/auth/logout"));
|
||||||
|
|
||||||
// 限定查询信息
|
// 限定查询信息
|
||||||
String[] fieldsName = ReflectUtils.getNonStaticFieldsName(LoginLogVO.class);
|
String[] fieldsName = ReflectUtils.getNonStaticFieldsName(LoginLogVO.class);
|
||||||
@ -111,6 +113,35 @@ public class LogServiceImpl implements LogService {
|
|||||||
return pageInfo;
|
return pageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageInfo<SystemLogVO> list(SystemLogQuery query, PageQuery pageQuery) {
|
||||||
|
QueryWrapper<SysLog> queryWrapper = QueryHelper.build(query);
|
||||||
|
|
||||||
|
// 限定查询信息
|
||||||
|
String[] fieldsName = ReflectUtils.getNonStaticFieldsName(SystemLogVO.class);
|
||||||
|
List<String> columns = Arrays.stream(fieldsName).map(StrUtil::toUnderlineCase)
|
||||||
|
.filter(n -> !n.endsWith("string")).collect(Collectors.toList());
|
||||||
|
queryWrapper.select(columns);
|
||||||
|
|
||||||
|
// 分页查询
|
||||||
|
IPage<SysLog> page = logMapper.selectPage(pageQuery.toPage(), queryWrapper);
|
||||||
|
PageInfo<SystemLogVO> pageInfo = PageInfo.build(page, SystemLogVO.class);
|
||||||
|
|
||||||
|
// 填充数据
|
||||||
|
pageInfo.getList().forEach(this::fill);
|
||||||
|
return pageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemLogDetailVO detail(Long logId) {
|
||||||
|
SysLog sysLog = logMapper.selectById(logId);
|
||||||
|
ValidationUtils.exIfNull(sysLog, String.format("ID为 [%s] 的日志已不存在", logId));
|
||||||
|
|
||||||
|
SystemLogDetailVO detailVO = BeanUtil.copyProperties(sysLog, SystemLogDetailVO.class);
|
||||||
|
this.fill(detailVO);
|
||||||
|
return detailVO;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 填充数据
|
* 填充数据
|
||||||
*
|
*
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-echarts": "^6.3.3",
|
"vue-echarts": "^6.3.3",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
|
"vue-json-pretty": "^2.2.3",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
59
continew-admin-ui/src/api/monitor/system-log.ts
Normal file
59
continew-admin-ui/src/api/monitor/system-log.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import qs from 'query-string';
|
||||||
|
|
||||||
|
export interface SystemLogRecord {
|
||||||
|
logId: string;
|
||||||
|
statusCode: number;
|
||||||
|
requestMethod: string;
|
||||||
|
requestUrl: string;
|
||||||
|
elapsedTime: number;
|
||||||
|
clientIp: string;
|
||||||
|
location: string;
|
||||||
|
browser: string;
|
||||||
|
errorMsg: string;
|
||||||
|
exceptionDetail?: string;
|
||||||
|
createUserString: string;
|
||||||
|
createTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemLogParams extends Partial<SystemLogRecord> {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
sort: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemLogListRes {
|
||||||
|
list: SystemLogRecord[];
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function querySystemLogList(params: SystemLogParams) {
|
||||||
|
return axios.get<SystemLogListRes>('/monitor/log/system', {
|
||||||
|
params,
|
||||||
|
paramsSerializer: (obj) => {
|
||||||
|
return qs.stringify(obj);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemLogDetailRecord {
|
||||||
|
logId: string;
|
||||||
|
description: string;
|
||||||
|
requestUrl: string;
|
||||||
|
requestMethod: string;
|
||||||
|
requestHeaders: string;
|
||||||
|
requestBody: string;
|
||||||
|
statusCode: number;
|
||||||
|
responseHeaders: string;
|
||||||
|
responseBody: string;
|
||||||
|
elapsedTime: number;
|
||||||
|
clientIp: string;
|
||||||
|
location: string;
|
||||||
|
browser: string;
|
||||||
|
createUserString: string;
|
||||||
|
createTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function querySystemLogDetail(logId: string) {
|
||||||
|
return axios.get<SystemLogDetailRecord>(`/monitor/log/system/${logId}`);
|
||||||
|
}
|
@ -8,8 +8,9 @@ import localeMonitor from '@/views/dashboard/monitor/locale/en-US';
|
|||||||
import localeDataAnalysis from '@/views/visualization/data-analysis/locale/en-US';
|
import localeDataAnalysis from '@/views/visualization/data-analysis/locale/en-US';
|
||||||
import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/en-US';
|
import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/en-US';
|
||||||
|
|
||||||
import localeOperationLog from '@/views/monitor/log/operation/locale/en-US';
|
|
||||||
import localeLoginLog from '@/views/monitor/log/login/locale/en-US';
|
import localeLoginLog from '@/views/monitor/log/login/locale/en-US';
|
||||||
|
import localeOperationLog from '@/views/monitor/log/operation/locale/en-US';
|
||||||
|
import localeSystemLog from '@/views/monitor/log/system/locale/en-US';
|
||||||
|
|
||||||
import localeSearchTable from '@/views/list/search-table/locale/en-US';
|
import localeSearchTable from '@/views/list/search-table/locale/en-US';
|
||||||
import localeCardList from '@/views/list/card/locale/en-US';
|
import localeCardList from '@/views/list/card/locale/en-US';
|
||||||
@ -56,8 +57,9 @@ export default {
|
|||||||
...localeDataAnalysis,
|
...localeDataAnalysis,
|
||||||
...localeMultiDAnalysis,
|
...localeMultiDAnalysis,
|
||||||
|
|
||||||
...localeOperationLog,
|
|
||||||
...localeLoginLog,
|
...localeLoginLog,
|
||||||
|
...localeOperationLog,
|
||||||
|
...localeSystemLog,
|
||||||
|
|
||||||
...localeSearchTable,
|
...localeSearchTable,
|
||||||
...localeCardList,
|
...localeCardList,
|
||||||
|
@ -8,8 +8,9 @@ import localeMonitor from '@/views/dashboard/monitor/locale/zh-CN';
|
|||||||
import localeDataAnalysis from '@/views/visualization/data-analysis/locale/zh-CN';
|
import localeDataAnalysis from '@/views/visualization/data-analysis/locale/zh-CN';
|
||||||
import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/zh-CN';
|
import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/zh-CN';
|
||||||
|
|
||||||
import localeOperationLog from '@/views/monitor/log/operation/locale/zh-CN';
|
|
||||||
import localeLoginLog from '@/views/monitor/log/login/locale/zh-CN';
|
import localeLoginLog from '@/views/monitor/log/login/locale/zh-CN';
|
||||||
|
import localeOperationLog from '@/views/monitor/log/operation/locale/zh-CN';
|
||||||
|
import localeSystemLog from '@/views/monitor/log/system/locale/zh-CN';
|
||||||
|
|
||||||
import localeSearchTable from '@/views/list/search-table/locale/zh-CN';
|
import localeSearchTable from '@/views/list/search-table/locale/zh-CN';
|
||||||
import localeCardList from '@/views/list/card/locale/zh-CN';
|
import localeCardList from '@/views/list/card/locale/zh-CN';
|
||||||
@ -56,8 +57,9 @@ export default {
|
|||||||
...localeDataAnalysis,
|
...localeDataAnalysis,
|
||||||
...localeMultiDAnalysis,
|
...localeMultiDAnalysis,
|
||||||
|
|
||||||
...localeOperationLog,
|
|
||||||
...localeLoginLog,
|
...localeLoginLog,
|
||||||
|
...localeOperationLog,
|
||||||
|
...localeSystemLog,
|
||||||
|
|
||||||
...localeSearchTable,
|
...localeSearchTable,
|
||||||
...localeCardList,
|
...localeCardList,
|
||||||
|
@ -12,6 +12,16 @@ const Monitor: AppRouteRecordRaw = {
|
|||||||
order: 2,
|
order: 2,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'log/login',
|
||||||
|
name: 'LoginLog',
|
||||||
|
component: () => import('@/views/monitor/log/login/index.vue'),
|
||||||
|
meta: {
|
||||||
|
locale: 'menu.log.login.list',
|
||||||
|
requiresAuth: true,
|
||||||
|
roles: ['*'],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'log/operation',
|
path: 'log/operation',
|
||||||
name: 'OperationLog',
|
name: 'OperationLog',
|
||||||
@ -23,11 +33,11 @@ const Monitor: AppRouteRecordRaw = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'log/login',
|
path: 'log/system',
|
||||||
name: 'LoginLog',
|
name: 'SystemLog',
|
||||||
component: () => import('@/views/monitor/log/login/index.vue'),
|
component: () => import('@/views/monitor/log/system/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.log.login.list',
|
locale: 'menu.log.system.list',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
roles: ['*'],
|
roles: ['*'],
|
||||||
},
|
},
|
||||||
|
@ -77,9 +77,6 @@
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template #operations>
|
|
||||||
<a-button v-permission="['admin']" type="text" size="small">详情</a-button>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
|
417
continew-admin-ui/src/views/monitor/log/system/index.vue
Normal file
417
continew-admin-ui/src/views/monitor/log/system/index.vue
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<Breadcrumb :items="['menu.monitor', 'menu.log.system.list']" />
|
||||||
|
<a-card class="general-card" :title="$t('menu.log.system.list')">
|
||||||
|
<!-- 查询 -->
|
||||||
|
<a-row style="margin-bottom: 15px">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form ref="queryFormRef" :model="queryFormData" layout="inline">
|
||||||
|
<a-form-item field="createTime" hide-label>
|
||||||
|
<a-range-picker
|
||||||
|
v-model="queryFormData.createTime"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
show-time
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-button type="primary" @click="toQuery">
|
||||||
|
<template #icon>
|
||||||
|
<icon-search />
|
||||||
|
</template>
|
||||||
|
查询
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="resetQuery">
|
||||||
|
<template #icon>
|
||||||
|
<icon-refresh />
|
||||||
|
</template>
|
||||||
|
重置
|
||||||
|
</a-button>
|
||||||
|
</a-form>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<a-table
|
||||||
|
row-key="logId"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
:columns="columns"
|
||||||
|
:data="renderData"
|
||||||
|
:bordered="false"
|
||||||
|
:stripe="true"
|
||||||
|
size="large"
|
||||||
|
@page-change="onPageChange"
|
||||||
|
>
|
||||||
|
<template #index="{ rowIndex }">
|
||||||
|
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
|
||||||
|
</template>
|
||||||
|
<template #statusCode="{ record }">
|
||||||
|
<a-space v-if="record.statusCode >= 400">
|
||||||
|
<a-tag color="red">{{ record.statusCode }}</a-tag>
|
||||||
|
</a-space>
|
||||||
|
<a-space v-else-if="record.statusCode === 200">
|
||||||
|
<a-tag color="green">{{ record.statusCode }}</a-tag>
|
||||||
|
</a-space>
|
||||||
|
<a-space v-else>
|
||||||
|
<a-tag color="orange">{{ record.statusCode }}</a-tag>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template #requestUrl="{ record }">
|
||||||
|
<a-space :title="decodeURIComponent(record.requestUrl)">{{ record.requestUrl.match(/(\w+):\/\/([^/:]+)(:\d*)?([^#|\?|\n]*)(\?.*)?/)[4] }}</a-space>
|
||||||
|
</template>
|
||||||
|
<template #elapsedTime="{ record }">
|
||||||
|
<a-space v-if="record.elapsedTime > 500">
|
||||||
|
<a-tag color="red">{{ record.elapsedTime }} ms</a-tag>
|
||||||
|
</a-space>
|
||||||
|
<a-space v-else-if="record.elapsedTime > 200">
|
||||||
|
<a-tag color="orange">{{ record.elapsedTime }} ms</a-tag>
|
||||||
|
</a-space>
|
||||||
|
<a-space v-else>
|
||||||
|
<a-tag color="green">{{ record.elapsedTime }} ms</a-tag>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template #operations="{ record }">
|
||||||
|
<a-button v-permission="['admin']" type="text" size="small" @click="handleClick(record.logId)">详情</a-button>
|
||||||
|
<a-button v-if="record.exceptionDetail" v-permission="['admin']" type="text" size="small" @click="handleExceptionDetail(record)">异常详情</a-button>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<!-- 窗口 -->
|
||||||
|
<a-drawer
|
||||||
|
:width="570"
|
||||||
|
:visible="visible"
|
||||||
|
:footer="false"
|
||||||
|
unmount-on-close
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<template #title>日志详情</template>
|
||||||
|
<div style="margin: 10px 0 0 10px">
|
||||||
|
<a-descriptions title="基础信息" :column="2" bordered>
|
||||||
|
<a-descriptions-item label="客户端IP">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :widths="['200px']" :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>{{ renderDetailData.clientIp }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="浏览器">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :widths="['200px']" :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>{{ renderDetailData.browser }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="IP归属地">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :widths="['200px']" :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>{{ renderDetailData.location }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求耗时">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :widths="['200px']" :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>
|
||||||
|
<a-tag v-if="renderDetailData.elapsedTime > 500" color="red">
|
||||||
|
{{ renderDetailData.elapsedTime }} ms
|
||||||
|
</a-tag>
|
||||||
|
<a-tag v-else-if="renderDetailData.elapsedTime > 200" color="orange">
|
||||||
|
{{ renderDetailData.elapsedTime }} ms
|
||||||
|
</a-tag>
|
||||||
|
<a-tag v-else color="green">{{ renderDetailData.elapsedTime }} ms</a-tag>
|
||||||
|
</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="创建时间">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :widths="['200px']" :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>{{ renderDetailData.createTime }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
<a-descriptions
|
||||||
|
title="协议信息"
|
||||||
|
style="margin-top: 25px"
|
||||||
|
:column="2"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<a-descriptions-item label="状态码">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>
|
||||||
|
<a-tag v-if="renderDetailData.statusCode >= 400" color="red">{{ renderDetailData.statusCode }}</a-tag>
|
||||||
|
<a-tag v-else-if="renderDetailData.statusCode === 200" color="green">{{ renderDetailData.statusCode }}</a-tag>
|
||||||
|
<a-tag v-else color="orange">{{ renderDetailData.statusCode }}</a-tag>
|
||||||
|
</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求方式">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>{{ renderDetailData.requestMethod }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求URL" :span="2">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :rows="1" />
|
||||||
|
</a-skeleton>
|
||||||
|
<span v-else>{{ renderDetailData.requestUrl }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="响应体" :span="2">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :rows="3" />
|
||||||
|
</a-skeleton>
|
||||||
|
<a-space v-else>
|
||||||
|
<VueJsonPretty
|
||||||
|
v-if="renderDetailData.responseBody"
|
||||||
|
:path="'res'"
|
||||||
|
:data="JSON.parse(renderDetailData.responseBody)"
|
||||||
|
:show-length="true" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-space>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="响应头" :span="2">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :rows="3" />
|
||||||
|
</a-skeleton>
|
||||||
|
<a-space v-else>
|
||||||
|
<VueJsonPretty
|
||||||
|
v-if="renderDetailData.responseHeaders"
|
||||||
|
:path="'res'"
|
||||||
|
:data="JSON.parse(renderDetailData.responseHeaders)"
|
||||||
|
:show-length="true" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-space>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求体" :span="2">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :rows="3" />
|
||||||
|
</a-skeleton>
|
||||||
|
<a-space v-else>
|
||||||
|
<VueJsonPretty
|
||||||
|
v-if="renderDetailData.requestBody"
|
||||||
|
:path="'res'"
|
||||||
|
:data="JSON.parse(renderDetailData.requestBody)"
|
||||||
|
:show-length="true" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-space>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求头" :span="2">
|
||||||
|
<a-skeleton v-if="detailLoading" :animation="true">
|
||||||
|
<a-skeleton-line :rows="3" />
|
||||||
|
</a-skeleton>
|
||||||
|
<a-space v-else>
|
||||||
|
<VueJsonPretty
|
||||||
|
v-if="renderDetailData.requestHeaders"
|
||||||
|
:path="'res'"
|
||||||
|
:data="JSON.parse(renderDetailData.requestHeaders)"
|
||||||
|
:show-length="true" />
|
||||||
|
<span v-else>无</span>
|
||||||
|
</a-space>
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
title="异常详情"
|
||||||
|
render-to-body
|
||||||
|
top="30px"
|
||||||
|
width="83%"
|
||||||
|
:visible="exceptionDetailVisible"
|
||||||
|
:footer="false"
|
||||||
|
unmount-on-close
|
||||||
|
@ok="handleExceptionDetailOk"
|
||||||
|
@cancel="handleExceptionDetailCancel"
|
||||||
|
>
|
||||||
|
<pre>{{ exceptionDetail }}</pre>
|
||||||
|
</a-modal>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref, reactive } from 'vue';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import {
|
||||||
|
querySystemLogDetail,
|
||||||
|
SystemLogDetailRecord,
|
||||||
|
querySystemLogList,
|
||||||
|
SystemLogRecord,
|
||||||
|
SystemLogParams,
|
||||||
|
} from '@/api/monitor/system-log';
|
||||||
|
import { Pagination } from '@/types/global';
|
||||||
|
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
import { FormInstance } from '@arco-design/web-vue/es/form';
|
||||||
|
import VueJsonPretty from 'vue-json-pretty';
|
||||||
|
import 'vue-json-pretty/lib/styles.css';
|
||||||
|
|
||||||
|
const { loading, setLoading } = useLoading(true);
|
||||||
|
const visible = ref(false);
|
||||||
|
const exceptionDetailVisible = ref(false);
|
||||||
|
const detailLoading = ref(false);
|
||||||
|
const exceptionDetail = ref();
|
||||||
|
const queryFormRef = ref<FormInstance>();
|
||||||
|
const renderData = ref<SystemLogRecord[]>([]);
|
||||||
|
const renderDetailData = ref<SystemLogDetailRecord>({
|
||||||
|
logId: '',
|
||||||
|
description: '',
|
||||||
|
requestUrl: '',
|
||||||
|
requestMethod: '',
|
||||||
|
requestHeaders: '',
|
||||||
|
requestBody: '',
|
||||||
|
statusCode: 200,
|
||||||
|
responseHeaders: '',
|
||||||
|
responseBody: '',
|
||||||
|
elapsedTime: 0,
|
||||||
|
clientIp: '',
|
||||||
|
location: '',
|
||||||
|
browser: '',
|
||||||
|
createUserString: '',
|
||||||
|
createTime: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryFormData = ref({
|
||||||
|
createTime: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const basePagination: Pagination = {
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
};
|
||||||
|
const pagination = reactive({
|
||||||
|
...basePagination,
|
||||||
|
});
|
||||||
|
const columns = computed<TableColumnData[]>(() => [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'index',
|
||||||
|
slotName: 'index',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态码',
|
||||||
|
dataIndex: 'statusCode',
|
||||||
|
slotName: 'statusCode',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '请求方式',
|
||||||
|
dataIndex: 'requestMethod',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '请求URI',
|
||||||
|
dataIndex: 'requestUrl',
|
||||||
|
slotName: 'requestUrl',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '客户端IP',
|
||||||
|
dataIndex: 'clientIp',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'IP归属地',
|
||||||
|
dataIndex: 'location',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '浏览器',
|
||||||
|
dataIndex: 'browser',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '请求耗时',
|
||||||
|
dataIndex: 'elapsedTime',
|
||||||
|
slotName: 'elapsedTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'operations',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 查询列表
|
||||||
|
const fetchData = async (
|
||||||
|
params: SystemLogParams = { page: 1, size: 10, sort: ['createTime,desc'] }
|
||||||
|
) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { data } = await querySystemLogList(params);
|
||||||
|
renderData.value = data.list;
|
||||||
|
pagination.current = params.page;
|
||||||
|
pagination.total = data.total;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPageChange = (current: number) => {
|
||||||
|
fetchData({ page: current, size: pagination.pageSize, sort: ['createTime,desc'] });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const toQuery = () => {
|
||||||
|
fetchData({
|
||||||
|
page: pagination.current,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
sort: ['createTime,desc'],
|
||||||
|
...queryFormData.value,
|
||||||
|
} as unknown as SystemLogParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const resetQuery = async () => {
|
||||||
|
await queryFormRef.value?.resetFields();
|
||||||
|
await fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
const handleClick = async (logId: string) => {
|
||||||
|
visible.value = true;
|
||||||
|
detailLoading.value = true;
|
||||||
|
try {
|
||||||
|
const { data } = await querySystemLogDetail(logId);
|
||||||
|
renderDetailData.value = data;
|
||||||
|
} finally {
|
||||||
|
detailLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleOk = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
const handleCancel = () => {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
|
||||||
|
// 查看异常详情
|
||||||
|
const handleExceptionDetail = async (record: SystemLogRecord) => {
|
||||||
|
exceptionDetailVisible.value = true;
|
||||||
|
exceptionDetail.value = record.exceptionDetail;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExceptionDetailOk = () => {
|
||||||
|
exceptionDetailVisible.value = false;
|
||||||
|
exceptionDetail.value = null;
|
||||||
|
};
|
||||||
|
const handleExceptionDetailCancel = () => {
|
||||||
|
exceptionDetailVisible.value = false;
|
||||||
|
exceptionDetail.value = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'SystemLog',
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.container {
|
||||||
|
padding: 0 20px 20px 20px;
|
||||||
|
}
|
||||||
|
:deep(.arco-table-th) {
|
||||||
|
&:last-child {
|
||||||
|
.arco-table-th-item-title {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
'menu.log.system.list': 'System log',
|
||||||
|
};
|
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
'menu.log.system.list': '系统日志',
|
||||||
|
};
|
@ -8117,6 +8117,11 @@ vue-i18n@^9.2.2:
|
|||||||
"@intlify/vue-devtools" "9.2.2"
|
"@intlify/vue-devtools" "9.2.2"
|
||||||
"@vue/devtools-api" "^6.2.1"
|
"@vue/devtools-api" "^6.2.1"
|
||||||
|
|
||||||
|
vue-json-pretty@^2.2.3:
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.npmmirror.com/vue-json-pretty/-/vue-json-pretty-2.2.3.tgz#7cf5c5af464ed5600c1883b6310aa848a8840a4d"
|
||||||
|
integrity sha512-tJo+4eFclQBt3gJ6EELXNdvo50wDKTZYthwmLpy9YS7UDldeJln5ff+IpdmUglfk+FqLVOX/re0+Ni/EOUPZgw==
|
||||||
|
|
||||||
vue-router@^4.1.6:
|
vue-router@^4.1.6:
|
||||||
version "4.1.6"
|
version "4.1.6"
|
||||||
resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1"
|
resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1"
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.cnadmin.webapi.controller.monitor;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import top.charles7c.cnadmin.common.model.query.PageQuery;
|
||||||
|
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||||
|
import top.charles7c.cnadmin.common.model.vo.R;
|
||||||
|
import top.charles7c.cnadmin.monitor.model.query.SystemLogQuery;
|
||||||
|
import top.charles7c.cnadmin.monitor.model.vo.SystemLogDetailVO;
|
||||||
|
import top.charles7c.cnadmin.monitor.model.vo.SystemLogVO;
|
||||||
|
import top.charles7c.cnadmin.monitor.service.LogService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统日志 API
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 2023/1/17 23:27
|
||||||
|
*/
|
||||||
|
@Tag(name = "操作日志 API")
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping(value = "/monitor/log/system", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public class SystemLogController {
|
||||||
|
|
||||||
|
private final LogService logService;
|
||||||
|
|
||||||
|
@Operation(summary = "分页查询系统日志列表")
|
||||||
|
@GetMapping
|
||||||
|
public R<PageInfo<SystemLogVO>> list(@Validated SystemLogQuery query, @Validated PageQuery pageQuery) {
|
||||||
|
PageInfo<SystemLogVO> pageInfo = logService.list(query, pageQuery);
|
||||||
|
return R.ok(pageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "系统日志列表")
|
||||||
|
@GetMapping("/{logId}")
|
||||||
|
public R<SystemLogDetailVO> detail(@PathVariable Long logId) {
|
||||||
|
SystemLogDetailVO detailVO = logService.detail(logId);
|
||||||
|
return R.ok(detailVO);
|
||||||
|
}
|
||||||
|
}
|
@ -29,10 +29,10 @@ CREATE TABLE IF NOT EXISTS `sys_log` (
|
|||||||
`description` varchar(255) DEFAULT NULL COMMENT '日志描述',
|
`description` varchar(255) DEFAULT NULL COMMENT '日志描述',
|
||||||
`request_url` varchar(512) NOT NULL DEFAULT '' COMMENT '请求URL',
|
`request_url` varchar(512) NOT NULL DEFAULT '' COMMENT '请求URL',
|
||||||
`request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',
|
`request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',
|
||||||
`request_header` text COMMENT '请求头',
|
`request_headers` text COMMENT '请求头',
|
||||||
`request_body` text DEFAULT NULL COMMENT '请求体',
|
`request_body` text DEFAULT NULL COMMENT '请求体',
|
||||||
`status_code` int(11) unsigned DEFAULT NULL COMMENT '状态码',
|
`status_code` int(11) unsigned DEFAULT NULL COMMENT '状态码',
|
||||||
`response_header` text DEFAULT NULL COMMENT '响应头',
|
`response_headers` text DEFAULT NULL COMMENT '响应头',
|
||||||
`response_body` mediumtext DEFAULT NULL COMMENT '响应体',
|
`response_body` mediumtext DEFAULT NULL COMMENT '响应体',
|
||||||
`elapsed_time` bigint(20) unsigned DEFAULT NULL COMMENT '请求耗时(ms)',
|
`elapsed_time` bigint(20) unsigned DEFAULT NULL COMMENT '请求耗时(ms)',
|
||||||
`status` tinyint(1) unsigned DEFAULT 1 COMMENT '操作状态(1成功 2失败)',
|
`status` tinyint(1) unsigned DEFAULT 1 COMMENT '操作状态(1成功 2失败)',
|
||||||
|
Loading…
Reference in New Issue
Block a user