This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

50
blade-starter-log/pom.xml Normal file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-starter-log</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-cloud</artifactId>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
</dependency>
<!-- Logstash -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</dependency>
<!-- validator -->
<dependency>
<artifactId>hibernate-validator</artifactId>
<groupId>org.hibernate.validator</groupId>
</dependency>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,47 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.annotation;
import java.lang.annotation.*;
/**
* 操作日志注解
*
* @author Chill
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog {
/**
* 日志描述
*
* @return {String}
*/
String value() default "日志记录";
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.log.publisher.ApiLogPublisher;
/**
* 操作日志使用spring event异步入库
*
* @author Chill
*/
@Slf4j
@Aspect
public class ApiLogAspect {
@Around("@annotation(apiLog)")
public Object around(ProceedingJoinPoint point, ApiLog apiLog) throws Throwable {
//获取类名
String className = point.getTarget().getClass().getName();
//获取方法
String methodName = point.getSignature().getName();
// 发送异步日志事件
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//记录日志
ApiLogPublisher.publishEvent(methodName, className, apiLog, time);
return result;
}
}

View File

@@ -0,0 +1,55 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springblade.core.log.utils.LogTraceUtil;
/**
* 为异步方法添加traceId
*
* @author Chill
*/
@Aspect
public class LogTraceAspect {
@Pointcut("@annotation(org.springframework.scheduling.annotation.Async)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
try {
LogTraceUtil.insert();
return point.proceed();
} finally {
LogTraceUtil.remove();
}
}
}

View File

@@ -0,0 +1,280 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.log.aspect;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springblade.core.launch.log.BladeLogLevel;
import org.springblade.core.log.props.BladeRequestLogProperties;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.ClassUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.InputStreamSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Spring boot 控制器 请求日志,方便代码调试
*
* @author L.cm
*/
@Slf4j
@Aspect
@AutoConfiguration
@RequiredArgsConstructor
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(value = BladeLogLevel.REQ_LOG_PROPS_PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class RequestLogAspect {
private final BladeRequestLogProperties properties;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
/**
* AOP 环切 控制器 R 返回值
*
* @param point JoinPoint
* @return Object
* @throws Throwable 异常
*/
@Around(
"execution(!static org.springblade.core.tool.api.R *(..)) && " +
"(@within(org.springframework.stereotype.Controller) || " +
"@within(org.springframework.web.bind.annotation.RestController))"
)
public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
BladeLogLevel level = properties.getLevel();
// 不打印日志,直接返回
if (BladeLogLevel.NONE == level) {
return point.proceed();
}
HttpServletRequest request = WebUtil.getRequest();
String requestUrl = Objects.requireNonNull(request).getRequestURI();
String requestMethod = request.getMethod();
// 放行的接口不打印日志
if (isSkip(requestUrl)) {
return point.proceed();
}
// 构建成一条长 日志,避免并发下日志错乱
StringBuilder beforeReqLog = new StringBuilder(300);
// 日志参数
List<Object> beforeReqArgs = new ArrayList<>();
beforeReqLog.append("\n\n================ Request Start ================\n");
// 打印路由
beforeReqLog.append("===> {}: {}");
beforeReqArgs.add(requestMethod);
beforeReqArgs.add(requestUrl);
// 打印请求参数
logIngArgs(point, beforeReqLog, beforeReqArgs);
// 打印请求 headers
logIngHeaders(request, level, beforeReqLog, beforeReqArgs);
beforeReqLog.append("================ Request End ================\n");
// 打印执行时间
long startNs = System.nanoTime();
log.info(beforeReqLog.toString(), beforeReqArgs.toArray());
// aop 执行后的日志
StringBuilder afterReqLog = new StringBuilder(200);
// 日志参数
List<Object> afterReqArgs = new ArrayList<>();
afterReqLog.append("\n\n=============== Response Start ================\n");
try {
Object result = point.proceed();
// 打印返回结构体
if (BladeLogLevel.BODY.lte(level)) {
afterReqLog.append("===Result=== {}\n");
afterReqArgs.add(JsonUtil.toJson(result));
}
return result;
} finally {
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
afterReqLog.append("<=== {}: {} ({} ms)\n");
afterReqArgs.add(requestMethod);
afterReqArgs.add(requestUrl);
afterReqArgs.add(tookMs);
afterReqLog.append("=============== Response End ================\n");
log.info(afterReqLog.toString(), afterReqArgs.toArray());
}
}
/**
* 激励请求参数
*
* @param point ProceedingJoinPoint
* @param beforeReqLog StringBuilder
* @param beforeReqArgs beforeReqArgs
*/
public void logIngArgs(ProceedingJoinPoint point, StringBuilder beforeReqLog, List<Object> beforeReqArgs) {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
Object[] args = point.getArgs();
// 请求参数处理
final Map<String, Object> paraMap = new HashMap<>(16);
// 一次请求只能有一个 request body
Object requestBodyValue = null;
for (int i = 0; i < args.length; i++) {
// 读取方法参数
MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
// PathVariable 参数跳过
PathVariable pathVariable = methodParam.getParameterAnnotation(PathVariable.class);
if (pathVariable != null) {
continue;
}
RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class);
String parameterName = methodParam.getParameterName();
Object value = args[i];
// 如果是body的json则是对象
if (requestBody != null) {
requestBodyValue = value;
continue;
}
// 处理 参数
if (value instanceof HttpServletRequest) {
paraMap.putAll(((HttpServletRequest) value).getParameterMap());
continue;
} else if (value instanceof WebRequest) {
paraMap.putAll(((WebRequest) value).getParameterMap());
continue;
} else if (value instanceof HttpServletResponse) {
continue;
} else if (value instanceof MultipartFile) {
MultipartFile multipartFile = (MultipartFile) value;
String name = multipartFile.getName();
String fileName = multipartFile.getOriginalFilename();
paraMap.put(name, fileName);
continue;
} else if (value instanceof MultipartFile[]) {
MultipartFile[] arr = (MultipartFile[]) value;
if (arr.length == 0) {
continue;
}
String name = arr[0].getName();
StringBuilder sb = new StringBuilder(arr.length);
for (MultipartFile multipartFile : arr) {
sb.append(multipartFile.getOriginalFilename());
sb.append(StringPool.COMMA);
}
paraMap.put(name, StringUtil.removeSuffix(sb.toString(), StringPool.COMMA));
continue;
} else if (value instanceof List) {
List<?> list = (List<?>) value;
AtomicBoolean isSkip = new AtomicBoolean(false);
for (Object o : list) {
if ("StandardMultipartFile".equalsIgnoreCase(o.getClass().getSimpleName())) {
isSkip.set(true);
break;
}
}
if (isSkip.get()) {
paraMap.put(parameterName, "此参数不能序列化为json");
continue;
}
}
// 参数名
RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class);
String paraName = parameterName;
if (requestParam != null && StringUtil.isNotBlank(requestParam.value())) {
paraName = requestParam.value();
}
if (value == null) {
paraMap.put(paraName, null);
} else if (ClassUtil.isPrimitiveOrWrapper(value.getClass())) {
paraMap.put(paraName, value);
} else if (value instanceof InputStream) {
paraMap.put(paraName, "InputStream");
} else if (value instanceof InputStreamSource) {
paraMap.put(paraName, "InputStreamSource");
} else if (JsonUtil.canSerialize(value)) {
// 判断模型能被 json 序列化,则添加
paraMap.put(paraName, value);
} else {
paraMap.put(paraName, "此参数不能序列化为json");
}
}
// 请求参数
if (paraMap.isEmpty()) {
beforeReqLog.append("\n");
} else {
beforeReqLog.append(" Parameters: {}\n");
beforeReqArgs.add(JsonUtil.toJson(paraMap));
}
if (requestBodyValue != null) {
beforeReqLog.append("====Body===== {}\n");
beforeReqArgs.add(JsonUtil.toJson(requestBodyValue));
}
}
/**
* 记录请求头
*
* @param request HttpServletRequest
* @param level 日志级别
* @param beforeReqLog StringBuilder
* @param beforeReqArgs beforeReqArgs
*/
public void logIngHeaders(HttpServletRequest request, BladeLogLevel level,
StringBuilder beforeReqLog, List<Object> beforeReqArgs) {
// 打印请求头
if (BladeLogLevel.HEADERS.lte(level)) {
Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String headerName = headers.nextElement();
String headerValue = request.getHeader(headerName);
beforeReqLog.append("===Headers=== {}: {}\n");
beforeReqArgs.add(headerName);
beforeReqArgs.add(headerValue);
}
}
}
private boolean isSkip(String path) {
return properties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
}

