feat: 添加日志打印器.
This commit is contained in:
@@ -9,7 +9,7 @@ import java.lang.annotation.*;
|
||||
* @version 1.0.0
|
||||
* @since 2022/4/20 10:33
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Target({ElementType.METHOD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface IgnoreLog {
|
||||
|
||||
@@ -28,7 +28,7 @@ package com.orion.ops.framework.common.utils;
|
||||
* \033[?25l 隐藏光标
|
||||
* \033[?25h 显示光标
|
||||
* <p>
|
||||
* TODO 后续直接使用kit
|
||||
* FIXME KIT
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
*/
|
||||
public interface ITypeHandler<P, R> extends TypeHandler<R> {
|
||||
|
||||
// TODO KIT
|
||||
// FIXME KIT
|
||||
String COMMA = ",";
|
||||
|
||||
/**
|
||||
@@ -25,7 +25,7 @@ public interface ITypeHandler<P, R> extends TypeHandler<R> {
|
||||
R getResult(P param);
|
||||
|
||||
/**
|
||||
* // TODO kit
|
||||
* // FIXME kit
|
||||
* 用 , 连接
|
||||
*
|
||||
* @param list list
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "orion.swagger",
|
||||
"type": "com.orion.ops.framework.swagger.config.SwaggerProperties",
|
||||
"sourceType": "com.orion.ops.framework.swagger.config.SwaggerProperties"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "orion.swagger.title",
|
||||
"type": "java.lang.String",
|
||||
"description": "swagger 项目标题."
|
||||
},
|
||||
{
|
||||
"name": "orion.swagger.description",
|
||||
"type": "java.lang.String",
|
||||
"description": "swagger 项目描述."
|
||||
},
|
||||
{
|
||||
"name": "orion.swagger.version",
|
||||
"type": "java.lang.String",
|
||||
"description": "swagger 项目版本."
|
||||
},
|
||||
{
|
||||
"name": "orion.swagger.url",
|
||||
"type": "java.lang.String",
|
||||
"description": "swagger 项目url."
|
||||
},
|
||||
{
|
||||
"name": "orion.swagger.email",
|
||||
"type": "java.lang.String",
|
||||
"description": "swagger 项目email."
|
||||
},
|
||||
{
|
||||
"name": "orion.swagger.license",
|
||||
"type": "java.lang.String",
|
||||
"description": "swagger 项目license."
|
||||
},
|
||||
{
|
||||
"name": "orion.swagger.license-url",
|
||||
"type": "java.lang.String",
|
||||
"description": "swagger 项目license-url."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -38,6 +38,20 @@
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- doc -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- knife4j -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,9 +1,16 @@
|
||||
package com.orion.ops.framework.web.config;
|
||||
|
||||
import com.orion.ops.framework.common.constant.InterceptorOrderConst;
|
||||
import com.orion.ops.framework.web.core.interceptor.LogPrintInterceptor;
|
||||
import com.orion.ops.framework.web.core.config.LogPrinterConfig;
|
||||
import com.orion.ops.framework.web.core.interceptor.LogPrinterInterceptor;
|
||||
import com.orion.ops.framework.web.core.interceptor.PrettyLogPrinterInterceptor;
|
||||
import com.orion.ops.framework.web.core.interceptor.RowLogPrinterInterceptor;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
@@ -14,18 +21,41 @@ import org.springframework.context.annotation.Bean;
|
||||
* @since 2023/6/16 18:18
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(LogPrinterConfig.class)
|
||||
public class OrionLogPrinterConfiguration {
|
||||
|
||||
/**
|
||||
* @param config config
|
||||
* @return 美化日志打印器
|
||||
*/
|
||||
@Bean
|
||||
public LogPrintInterceptor logPrintInterceptor() {
|
||||
return new LogPrintInterceptor();
|
||||
@ConditionalOnProperty(value = "logging.printer.mode", havingValue = "pretty")
|
||||
public LogPrinterInterceptor prettyPrinter(LogPrinterConfig config) {
|
||||
return new PrettyLogPrinterInterceptor(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param config config
|
||||
* @return 单行日志打印器
|
||||
*/
|
||||
@Bean
|
||||
public AspectJExpressionPointcutAdvisor logPrinterAdvisor(LogPrintInterceptor logPrintInterceptor) {
|
||||
@ConditionalOnProperty(value = "logging.printer.mode", havingValue = "row")
|
||||
public LogPrinterInterceptor rowPrinter(LogPrinterConfig config) {
|
||||
return new RowLogPrinterInterceptor(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param logPrinterInterceptor logPrinterInterceptor
|
||||
* @param expression 切面表达式
|
||||
* @return 日志打印切面
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(LogPrinterInterceptor.class)
|
||||
public AspectJExpressionPointcutAdvisor logPrinterAdvisor(LogPrinterInterceptor logPrinterInterceptor,
|
||||
@Value("${logging.printer.expression}") String expression) {
|
||||
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
|
||||
advisor.setExpression("execution (* com.orion.ops.**.controller.*.*(..)) && !@annotation(com.orion.ops.framework.common.annotation.IgnoreLog)");
|
||||
advisor.setAdvice(logPrintInterceptor);
|
||||
advisor.setExpression(expression);
|
||||
advisor.setAdvice(logPrinterInterceptor);
|
||||
advisor.setOrder(InterceptorOrderConst.LOG_FILTER);
|
||||
return advisor;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.orion.ops.framework.web.core.config;
|
||||
|
||||
import com.orion.ops.framework.web.core.utils.Utils;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 日志打印配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/28 22:36
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("logging.printer")
|
||||
public class LogPrinterConfig {
|
||||
|
||||
/**
|
||||
* 字段配置
|
||||
*/
|
||||
private LogPrinterFieldConfig field;
|
||||
|
||||
/**
|
||||
* 显示的请求头
|
||||
*/
|
||||
private List<String> headers;
|
||||
|
||||
public void setField(LogPrinterFieldConfig field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public void setHeaders(List<String> headers) {
|
||||
this.headers = Utils.parseStringList(headers, String::toLowerCase);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.orion.ops.framework.web.core.config;
|
||||
|
||||
import com.orion.ops.framework.web.core.utils.Utils;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 日志打印字段配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/28 22:36
|
||||
*/
|
||||
@Data
|
||||
public class LogPrinterFieldConfig {
|
||||
|
||||
/**
|
||||
* 忽略的字段
|
||||
*/
|
||||
private List<String> ignore;
|
||||
|
||||
/**
|
||||
* 脱敏的字段
|
||||
*/
|
||||
private List<String> desensitization;
|
||||
|
||||
public void setIgnore(List<String> ignore) {
|
||||
this.ignore = Utils.parseStringList(ignore);
|
||||
}
|
||||
|
||||
public void setDesensitization(List<String> desensitization) {
|
||||
this.desensitization = Utils.parseStringList(desensitization);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package com.orion.ops.framework.web.core.interceptor;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.serializer.PropertyFilter;
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import com.alibaba.fastjson.serializer.ValueFilter;
|
||||
import com.orion.lang.utils.Objects1;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.lang.utils.reflect.Annotations;
|
||||
import com.orion.lang.utils.reflect.Classes;
|
||||
import com.orion.ops.framework.common.annotation.IgnoreLog;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.meta.TraceIdHolder;
|
||||
import com.orion.ops.framework.web.core.config.LogPrinterConfig;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 日志打印拦截器 基类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/29 10:36
|
||||
*/
|
||||
public abstract class BaseLogPrinterInterceptor implements LogPrinterInterceptor {
|
||||
|
||||
private static final String EMPTY_ARG = "<EMPTY>";
|
||||
|
||||
private static final String ERROR_ARG = "<ERROR>";
|
||||
|
||||
/**
|
||||
* 请求头过滤器
|
||||
*/
|
||||
protected Predicate<String> headerFilter;
|
||||
|
||||
/**
|
||||
* 字段过滤器
|
||||
*/
|
||||
protected SerializeFilter[] fieldFilter;
|
||||
|
||||
/**
|
||||
* api 描述
|
||||
*/
|
||||
private final Map<String, String> summaryMapping;
|
||||
|
||||
/**
|
||||
* 忽略的参数
|
||||
*/
|
||||
private final Map<String, boolean[]> ignoreParameter;
|
||||
|
||||
public BaseLogPrinterInterceptor(LogPrinterConfig config) {
|
||||
this.summaryMapping = Maps.newMap();
|
||||
this.ignoreParameter = Maps.newMap();
|
||||
this.init(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param config config
|
||||
*/
|
||||
protected void init(LogPrinterConfig config) {
|
||||
// 请求头过滤器
|
||||
this.headerFilter = header -> config.getHeaders().contains(header);
|
||||
// 忽略字段过滤器
|
||||
PropertyFilter ignoreFilter = (Object object, String name, Object value) -> !config.getField().getIgnore().contains(name);
|
||||
// 脱敏字段过滤器
|
||||
ValueFilter desensitizationFilter = (Object object, String name, Object value) -> {
|
||||
if (config.getField().getDesensitization().contains(name)) {
|
||||
String s = Objects1.toString(value);
|
||||
// Safes.mix()
|
||||
// TODO
|
||||
return "xxxxxx";
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
this.fieldFilter = new SerializeFilter[]{ignoreFilter, desensitizationFilter};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Date startTime = new Date();
|
||||
String traceId = TraceIdHolder.get();
|
||||
// 打印请求日志
|
||||
this.requestPrinter(startTime, traceId, invocation);
|
||||
try {
|
||||
// 执行方法
|
||||
Object ret = invocation.proceed();
|
||||
// 打印响应日志
|
||||
this.responsePrinter(startTime, traceId, ret);
|
||||
return ret;
|
||||
} catch (Throwable t) {
|
||||
// 打印异常日志
|
||||
this.errorPrinter(startTime, traceId, t);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印请求信息
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param traceId traceId
|
||||
* @param invocation invocation
|
||||
*/
|
||||
protected abstract void requestPrinter(Date startTime, String traceId, MethodInvocation invocation);
|
||||
|
||||
/**
|
||||
* 打印响应信息
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param traceId traceId
|
||||
* @param ret return
|
||||
*/
|
||||
protected abstract void responsePrinter(Date startTime, String traceId, Object ret);
|
||||
|
||||
/**
|
||||
* 打印异常信息
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param traceId traceId
|
||||
* @param throwable ex
|
||||
*/
|
||||
protected abstract void errorPrinter(Date startTime, String traceId, Throwable throwable);
|
||||
|
||||
/**
|
||||
* 获取 api 描述
|
||||
*
|
||||
* @param m method
|
||||
* @return summary
|
||||
*/
|
||||
protected String getApiSummary(Method m) {
|
||||
// 缓存中获取描述
|
||||
String key = m.toString();
|
||||
String cache = summaryMapping.get(key);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
// 获取注解描述
|
||||
Operation operation = Annotations.getAnnotation(m, Operation.class);
|
||||
String summary = Const.EMPTY;
|
||||
if (operation != null) {
|
||||
summary = operation.summary();
|
||||
}
|
||||
summaryMapping.put(key, summary);
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求参数 json
|
||||
*
|
||||
* @param method method
|
||||
* @param args object
|
||||
* @return json
|
||||
*/
|
||||
protected String requestToString(Method method, Object[] args) {
|
||||
int length = args.length;
|
||||
if (length == 0) {
|
||||
return EMPTY_ARG;
|
||||
}
|
||||
try {
|
||||
// 检查是否需要忽略字段
|
||||
boolean[] ignored = this.getParameterIgnoreConfig(method, args);
|
||||
int printCount = 0;
|
||||
int lastPrintIndex = 0;
|
||||
for (int i = 0; i < ignored.length; i++) {
|
||||
if (!ignored[i]) {
|
||||
printCount++;
|
||||
lastPrintIndex = i;
|
||||
}
|
||||
}
|
||||
if (printCount == 0) {
|
||||
// 无打印参数
|
||||
return EMPTY_ARG;
|
||||
} else if (printCount == 1) {
|
||||
// 单个打印参数
|
||||
return JSON.toJSONString(args[lastPrintIndex], fieldFilter);
|
||||
} else {
|
||||
// 多个打印参数
|
||||
JSONArray arr = new JSONArray();
|
||||
for (int i = 0; i < ignored.length; i++) {
|
||||
if (!ignored[i]) {
|
||||
arr.add(args[i]);
|
||||
}
|
||||
}
|
||||
return JSON.toJSONString(arr, fieldFilter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应结果 json
|
||||
*
|
||||
* @param o object
|
||||
* @return json
|
||||
*/
|
||||
protected String responseToString(Object o) {
|
||||
try {
|
||||
return JSON.toJSONString(o, fieldFilter);
|
||||
} catch (Exception e) {
|
||||
return ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数忽略字段配置
|
||||
*
|
||||
* @param method method
|
||||
* @param args args
|
||||
* @return ignoreArr
|
||||
*/
|
||||
private boolean[] getParameterIgnoreConfig(Method method, Object[] args) {
|
||||
int length = args.length;
|
||||
// 获取缓存
|
||||
String key = method.toString();
|
||||
boolean[] ignored = ignoreParameter.get(key);
|
||||
if (ignored != null) {
|
||||
return ignored;
|
||||
}
|
||||
ignored = new boolean[length];
|
||||
ignoreParameter.put(key, ignored);
|
||||
// 检查是否有忽略字段注解
|
||||
Annotation[][] annotations = method.getParameterAnnotations();
|
||||
for (int parameterIndex = 0; parameterIndex < length; parameterIndex++) {
|
||||
boolean ignore = false;
|
||||
Annotation[] parameterAnnotations = annotations[parameterIndex];
|
||||
for (Annotation parameterAnnotation : parameterAnnotations) {
|
||||
// 需要忽略该字段
|
||||
if (parameterAnnotation.annotationType().equals(IgnoreLog.class)) {
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ignored[parameterIndex] = ignore;
|
||||
}
|
||||
// 检查是否可以序列化
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (ignored[i]) {
|
||||
continue;
|
||||
}
|
||||
Object arg = args[i];
|
||||
// 是否为 request / response
|
||||
if (arg instanceof ServletRequest || arg instanceof ServletResponse) {
|
||||
ignored[i] = true;
|
||||
continue;
|
||||
}
|
||||
// 是否为代理对象 (bean)
|
||||
if (arg != null) {
|
||||
if (Classes.isJdkProxy(arg) || Classes.isCglibProxy(arg)) {
|
||||
ignored[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ignored;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package com.orion.ops.framework.web.core.interceptor;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.PropertyFilter;
|
||||
import com.orion.lang.constant.Const;
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.time.Dates;
|
||||
import com.orion.ops.framework.common.meta.TraceIdHolder;
|
||||
import com.orion.web.servlet.web.Servlets;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 日志打印拦截器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2022/7/14 18:25
|
||||
*/
|
||||
@Slf4j
|
||||
@Order(10)
|
||||
public class LogPrintInterceptor implements MethodInterceptor {
|
||||
|
||||
// TODO @OperatorLog(bizName = "")
|
||||
// TODO 日志规格模型 SIMPLIFY FULL
|
||||
|
||||
// https://blog.csdn.net/gaojie_csdn/article/details/127810618
|
||||
// 加注解
|
||||
|
||||
// TODO 改为正则?
|
||||
@Value("#{'${log.interceptor.ignore.fields:}'.split(',')}")
|
||||
private String[] ignoreFields;
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Date startTime = new Date();
|
||||
String traceId = TraceIdHolder.get();
|
||||
// 打印开始日志
|
||||
this.beforeLogPrint(startTime, traceId, invocation);
|
||||
try {
|
||||
// 执行方法
|
||||
Object ret = invocation.proceed();
|
||||
// 返回打印
|
||||
this.afterReturnLogPrint(startTime, traceId, ret);
|
||||
return ret;
|
||||
} catch (Throwable t) {
|
||||
// 异常打印
|
||||
this.afterThrowingLogPrint(startTime, traceId, t);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法进入打印
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param traceId trace
|
||||
* @param invocation invocation
|
||||
*/
|
||||
public void beforeLogPrint(Date startTime, String traceId, MethodInvocation invocation) {
|
||||
StringBuilder requestLog = new StringBuilder("\napi请求-开始-traceId: ").append(traceId).append('\n');
|
||||
// TODO 登陆用户
|
||||
// requestLog.append("\t当前用户: ").append(JSON.toJSONString(UserHolder.get())).append('\n');
|
||||
// TODO @OperatorLog
|
||||
// http请求信息
|
||||
Optional.ofNullable(RequestContextHolder.getRequestAttributes())
|
||||
.map(s -> (ServletRequestAttributes) s)
|
||||
.map(ServletRequestAttributes::getRequest)
|
||||
.ifPresent(request -> {
|
||||
// url
|
||||
requestLog.append("\t").append(Servlets.getMethod(request)).append(" ")
|
||||
.append(Servlets.getRequestUrl(request)).append('\n');
|
||||
// query
|
||||
requestLog.append("\tip: ").append(Servlets.getRemoteAddr(request)).append('\n')
|
||||
.append("\tquery: ").append(Servlets.getQueryString(request)).append('\n');
|
||||
// header
|
||||
Servlets.getHeaderMap(request).forEach((hk, hv) -> requestLog.append('\t')
|
||||
.append(hk).append(": ")
|
||||
.append(hv).append('\n'));
|
||||
});
|
||||
// 方法信息
|
||||
Method method = invocation.getMethod();
|
||||
requestLog.append("\t开始时间: ").append(Dates.format(startTime, Dates.YMD_HMSS)).append('\n')
|
||||
.append("\t方法签名: ").append(method.getDeclaringClass().getName()).append('#')
|
||||
.append(method.getName()).append("\n")
|
||||
.append("\t请求参数: ").append(this.argsToString(invocation.getArguments()));
|
||||
log.info(requestLog.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回打印
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param traceId traceId
|
||||
* @param ret return
|
||||
*/
|
||||
private void afterReturnLogPrint(Date startTime, String traceId, Object ret) {
|
||||
Date endTime = new Date();
|
||||
// 响应日志
|
||||
StringBuilder responseLog = new StringBuilder("\napi请求-结束-traceId: ").append(traceId).append('\n');
|
||||
responseLog.append("\t结束时间: ").append(Dates.format(endTime, Dates.YMD_HMSS))
|
||||
.append(" used: ").append(endTime.getTime() - startTime.getTime()).append("ms \n")
|
||||
.append("\t响应结果: ").append(this.argsToString(ret));
|
||||
log.info(responseLog.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常打印
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param traceId trace
|
||||
* @param throwable ex
|
||||
*/
|
||||
private void afterThrowingLogPrint(Date startTime, String traceId, Throwable throwable) {
|
||||
Date endTime = new Date();
|
||||
// 响应日志
|
||||
StringBuilder responseLog = new StringBuilder("\napi请求-异常-traceId: ").append(traceId).append('\n');
|
||||
responseLog.append("\t结束时间: ").append(Dates.format(endTime, Dates.YMD_HMSS))
|
||||
.append(" used: ").append(endTime.getTime() - startTime.getTime()).append("ms \n")
|
||||
.append("\t异常摘要: ").append(Exceptions.getDigest(throwable));
|
||||
log.error(responseLog.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数转json
|
||||
*
|
||||
* @param o object
|
||||
* @return json
|
||||
*/
|
||||
private String argsToString(Object o) {
|
||||
try {
|
||||
if (ignoreFields.length == 1 && Const.EMPTY.equals(ignoreFields[0])) {
|
||||
// 不过滤
|
||||
return JSON.toJSONString(o);
|
||||
} else {
|
||||
return JSON.toJSONString(o, (PropertyFilter) (object, name, value) -> !Arrays1.contains(ignoreFields, name));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return String.valueOf(o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.orion.ops.framework.web.core.interceptor;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
|
||||
/**
|
||||
* 日志打印拦截器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/29 10:36
|
||||
*/
|
||||
public interface LogPrinterInterceptor extends MethodInterceptor {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.orion.ops.framework.web.core.interceptor;
|
||||
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.time.Dates;
|
||||
import com.orion.ops.framework.web.core.config.LogPrinterConfig;
|
||||
import com.orion.web.servlet.web.Servlets;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 美化 日志打印拦截器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/29 10:36
|
||||
*/
|
||||
@Slf4j
|
||||
public class PrettyLogPrinterInterceptor extends BaseLogPrinterInterceptor {
|
||||
|
||||
public PrettyLogPrinterInterceptor(LogPrinterConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void requestPrinter(Date startTime, String traceId, MethodInvocation invocation) {
|
||||
StringBuilder requestLog = new StringBuilder("\napi请求-开始\n");
|
||||
// http请求信息
|
||||
HttpServletRequest request = Optional.ofNullable(RequestContextHolder.getRequestAttributes())
|
||||
.map(s -> (ServletRequestAttributes) s)
|
||||
.map(ServletRequestAttributes::getRequest)
|
||||
.orElse(null);
|
||||
// url
|
||||
if (request != null) {
|
||||
requestLog.append("\t").append(Servlets.getMethod(request)).append(" ")
|
||||
.append(Servlets.getRequestUrl(request)).append('\n');
|
||||
}
|
||||
// traceId
|
||||
requestLog.append("\ttraceId: ").append(traceId).append("\n");
|
||||
// 开始时间
|
||||
requestLog.append("\tstart: ").append(Dates.format(startTime, Dates.YMD_HMSS)).append('\n');
|
||||
// api 描述
|
||||
String summary = this.getApiSummary(invocation.getMethod());
|
||||
if (!Strings.isEmpty(summary)) {
|
||||
requestLog.append("\tsummary: ").append(summary).append('\n');
|
||||
}
|
||||
// TODO 登陆用户
|
||||
// http
|
||||
if (request != null) {
|
||||
// remoteAddr
|
||||
requestLog.append("\tremoteAddr: ").append(Servlets.getRemoteAddr(request)).append('\n');
|
||||
// header
|
||||
Servlets.getHeaderMap(request).forEach((hk, hv) -> {
|
||||
if (headerFilter.test(hk.toLowerCase())) {
|
||||
requestLog.append('\t')
|
||||
.append(hk).append(": ")
|
||||
.append(hv).append('\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
Method method = invocation.getMethod();
|
||||
// 方法签名
|
||||
requestLog.append("\tmethodSign: ").append(method.getDeclaringClass().getName()).append('#')
|
||||
.append(method.getName()).append("\n");
|
||||
// 参数
|
||||
requestLog.append("\tparameter: ").append(this.requestToString(method, invocation.getArguments()));
|
||||
log.info(requestLog.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void responsePrinter(Date startTime, String traceId, Object ret) {
|
||||
Date endTime = new Date();
|
||||
// 响应日志
|
||||
StringBuilder responseLog = new StringBuilder("\napi请求-结束\n")
|
||||
.append("\ttraceId: ").append(traceId).append('\n')
|
||||
.append("\tend: ").append(Dates.format(endTime, Dates.YMD_HMSS)).append('\n')
|
||||
.append("\tused: ").append(endTime.getTime() - startTime.getTime()).append("ms \n")
|
||||
.append("\tresponse: ").append(this.responseToString(ret));
|
||||
log.info(responseLog.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void errorPrinter(Date startTime, String traceId, Throwable throwable) {
|
||||
Date endTime = new Date();
|
||||
// 异常日志
|
||||
StringBuilder errorLog = new StringBuilder("\napi请求-异常\n")
|
||||
.append("\ttraceId: ").append(traceId).append('\n')
|
||||
.append("\tend: ").append(Dates.format(endTime, Dates.YMD_HMSS)).append('\n')
|
||||
.append("\tused: ").append(endTime.getTime() - startTime.getTime()).append("ms \n")
|
||||
.append("\terrorDigest: ").append(Exceptions.getDigest(throwable));
|
||||
log.error(errorLog.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.orion.ops.framework.web.core.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/28 23:21
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
private Utils() {
|
||||
}
|
||||
|
||||
public static List<String> parseStringList(List<String> list) {
|
||||
return parseStringList(list, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析配置 List<String:String[,]>
|
||||
*
|
||||
* @param list list
|
||||
* @param mapper mapper
|
||||
* @return config
|
||||
*/
|
||||
public static List<String> parseStringList(List<String> list, Function<String, String> mapper) {
|
||||
return Optional.ofNullable(list)
|
||||
.map(List::stream)
|
||||
.orElseGet(Stream::empty)
|
||||
// FIXME kit
|
||||
.map(s -> s.split(","))
|
||||
.flatMap(Arrays::stream)
|
||||
.map(String::trim)
|
||||
.map(mapper)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "logging.printer",
|
||||
"type": "com.orion.ops.framework.web.core.config.LogPrinterConfig",
|
||||
"sourceType": "com.orion.ops.framework.web.core.config.LogPrinterConfig"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "logging.printer.mode",
|
||||
"type": "com.orion.ops.framework.web.core.enums.LogPrinterMode",
|
||||
"description": "日志打印模型.",
|
||||
"defaultValue": "NONE"
|
||||
},
|
||||
{
|
||||
"name": "logging.printer.expression",
|
||||
"type": "java.lang.String",
|
||||
"description": "aspectj 表达式."
|
||||
},
|
||||
{
|
||||
"name": "logging.printer.headers",
|
||||
"type": "java.util.List",
|
||||
"description": "需要打印的 HttpHandlers."
|
||||
},
|
||||
{
|
||||
"name": "logging.printer.field.ignore",
|
||||
"type": "java.util.List",
|
||||
"description": "忽略打印的字段."
|
||||
},
|
||||
{
|
||||
"name": "logging.printer.field.desensitization",
|
||||
"type": "java.util.List",
|
||||
"description": "需要脱敏的字段."
|
||||
},
|
||||
{
|
||||
"name": "orion.version",
|
||||
"type": "java.lang.String",
|
||||
"description": "项目版本."
|
||||
},
|
||||
{
|
||||
"name": "orion.api.prefix",
|
||||
"type": "java.lang.String",
|
||||
"description": "项目 api 前缀."
|
||||
},
|
||||
{
|
||||
"name": "orion.api.cors",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否开启 cors 过滤器."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -108,6 +108,19 @@ logging:
|
||||
pattern:
|
||||
console: '%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %boldBlue([%X{tid}]) %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}'
|
||||
file: "%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} [%X{tid}] [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}"
|
||||
printer:
|
||||
mode: PRETTY
|
||||
expression: 'execution (* com.orion.ops.**.controller.*.*(..)) && !@annotation(com.orion.ops.framework.common.annotation.IgnoreLog)'
|
||||
headers:
|
||||
- user-agent,accept
|
||||
- content-type
|
||||
field:
|
||||
ignore:
|
||||
- password,newPassword
|
||||
- metrics
|
||||
desensitization:
|
||||
- phone,phoneNumber
|
||||
- email,sendEmail
|
||||
|
||||
orion:
|
||||
# 版本
|
||||
@@ -118,7 +131,6 @@ orion:
|
||||
prefix: /orion-api
|
||||
# 是否开启跨域
|
||||
cors: true
|
||||
# 文档配置
|
||||
swagger:
|
||||
title: orion-ops-pro 运维平台
|
||||
description: 一站式提供运维功能
|
||||
|
||||
Reference in New Issue
Block a user