feat: 新增异常日志收集功能,mediabot 可提交运行时异常,管理端可查看

This commit is contained in:
mediabot-pt
2026-07-01 00:14:07 +08:00
parent 3972f00e25
commit a45650f480
12 changed files with 439 additions and 1 deletions

View File

@@ -33,7 +33,7 @@ public class WebConfig implements WebMvcConfigurer {
// HMAC 鉴权拦截器:拦截所有写入端点
registry.addInterceptor(hmacAuthInterceptor)
.addPathPatterns("/api/v1/configs/*/submit", "/api/v1/sites/*/submit",
"/api/v1/sites/*/access-result",
"/api/v1/sites/*/access-result", "/api/v1/sites/*/error-log",
"/api/v1/admin/**", "/api/v1/account/**");
}
}

View File

@@ -0,0 +1,22 @@
package com.par.core.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 异常日志 DTO
*/
@Data
public class ErrorLogDTO {
private Long id;
private String siteId;
private String clientId;
private String errorType;
private String message;
private String stackTrace;
private String configVersion;
private String url;
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,41 @@
package com.par.core.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 异常日志实体(mediabot 提交的运行时异常)
*/
@Data
@TableName("error_logs")
public class ErrorLog {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("site_id")
private String siteId;
@TableField("client_id")
private String clientId;
@TableField("error_type")
private String errorType;
@TableField("message")
private String message;
@TableField("stack_trace")
private String stackTrace;
@TableField("config_version")
private String configVersion;
@TableField("url")
private String url;
@TableField(value = "created_at", fill = FieldFill.INSERT)
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,22 @@
package com.par.core.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.par.core.entity.ErrorLog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 异常日志 Mapper
*/
@Mapper
public interface ErrorLogMapper extends BaseMapper<ErrorLog> {
@Select("SELECT * FROM error_logs WHERE site_id = #{siteId} ORDER BY created_at DESC")
List<ErrorLog> selectBySiteId(@Param("siteId") String siteId);
@Select("SELECT * FROM error_logs ORDER BY created_at DESC")
List<ErrorLog> selectAll();
}

View File

@@ -0,0 +1,38 @@
package com.par.core.service;
import com.par.core.dto.ErrorLogDTO;
import com.par.core.entity.ErrorLog;
import java.util.List;
/**
* 异常日志服务接口
*/
public interface ErrorLogService {
/**
* 提交异常日志(mediabot 调用)
*/
ErrorLog submit(String siteId, String clientId, String errorType, String message,
String stackTrace, String configVersion, String url);
/**
* 获取所有异常日志
*/
List<ErrorLogDTO> listAll();
/**
* 获取指定站点的异常日志
*/
List<ErrorLogDTO> listBySiteId(String siteId);
/**
* 获取单条详情
*/
ErrorLogDTO getById(Long id);
/**
* 实体转 DTO
*/
ErrorLogDTO toDTO(ErrorLog entity);
}

View File

@@ -0,0 +1,67 @@
package com.par.core.service.impl;
import com.par.core.dto.ErrorLogDTO;
import com.par.core.entity.ErrorLog;
import com.par.core.mapper.ErrorLogMapper;
import com.par.core.service.ErrorLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class ErrorLogServiceImpl implements ErrorLogService {
private final ErrorLogMapper mapper;
@Override
public ErrorLog submit(String siteId, String clientId, String errorType, String message,
String stackTrace, String configVersion, String url) {
ErrorLog logEntry = new ErrorLog();
logEntry.setSiteId(siteId);
logEntry.setClientId(clientId);
logEntry.setErrorType(errorType);
logEntry.setMessage(message);
logEntry.setStackTrace(stackTrace);
logEntry.setConfigVersion(configVersion);
logEntry.setUrl(url);
mapper.insert(logEntry);
log.info("Error log submitted: siteId={}, type={}, clientId={}, id={}", siteId, errorType, clientId, logEntry.getId());
return logEntry;
}
@Override
public List<ErrorLogDTO> listAll() {
return mapper.selectAll().stream().map(this::toDTO).collect(Collectors.toList());
}
@Override
public List<ErrorLogDTO> listBySiteId(String siteId) {
return mapper.selectBySiteId(siteId).stream().map(this::toDTO).collect(Collectors.toList());
}
@Override
public ErrorLogDTO getById(Long id) {
return toDTO(mapper.selectById(id));
}
@Override
public ErrorLogDTO toDTO(ErrorLog entity) {
if (entity == null) return null;
ErrorLogDTO dto = new ErrorLogDTO();
dto.setId(entity.getId());
dto.setSiteId(entity.getSiteId());
dto.setClientId(entity.getClientId());
dto.setErrorType(entity.getErrorType());
dto.setMessage(entity.getMessage());
dto.setStackTrace(entity.getStackTrace());
dto.setConfigVersion(entity.getConfigVersion());
dto.setUrl(entity.getUrl());
dto.setCreatedAt(entity.getCreatedAt());
return dto;
}
}

View File

@@ -0,0 +1,16 @@
-- PAR Flyway V12: 异常日志表(mediabot 提交运行时异常信息)
CREATE TABLE error_logs (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
site_id VARCHAR(64) COMMENT '站点ID',
client_id VARCHAR(128) COMMENT '客户端标识(mediabot ID)',
error_type VARCHAR(200) COMMENT '异常类型(类名)',
message TEXT COMMENT '异常消息',
stack_trace MEDIUMTEXT COMMENT '完整堆栈信息',
config_version VARCHAR(20) COMMENT '使用的配置版本号',
url VARCHAR(500) COMMENT '异常发生时的访问 URL',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发生时间',
PRIMARY KEY (id),
KEY idx_site_id (site_id),
KEY idx_error_type (error_type(50)),
KEY idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常日志表';