View File

@@ -0,0 +1,73 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.config;
import lombok.AllArgsConstructor;
import org.springblade.core.log.error.BladeErrorAttributes;
import org.springblade.core.log.error.BladeErrorController;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;
import jakarta.servlet.Servlet;
/**
* 统一异常处理
*
* @author Chill
*/
@AllArgsConstructor
@ConditionalOnWebApplication
@AutoConfiguration(before = ErrorMvcAutoConfiguration.class)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
public class BladeErrorMvcAutoConfiguration {
private final ServerProperties serverProperties;
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new BladeErrorAttributes();
}
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BladeErrorController(errorAttributes, serverProperties.getError());
}
}

View File

@@ -0,0 +1,106 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.config;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.props.BladePropertySource;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.aspect.ApiLogAspect;
import org.springblade.core.log.aspect.LogTraceAspect;
import org.springblade.core.log.event.ApiLogListener;
import org.springblade.core.log.event.ErrorLogListener;
import org.springblade.core.log.event.UsualLogListener;
import org.springblade.core.log.feign.ILogClient;
import org.springblade.core.log.filter.LogTraceFilter;
import org.springblade.core.log.logger.BladeLogger;
import org.springblade.core.log.props.BladeRequestLogProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import jakarta.servlet.DispatcherType;
/**
* 日志工具自动配置
*
* @author Chill
*/
@AutoConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(BladeRequestLogProperties.class)
@BladePropertySource(value = "classpath:/blade-log.yml")
public class BladeLogToolAutoConfiguration {
@Bean
public ApiLogAspect apiLogAspect() {
return new ApiLogAspect();
}
@Bean
public LogTraceAspect logTraceAspect() {
return new LogTraceAspect();
}
@Bean
public BladeLogger bladeLogger() {
return new BladeLogger();
}
@Bean
public FilterRegistrationBean<LogTraceFilter> logTraceFilterRegistration() {
FilterRegistrationBean<LogTraceFilter> registration = new FilterRegistrationBean<>();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new LogTraceFilter());
registration.addUrlPatterns("/*");
registration.setName("LogTraceFilter");
registration.setOrder(Ordered.LOWEST_PRECEDENCE);
return registration;
}
@Bean
@ConditionalOnMissingBean(name = "apiLogListener")
public ApiLogListener apiLogListener(ILogClient logService, ServerInfo serverInfo, BladeProperties bladeProperties) {
return new ApiLogListener(logService, serverInfo, bladeProperties);
}
@Bean
@ConditionalOnMissingBean(name = "errorEventListener")
public ErrorLogListener errorEventListener(ILogClient logService, ServerInfo serverInfo, BladeProperties bladeProperties) {
return new ErrorLogListener(logService, serverInfo, bladeProperties);
}
@Bean
@ConditionalOnMissingBean(name = "usualEventListener")
public UsualLogListener usualEventListener(ILogClient logService, ServerInfo serverInfo, BladeProperties bladeProperties) {
return new UsualLogListener(logService, serverInfo, bladeProperties);
}
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.constant;
/**
* 事件常量
*
* @author Chill
*/
public interface EventConstant {
/**
* log
*/
String EVENT_LOG = "log";
/**
* request
*/
String EVENT_REQUEST = "request";
}

View File

@@ -0,0 +1,72 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.error;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.publisher.ErrorLogPublisher;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.utils.BeanUtil;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
/**
* 全局异常处理
*
* @author Chill
*/
@Slf4j
public class BladeErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
String requestUri = this.getAttr(webRequest, "jakarta.servlet.error.request_uri");
Integer status = this.getAttr(webRequest, "jakarta.servlet.error.status_code");
Throwable error = getError(webRequest);
R result;
if (error == null) {
log.error("URL:{} error status:{}", requestUri, status);
result = R.fail(ResultCode.FAILURE, "系统未知异常[HttpStatus]:" + status);
} else {
log.error(String.format("URL:%s error status:%d", requestUri, status), error);
result = R.fail(status, error.getMessage());
}
//发送服务异常事件
ErrorLogPublisher.publishEvent(error, requestUri);
return BeanUtil.toMap(result);
}
@Nullable
private <T> T getAttr(WebRequest webRequest, String name) {
return (T) webRequest.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
}
}

View File

@@ -0,0 +1,65 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.error;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* 更改html请求异常为ajax
*
* @author Chill
*/
public class BladeErrorController extends BasicErrorController {
public BladeErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
super(errorAttributes, errorProperties);
}
@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
Map<String, Object> body = getErrorAttributes(request, (includeStackTrace) ? ErrorAttributeOptions.of(ErrorAttributeOptions.Include.STACK_TRACE) : ErrorAttributeOptions.defaults());
HttpStatus status = getStatus(request);
response.setStatus(status.value());
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setObjectMapper(JsonUtil.getInstance());
view.setContentType(MediaType.APPLICATION_JSON_VALUE);
return new ModelAndView(view, body);
}
}

View File

@@ -0,0 +1,100 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.error;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.log.props.BladeRequestLogProperties;
import org.springblade.core.log.publisher.ErrorLogPublisher;
import org.springblade.core.secure.exception.SecureException;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.UrlUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.DispatcherServlet;
import jakarta.servlet.Servlet;
import java.util.Objects;
/**
* 未知异常转译和发送方便监听对未知异常统一处理。Order 排序优先级低
*
* @author Chill
*/
@Slf4j
@Order
@RequiredArgsConstructor
@AutoConfiguration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@RestControllerAdvice
public class BladeRestExceptionTranslator {
private final BladeProperties bladeProperties;
private final BladeRequestLogProperties requestLogProperties;
@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(ServiceException e) {
log.error("业务异常", e);
return R.fail(e.getResultCode(), e.getMessage());
}
@ExceptionHandler(SecureException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public R handleError(SecureException e) {
log.error("认证异常", e);
return R.fail(e.getResultCode(), e.getMessage());
}
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleError(Throwable e) {
log.error("服务器异常", e);
if (requestLogProperties.getErrorLog()) {
//发送服务异常事件
ErrorLogPublisher.publishEvent(e, UrlUtil.getPath(Objects.requireNonNull(WebUtil.getRequest()).getRequestURI()));
}
// 生产环境屏蔽具体异常信息返回
if (bladeProperties.isProd()) {
return R.fail(ResultCode.INTERNAL_SERVER_ERROR);
}
return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage()));
}
}

View File

@@ -0,0 +1,157 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.error;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.NoHandlerFoundException;
import jakarta.servlet.Servlet;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.Set;
/**
* 全局异常处理处理可预见的异常Order 排序优先级高
*
* @author Chill
*/
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@AutoConfiguration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@RestControllerAdvice
public class RestExceptionTranslator {
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(MissingServletRequestParameterException e) {
log.warn("缺少请求参数", e.getMessage());
String message = String.format("缺少必要的请求参数: %s", e.getParameterName());
return R.fail(ResultCode.PARAM_MISS, message);
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(MethodArgumentTypeMismatchException e) {
log.warn("请求参数格式错误", e.getMessage());
String message = String.format("请求参数格式错误: %s", e.getName());
return R.fail(ResultCode.PARAM_TYPE_ERROR, message);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(MethodArgumentNotValidException e) {
log.warn("参数验证失败", e.getMessage());
return handleError(e.getBindingResult());
}
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(BindException e) {
log.warn("参数绑定失败", e.getMessage());
return handleError(e.getBindingResult());
}
private R handleError(BindingResult result) {
FieldError error = result.getFieldError();
String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
return R.fail(ResultCode.PARAM_BIND_ERROR, message);
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(ConstraintViolationException e) {
log.warn("参数验证失败", e.getMessage());
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String path = ((PathImpl) violation.getPropertyPath()).getLeafNode().getName();
String message = String.format("%s:%s", path, violation.getMessage());
return R.fail(ResultCode.PARAM_VALID_ERROR, message);
}
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public R handleError(NoHandlerFoundException e) {
log.error("404没找到请求:{}", e.getMessage());
return R.fail(ResultCode.NOT_FOUND, e.getMessage());
}
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(HttpMessageNotReadableException e) {
log.error("消息不能读取:{}", e.getMessage());
return R.fail(ResultCode.MSG_NOT_READABLE, e.getMessage());
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public R handleError(HttpRequestMethodNotSupportedException e) {
log.error("不支持当前请求方法:{}", e.getMessage());
return R.fail(ResultCode.METHOD_NOT_SUPPORTED, e.getMessage());
}
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public R handleError(HttpMediaTypeNotSupportedException e) {
log.error("不支持当前媒体类型:{}", e.getMessage());
return R.fail(ResultCode.MEDIA_TYPE_NOT_SUPPORTED, e.getMessage());
}
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public R handleError(HttpMediaTypeNotAcceptableException e) {
String message = e.getMessage() + " " + StringUtil.join(e.getSupportedMediaTypes());
log.error("不接受的媒体类型:{}", message);
return R.fail(ResultCode.MEDIA_TYPE_NOT_SUPPORTED, message);
}
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.event;
import org.springframework.context.ApplicationEvent;
import java.util.Map;
/**
* 系统日志事件
*
* @author Chill
*/
public class ApiLogEvent extends ApplicationEvent {
public ApiLogEvent(Map<String, Object> source) {
super(source);
}
}

View File

@@ -0,0 +1,68 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.feign.ILogClient;
import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* 异步监听日志事件
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
public class ApiLogListener {
private final ILogClient logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(ApiLogEvent.class)
public void saveApiLog(ApiLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogApi logApi = (LogApi) source.get(EventConstant.EVENT_LOG);
LogAbstractUtil.addOtherInfoToLog(logApi, bladeProperties, serverInfo);
logService.saveApiLog(logApi);
}
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.event;
import org.springframework.context.ApplicationEvent;
import java.util.Map;
/**
* 错误日志事件
*
* @author Chill
*/
public class ErrorLogEvent extends ApplicationEvent {
public ErrorLogEvent(Map<String, Object> source) {
super(source);
}
}

View File

@@ -0,0 +1,66 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.feign.ILogClient;
import org.springblade.core.log.model.LogError;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* 异步监听错误日志事件
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
public class ErrorLogListener {
private final ILogClient logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(ErrorLogEvent.class)
public void saveErrorLog(ErrorLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogError logError = (LogError) source.get(EventConstant.EVENT_LOG);
LogAbstractUtil.addOtherInfoToLog(logError, bladeProperties, serverInfo);
logService.saveErrorLog(logError);
}
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.event;
import org.springframework.context.ApplicationEvent;
import java.util.Map;
/**
* 系统日志事件
*
* @author Chill
*/
public class UsualLogEvent extends ApplicationEvent {
public UsualLogEvent(Map<String, Object> source) {
super(source);
}
}

View File

@@ -0,0 +1,66 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.feign.ILogClient;
import org.springblade.core.log.model.LogUsual;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* 异步监听日志事件
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
public class UsualLogListener {
private final ILogClient logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(UsualLogEvent.class)
public void saveUsualLog(UsualLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogUsual logUsual = (LogUsual) source.get(EventConstant.EVENT_LOG);
LogAbstractUtil.addOtherInfoToLog(logUsual, bladeProperties, serverInfo);
logService.saveUsualLog(logUsual);
}
}

View File

@@ -0,0 +1,73 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.exception;
import lombok.Getter;
import org.springblade.core.tool.api.IResultCode;
import org.springblade.core.tool.api.ResultCode;
/**
* 业务异常
*
* @author Chill
*/
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 2359767895161832954L;
@Getter
private final IResultCode resultCode;
public ServiceException(String message) {
super(message);
this.resultCode = ResultCode.FAILURE;
}
public ServiceException(IResultCode resultCode) {
super(resultCode.getMessage());
this.resultCode = resultCode;
}
public ServiceException(IResultCode resultCode, Throwable cause) {
super(cause);
this.resultCode = resultCode;
}
/**
* 提高性能
*
* @return Throwable
*/
@Override
public Throwable fillInStackTrace() {
return this;
}
public Throwable doFillInStackTrace() {
return super.fillInStackTrace();
}
}

View File

@@ -0,0 +1,77 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.feign;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.model.LogUsual;
import org.springblade.core.log.model.LogError;
import org.springblade.core.tool.api.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* Feign接口类
*
* @author Chill
*/
@FeignClient(
value = AppConstant.APPLICATION_LOG_NAME,
fallback = LogClientFallback.class
)
public interface ILogClient {
String API_PREFIX = "/log";
/**
* 保存错误日志
*
* @param log
* @return
*/
@PostMapping(API_PREFIX + "/saveUsualLog")
R<Boolean> saveUsualLog(@RequestBody LogUsual log);
/**
* 保存操作日志
*
* @param log
* @return
*/
@PostMapping(API_PREFIX + "/saveApiLog")
R<Boolean> saveApiLog(@RequestBody LogApi log);
/**
* 保存错误日志
*
* @param log
* @return
*/
@PostMapping(API_PREFIX + "/saveErrorLog")
R<Boolean> saveErrorLog(@RequestBody LogError log);
}

View File

@@ -0,0 +1,58 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.feign;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.model.LogError;
import org.springblade.core.log.model.LogUsual;
import org.springblade.core.tool.api.R;
import org.springframework.stereotype.Component;
/**
* 日志fallback
*
* @author jiang
*/
@Slf4j
@Component
public class LogClientFallback implements ILogClient {
@Override
public R<Boolean> saveUsualLog(LogUsual log) {
return R.fail("usual log send fail");
}
@Override
public R<Boolean> saveApiLog(LogApi log) {
return R.fail("api log send fail");
}
@Override
public R<Boolean> saveErrorLog(LogError log) {
return R.fail("error log send fail");
}
}

View File

@@ -0,0 +1,60 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.filter;
import org.springblade.core.log.utils.LogTraceUtil;
import jakarta.servlet.*;
import java.io.IOException;
/**
* 日志追踪过滤器
*
* @author Chill
*/
public class LogTraceFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
boolean flag = LogTraceUtil.insert();
try {
chain.doFilter(request, response);
} finally {
if (flag) {
LogTraceUtil.remove();
}
}
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,52 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.launch;
import org.springblade.core.auto.service.AutoService;
import org.springblade.core.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.core.Ordered;
import java.util.Properties;
/**
* 日志启动配置类
*
* @author Chill
*/
@AutoService(LauncherService.class)
public class LogLauncherServiceImpl implements LauncherService {
@Override
public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
Properties props = System.getProperties();
props.setProperty("logging.config", "classpath:log/logback-" + profile + ".xml");
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

View File

@@ -0,0 +1,97 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.listener;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import org.springblade.core.log.utils.ElkPropsUtil;
import org.springblade.core.tool.utils.StringUtil;
/**
* logback监听类
*
* @author Chill
*/
public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
@Override
public void start() {
Context context = getContext();
context.putProperty("ELK_MODE", "FALSE");
context.putProperty("STDOUT_APPENDER", "STDOUT");
context.putProperty("INFO_APPENDER", "INFO");
context.putProperty("ERROR_APPENDER", "ERROR");
context.putProperty("DESTINATION", "127.0.0.1:9000");
String destination = ElkPropsUtil.getDestination();
if (StringUtil.isNotBlank(destination)) {
context.putProperty("ELK_MODE", "TRUE");
context.putProperty("STDOUT_APPENDER", "STDOUT_LOGSTASH");
context.putProperty("INFO_APPENDER", "INFO_LOGSTASH");
context.putProperty("ERROR_APPENDER", "ERROR_LOGSTASH");
context.putProperty("DESTINATION", destination);
}
}
@Override
public void stop() {
}
@Override
public boolean isStarted() {
return false;
}
@Override
public boolean isResetResistant() {
return false;
}
@Override
public void onStart(LoggerContext context) {
}
@Override
public void onReset(LoggerContext context) {
}
@Override
public void onStop(LoggerContext context) {
}
@Override
public void onLevelChange(Logger logger, Level level) {
}
}

View File

@@ -0,0 +1,65 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.logger;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.publisher.UsualLogPublisher;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
/**
* 日志工具类
*
* @author Chill
*/
@Slf4j
public class BladeLogger implements InitializingBean {
@Value("${spring.application.name}")
private String serviceId;
public void info(String id, String data) {
UsualLogPublisher.publishEvent("info", id, data);
}
public void debug(String id, String data) {
UsualLogPublisher.publishEvent("debug", id, data);
}
public void warn(String id, String data) {
UsualLogPublisher.publishEvent("warn", id, data);
}
public void error(String id, String data) {
UsualLogPublisher.publishEvent("error", id, data);
}
@Override
public void afterPropertiesSet() throws Exception {
log.info(serviceId + ": BladeLogger init success!");
}
}

View File

@@ -0,0 +1,116 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import org.springblade.core.tool.utils.DateUtil;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
* logApi、logError、logUsual父类
*
* @author Chill
*/
@Data
public class LogAbstract implements Serializable {
protected static final long serialVersionUID = 1L;
/**
* 主键id
*/
@JsonSerialize(using = ToStringSerializer.class)
@TableId(value = "id", type = IdType.ASSIGN_ID)
protected Long id;
/**
* 租户ID
*/
private String tenantId;
/**
* 服务ID
*/
protected String serviceId;
/**
* 服务器 ip
*/
protected String serverIp;
/**
* 服务器名
*/
protected String serverHost;
/**
* 环境
*/
protected String env;
/**
* 操作IP地址
*/
protected String remoteIp;
/**
* 用户代理
*/
protected String userAgent;
/**
* 请求URI
*/
protected String requestUri;
/**
* 操作方式
*/
protected String method;
/**
* 方法类
*/
protected String methodClass;
/**
* 方法名
*/
protected String methodName;
/**
* 操作提交的数据
*/
protected String params;
/**
* 创建人
*/
protected String createBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
protected Date createTime;
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.model;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 实体类
*
* @author Chill
*/
@Data
@TableName("blade_log_api")
@EqualsAndHashCode(callSuper = true)
public class LogApi extends LogAbstract {
@Serial
private static final long serialVersionUID = 1L;
/**
* 日志类型
*/
private String type;
/**
* 日志标题
*/
private String title;
/**
* 执行时间
*/
private String time;
}

View File

@@ -0,0 +1,70 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.model;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 服务 异常
*
* @author Chill
*/
@Data
@TableName("blade_log_error")
@EqualsAndHashCode(callSuper = true)
public class LogError extends LogAbstract {
@Serial
private static final long serialVersionUID = 1L;
/**
* 堆栈信息
*/
private String stackTrace;
/**
* 异常名
*/
private String exceptionName;
/**
* 异常消息
*/
private String message;
/**
* 文件名
*/
private String fileName;
/**
* 代码行数
*/
private Integer lineNumber;
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.model;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 实体类
*
* @author Chill
*/
@Data
@TableName("blade_log_usual")
@EqualsAndHashCode(callSuper = true)
public class LogUsual extends LogAbstract {
@Serial
private static final long serialVersionUID = 1L;
/**
* 日志级别
*/
private String logLevel;
/**
* 日志业务id
*/
private String logId;
/**
* 日志数据
*/
private String logData;
}

View File

@@ -0,0 +1,67 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.log.props;
import lombok.Getter;
import lombok.Setter;
import org.springblade.core.launch.log.BladeLogLevel;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.ArrayList;
import java.util.List;
/**
* 日志配置
*
* @author L.cm
*/
@Getter
@Setter
@RefreshScope
@ConfigurationProperties(BladeLogLevel.REQ_LOG_PROPS_PREFIX)
public class BladeRequestLogProperties {
/**
* 是否开启请求日志
*/
private Boolean enabled = true;
/**
* 是否开启异常日志推送
*/
private Boolean errorLog = true;
/**
* 日志级别配置默认BODY
*/
private BladeLogLevel level = BladeLogLevel.BODY;
/**
* 放行url
*/
private List<String> skipUrl = new ArrayList<>();
}

View File

@@ -0,0 +1,63 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.publisher;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.ApiLogEvent;
import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.WebUtil;
import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* API日志信息事件发送
*
* @author Chill
*/
public class ApiLogPublisher {
public static void publishEvent(String methodName, String methodClass, ApiLog apiLog, long time) {
HttpServletRequest request = WebUtil.getRequest();
LogApi logApi = new LogApi();
logApi.setType(BladeConstant.LOG_NORMAL_TYPE);
logApi.setTitle(apiLog.value());
logApi.setTime(String.valueOf(time));
logApi.setMethodClass(methodClass);
logApi.setMethodName(methodName);
LogAbstractUtil.addRequestInfoToLog(request, logApi);
Map<String, Object> event = new HashMap<>(16);
event.put(EventConstant.EVENT_LOG, logApi);
SpringUtil.publishEvent(new ApiLogEvent(event));
}
}

View File

@@ -0,0 +1,72 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.publisher;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.ErrorLogEvent;
import org.springblade.core.log.model.LogError;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springblade.core.tool.utils.Exceptions;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.WebUtil;
import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* 异常信息事件发送
*
* @author Chill
*/
public class ErrorLogPublisher {
public static void publishEvent(Throwable error, String requestUri) {
HttpServletRequest request = WebUtil.getRequest();
LogError logError = new LogError();
logError.setRequestUri(requestUri);
if (Func.isNotEmpty(error)) {
logError.setStackTrace(Exceptions.getStackTraceAsString(error));
logError.setExceptionName(error.getClass().getName());
logError.setMessage(error.getMessage());
StackTraceElement[] elements = error.getStackTrace();
if (Func.isNotEmpty(elements)) {
StackTraceElement element = elements[0];
logError.setMethodName(element.getMethodName());
logError.setMethodClass(element.getClassName());
logError.setFileName(element.getFileName());
logError.setLineNumber(element.getLineNumber());
}
}
LogAbstractUtil.addRequestInfoToLog(request, logError);
Map<String, Object> event = new HashMap<>(16);
event.put(EventConstant.EVENT_LOG, logError);
SpringUtil.publishEvent(new ErrorLogEvent(event));
}
}

View File

@@ -0,0 +1,65 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.publisher;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.UsualLogEvent;
import org.springblade.core.log.model.LogUsual;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.WebUtil;
import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* BLADE日志信息事件发送
*
* @author Chill
*/
public class UsualLogPublisher {
public static void publishEvent(String level, String id, String data) {
HttpServletRequest request = WebUtil.getRequest();
LogUsual logUsual = new LogUsual();
logUsual.setLogLevel(level);
logUsual.setLogId(id);
logUsual.setLogData(data);
Thread thread = Thread.currentThread();
StackTraceElement[] trace = thread.getStackTrace();
if (trace.length > 3) {
logUsual.setMethodClass(trace[3].getClassName());
logUsual.setMethodName(trace[3].getMethodName());
}
LogAbstractUtil.addRequestInfoToLog(request, logUsual);
Map<String, Object> event = new HashMap<>(16);
event.put(EventConstant.EVENT_LOG, logUsual);
SpringUtil.publishEvent(new UsualLogEvent(event));
}
}

View File

@@ -0,0 +1,49 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.utils;
import org.springblade.core.tool.utils.StringPool;
import java.util.Properties;
/**
* Elk配置工具
*
* @author Chill
*/
public class ElkPropsUtil {
/**
* 获取elk服务地址
*
* @return 服务地址
*/
public static String getDestination() {
Properties props = System.getProperties();
return props.getProperty("blade.log.elk.destination", StringPool.EMPTY);
}
}

View File

@@ -0,0 +1,80 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.utils;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.model.LogAbstract;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.*;
import jakarta.servlet.http.HttpServletRequest;
/**
* Log 相关工具
*
* @author Chill
*/
public class LogAbstractUtil {
/**
* 向log中添加补齐request的信息
*
* @param request 请求
* @param logAbstract 日志基础类
*/
public static void addRequestInfoToLog(HttpServletRequest request, LogAbstract logAbstract) {
if (ObjectUtil.isNotEmpty(request)) {
logAbstract.setTenantId(Func.toStrWithEmpty(AuthUtil.getTenantId(), BladeConstant.ADMIN_TENANT_ID));
logAbstract.setRemoteIp(WebUtil.getIP(request));
logAbstract.setUserAgent(request.getHeader(WebUtil.USER_AGENT_HEADER));
logAbstract.setRequestUri(UrlUtil.getPath(request.getRequestURI()));
logAbstract.setMethod(request.getMethod());
logAbstract.setParams(WebUtil.getRequestContent(request));
logAbstract.setCreateBy(AuthUtil.getUserAccount(request));
}
}
/**
* 向log中添加补齐其他的信息egblade、server等
*
* @param logAbstract 日志基础类
* @param bladeProperties 配置信息
* @param serverInfo 服务信息
*/
public static void addOtherInfoToLog(LogAbstract logAbstract, BladeProperties bladeProperties, ServerInfo serverInfo) {
logAbstract.setServiceId(bladeProperties.getName());
logAbstract.setServerHost(serverInfo.getHostName());
logAbstract.setServerIp(serverInfo.getIpWithPort());
logAbstract.setEnv(bladeProperties.getEnv());
logAbstract.setCreateTime(DateUtil.now());
if (logAbstract.getParams() == null) {
logAbstract.setParams(StringPool.EMPTY);
}
}
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.log.utils;
import org.slf4j.MDC;
import org.springblade.core.tool.utils.StringUtil;
/**
* 日志追踪工具类
*
* @author Chill
*/
public class LogTraceUtil {
private static final String UNIQUE_ID = "traceId";
/**
* 获取日志追踪id格式
*/
public static String getTraceId() {
return StringUtil.randomUUID();
}
/**
* 插入traceId
*/
public static boolean insert() {
MDC.put(UNIQUE_ID, getTraceId());
return true;
}
/**
* 移除traceId
*/
public static boolean remove() {
MDC.remove(UNIQUE_ID);
return true;
}
}

View File

@@ -0,0 +1,3 @@
#配置日志地址
logging:
config: classpath:log/logback-${blade.env}.xml

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" debug="false">
<!-- 自定义参数监听 -->
<contextListener class="org.springblade.core.log.listener.LoggerStartupListener"/>
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<if condition='property("ELK_MODE").toUpperCase().contains("TRUE")'>
<then>
<!-- 推送日志至elk -->
<appender name="STDOUT_LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${DESTINATION}</destination>
<!-- 日志输出编码 -->
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"traceId": "%X{traceId}",
"requestId": "%X{requestId}",
"accountId": "%X{accountId}",
"tenantId": "%X{tenantId}",
"logLevel": "%level",
"serviceName": "${springAppName:-SpringApp}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"line":"%L",
"message": "%message"
}
</pattern>
</pattern>
<mdc/>
<stackTrace/>
</providers>
</encoder>
</appender>
</then>
</if>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="${STDOUT_APPENDER}"/>
</root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
<!-- MyBatis log configure -->
<logger name="com.apache.ibatis" level="INFO"/>
<logger name="org.mybatis.spring" level="INFO"/>
<logger name="java.sql.Connection" level="INFO"/>
<logger name="java.sql.Statement" level="INFO"/>
<logger name="java.sql.PreparedStatement" level="INFO"/>
<!-- 减少部分debug日志 -->
<logger name="druid.sql" level="INFO"/>
<logger name="org.apache.shiro" level="INFO"/>
<logger name="org.mybatis.spring" level="INFO"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.springframework.context" level="WARN"/>
<logger name="org.springframework.beans" level="WARN"/>
<logger name="com.baomidou.mybatisplus" level="INFO"/>
<logger name="org.apache.ibatis.io" level="INFO"/>
<logger name="org.apache.velocity" level="INFO"/>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.undertow" level="INFO"/>
<logger name="org.xnio.nio" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
<logger name="springfox.documentation" level="INFO"/>
<logger name="org.hibernate.validator" level="INFO"/>
<logger name="com.netflix.loadbalancer" level="INFO"/>
<logger name="com.netflix.hystrix" level="INFO"/>
<logger name="com.netflix.zuul" level="INFO"/>
<logger name="de.codecentric" level="INFO"/>
<!-- cache INFO -->
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="org.springframework.cache" level="INFO"/>
<!-- cloud -->
<logger name="org.apache.http" level="INFO"/>
<logger name="com.netflix.discovery" level="INFO"/>
<logger name="com.netflix.eureka" level="INFO"/>
<!-- 业务日志 -->
<Logger name="org.springblade" level="INFO"/>
<Logger name="org.springblade.core.tenant" level="INFO"/>
<Logger name="org.springblade.core.version" level="INFO"/>
<!-- 减少nacos日志 -->
<logger name="com.alibaba.nacos" level="ERROR"/>
</configuration>

View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" debug="false">
<!-- 自定义参数监听 -->
<contextListener class="org.springblade.core.log.listener.LoggerStartupListener"/>
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 生成日志文件 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/info-%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 生成日志文件 -->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/error-%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<if condition='property("ELK_MODE").toUpperCase().contains("TRUE")'>
<then>
<!-- 推送日志至elk -->
<appender name="INFO_LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${DESTINATION}</destination>
<!-- 日志输出编码 -->
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"traceId": "%X{traceId}",
"requestId": "%X{requestId}",
"accountId": "%X{accountId}",
"tenantId": "%X{tenantId}",
"logLevel": "%level",
"serviceName": "${springAppName:-SpringApp}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"line":"%L",
"message": "%message"
}
</pattern>
</pattern>
<mdc/>
<stackTrace/>
</providers>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 推送日志至elk -->
<appender name="ERROR_LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${DESTINATION}</destination>
<!-- 日志输出编码 -->
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"traceId": "%X{traceId}",
"requestId": "%X{requestId}",
"accountId": "%X{accountId}",
"tenantId": "%X{tenantId}",
"logLevel": "%level",
"serviceName": "${springAppName:-SpringApp}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"line":"%L",
"message": "%message"
}
</pattern>
</pattern>
<mdc/>
<stackTrace/>
</providers>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
</then>
</if>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="${INFO_APPENDER}"/>
<appender-ref ref="${ERROR_APPENDER}"/>
</root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
<!-- 减少nacos日志 -->
<logger name="com.alibaba.nacos" level="ERROR"/>
</configuration>

View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" debug="false">
<!-- 自定义参数监听 -->
<contextListener class="org.springblade.core.log.listener.LoggerStartupListener"/>
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 生成日志文件 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/info-%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 生成日志文件 -->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/error-%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<if condition='property("ELK_MODE").toUpperCase().contains("TRUE")'>
<then>
<!-- 推送日志至elk -->
<appender name="INFO_LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${DESTINATION}</destination>
<!-- 日志输出编码 -->
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"traceId": "%X{traceId}",
"requestId": "%X{requestId}",
"accountId": "%X{accountId}",
"tenantId": "%X{tenantId}",
"logLevel": "%level",
"serviceName": "${springAppName:-SpringApp}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"line":"%L",
"message": "%message"
}
</pattern>
</pattern>
<mdc/>
<stackTrace/>
</providers>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 推送日志至elk -->
<appender name="ERROR_LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${DESTINATION}</destination>
<!-- 日志输出编码 -->
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"traceId": "%X{traceId}",
"requestId": "%X{requestId}",
"accountId": "%X{accountId}",
"tenantId": "%X{tenantId}",
"logLevel": "%level",
"serviceName": "${springAppName:-SpringApp}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"line":"%L",
"message": "%message"
}
</pattern>
</pattern>
<mdc/>
<stackTrace/>
</providers>
</encoder>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
</then>
</if>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="${INFO_APPENDER}"/>
<appender-ref ref="${ERROR_APPENDER}"/>
</root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
<!-- 减少nacos日志 -->
<logger name="com.alibaba.nacos" level="ERROR"/>
</configuration>