mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-10-25 18:57:17 +08:00
feat(log): 新增日志模块 - HttpTracePro(Spring Boot Actuator HttpTrace 定制增强版)
This commit is contained in:
@@ -45,26 +45,26 @@ public class IpUtils {
|
|||||||
private static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";
|
private static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 IP 获取归属地信息
|
* 查询 IP 归属地
|
||||||
*
|
*
|
||||||
* @param ip IP 地址
|
* @param ip IP 地址
|
||||||
* @return 归属地信息
|
* @return IP 归属地
|
||||||
*/
|
*/
|
||||||
public static String getCityInfo(String ip) {
|
public static String getAddress(String ip) {
|
||||||
if (ProjectProperties.IP_ADDR_LOCAL_PARSE_ENABLED) {
|
if (ProjectProperties.IP_ADDR_LOCAL_PARSE_ENABLED) {
|
||||||
return getLocalCityInfo(ip);
|
return getAddressByLocal(ip);
|
||||||
} else {
|
} else {
|
||||||
return getHttpCityInfo(ip);
|
return getAddressByHttp(ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 IP 获取归属地信息(网络解析)
|
* 查询 IP 归属地(网络解析)
|
||||||
*
|
*
|
||||||
* @param ip IP 地址
|
* @param ip IP 地址
|
||||||
* @return 归属地信息
|
* @return IP 归属地
|
||||||
*/
|
*/
|
||||||
public static String getHttpCityInfo(String ip) {
|
public static String getAddressByHttp(String ip) {
|
||||||
if (isInnerIp(ip)) {
|
if (isInnerIp(ip)) {
|
||||||
return "内网IP";
|
return "内网IP";
|
||||||
}
|
}
|
||||||
@@ -74,12 +74,12 @@ public class IpUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 IP 获取归属地信息(本地解析)
|
* 查询 IP 归属地(本地库解析)
|
||||||
*
|
*
|
||||||
* @param ip IP 地址
|
* @param ip IP 地址
|
||||||
* @return 归属地信息
|
* @return IP 归属地
|
||||||
*/
|
*/
|
||||||
public static String getLocalCityInfo(String ip) {
|
public static String getAddressByLocal(String ip) {
|
||||||
if (isInnerIp(ip)) {
|
if (isInnerIp(ip)) {
|
||||||
return "内网IP";
|
return "内网IP";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import lombok.AccessLevel;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -64,8 +65,42 @@ public class ServletUtils {
|
|||||||
if (null == request) {
|
if (null == request) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
|
return getBrowser(request.getHeader("User-Agent"));
|
||||||
return userAgent.getBrowser().getName() + " " + userAgent.getVersion();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取浏览器及其版本信息
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent 字符串
|
||||||
|
* @return 浏览器及其版本信息
|
||||||
|
*/
|
||||||
|
public static String getBrowser(String userAgentString) {
|
||||||
|
UserAgent userAgent = UserAgentUtil.parse(userAgentString);
|
||||||
|
return userAgent.getBrowser().getName() + StringConstants.SPACE + userAgent.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取操作系统
|
||||||
|
*
|
||||||
|
* @param request 请求对象
|
||||||
|
* @return 操作系统
|
||||||
|
*/
|
||||||
|
public static String getOs(HttpServletRequest request) {
|
||||||
|
if (null == request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getOs(request.getHeader("User-Agent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取操作系统
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent 字符串
|
||||||
|
* @return 操作系统
|
||||||
|
*/
|
||||||
|
public static String getOs(String userAgentString) {
|
||||||
|
UserAgent userAgent = UserAgentUtil.parse(userAgentString);
|
||||||
|
return userAgent.getOs().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ServletRequestAttributes getServletRequestAttributes() {
|
private static ServletRequestAttributes getServletRequestAttributes() {
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
<easy-captcha.version>1.6.2</easy-captcha.version>
|
<easy-captcha.version>1.6.2</easy-captcha.version>
|
||||||
<easy-excel.version>3.3.2</easy-excel.version>
|
<easy-excel.version>3.3.2</easy-excel.version>
|
||||||
<knife4j.version>4.3.0</knife4j.version>
|
<knife4j.version>4.3.0</knife4j.version>
|
||||||
|
<ttl.version>2.14.4</ttl.version>
|
||||||
<ip2region.version>3.1.5.1</ip2region.version>
|
<ip2region.version>3.1.5.1</ip2region.version>
|
||||||
<hutool.version>5.8.23</hutool.version>
|
<hutool.version>5.8.23</hutool.version>
|
||||||
</properties>
|
</properties>
|
||||||
@@ -171,6 +172,13 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- TTL(线程间传递 ThreadLocal,异步执行时上下文传递的解决方案) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
<version>${ttl.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 第三方封装 Ip2region(离线 IP 数据管理框架和定位库,支持亿级别的数据段,10 微秒级别的查询性能,提供了许多主流编程语言的 xdb 数据管理引擎的实现) -->
|
<!-- 第三方封装 Ip2region(离线 IP 数据管理框架和定位库,支持亿级别的数据段,10 微秒级别的查询性能,提供了许多主流编程语言的 xdb 数据管理引擎的实现) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.dreamlu</groupId>
|
<groupId>net.dreamlu</groupId>
|
||||||
@@ -249,6 +257,20 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 日志模块 - HttpTracePro(Spring Boot Actuator HttpTrace 定制增强版) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-log-httptrace-pro</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 日志模块 - 公共模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-log-common</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- API 文档模块 -->
|
<!-- API 文档模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.charles7c.continew</groupId>
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
|||||||
17
continew-starter-log/continew-starter-log-common/pom.xml
Normal file
17
continew-starter-log/continew-starter-log-common/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?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>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-log</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-log-common</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>ContiNew Starter 日志模块 - 公共模块</description>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志注解
|
||||||
|
* <p>用于接口方法或类上,辅助 Spring Doc 使用效果最佳</p>
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Log {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志描述(仅用于接口方法上)
|
||||||
|
* <p>
|
||||||
|
* 优先级:@Log("描述") > @Operation(summary="描述")
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所属模块(用于接口方法或类上)
|
||||||
|
* <p>
|
||||||
|
* 优先级: 接口方法上的 @Log(module = "模块") > 接口类上的 @Log(module = "模块") > @Tag(name = "模块") 内容
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
String module() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否忽略日志记录(用于接口方法或类上)
|
||||||
|
*/
|
||||||
|
boolean ignore() default false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.dao;
|
||||||
|
|
||||||
|
import top.charles7c.continew.starter.log.common.model.LogRecord;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志持久层接口
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public interface LogDao {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询日志列表
|
||||||
|
*
|
||||||
|
* @return 日志列表
|
||||||
|
*/
|
||||||
|
default List<LogRecord> list() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录日志
|
||||||
|
*
|
||||||
|
* @param logRecord 日志信息
|
||||||
|
*/
|
||||||
|
void add(LogRecord logRecord);
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.dao.impl;
|
||||||
|
|
||||||
|
import top.charles7c.continew.starter.log.common.dao.LogDao;
|
||||||
|
import top.charles7c.continew.starter.log.common.model.LogRecord;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志持久层接口默认实现类(基于内存)
|
||||||
|
*
|
||||||
|
* @author Dave Syer(Spring Boot Actuator)
|
||||||
|
* @author Olivier Bourgain(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public class LogDaoDefaultImpl implements LogDao {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 容量
|
||||||
|
*/
|
||||||
|
private int capacity = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否降序
|
||||||
|
*/
|
||||||
|
private boolean reverse = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志列表
|
||||||
|
*/
|
||||||
|
private final List<LogRecord> logRecords = new LinkedList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LogRecord> list() {
|
||||||
|
synchronized (this.logRecords) {
|
||||||
|
return List.copyOf(this.logRecords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(LogRecord logRecord) {
|
||||||
|
synchronized (this.logRecords) {
|
||||||
|
while (this.logRecords.size() >= this.capacity) {
|
||||||
|
this.logRecords.remove(this.reverse ? this.capacity - 1 : 0);
|
||||||
|
}
|
||||||
|
if (this.reverse) {
|
||||||
|
this.logRecords.add(0, logRecord);
|
||||||
|
} else {
|
||||||
|
this.logRecords.add(logRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置内存中存储的最大日志容量
|
||||||
|
*
|
||||||
|
* @param capacity 容量
|
||||||
|
*/
|
||||||
|
public void setCapacity(int capacity) {
|
||||||
|
synchronized (this.logRecords) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否降序
|
||||||
|
*
|
||||||
|
* @param reverse 是否降序(默认:true)
|
||||||
|
*/
|
||||||
|
public void setReverse(boolean reverse) {
|
||||||
|
synchronized (this.logRecords) {
|
||||||
|
this.reverse = reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.enums;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志包含信息
|
||||||
|
*
|
||||||
|
* @author Wallace Wadge(Spring Boot Actuator)
|
||||||
|
* @author Emily Tsanova(Spring Boot Actuator)
|
||||||
|
* @author Joseph Beeton(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public enum Include {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
*/
|
||||||
|
DESCRIPTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块
|
||||||
|
*/
|
||||||
|
MODULE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求头
|
||||||
|
*/
|
||||||
|
REQUEST_HEADERS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求体
|
||||||
|
*/
|
||||||
|
REQUEST_BODY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求参数
|
||||||
|
*/
|
||||||
|
REQUEST_PARAM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP 归属地
|
||||||
|
*/
|
||||||
|
IP_ADDRESS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览器
|
||||||
|
*/
|
||||||
|
BROWSER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作系统
|
||||||
|
*/
|
||||||
|
OS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应头
|
||||||
|
*/
|
||||||
|
RESPONSE_HEADERS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应体
|
||||||
|
*/
|
||||||
|
RESPONSE_BODY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应参数
|
||||||
|
*/
|
||||||
|
RESPONSE_PARAM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 耗时
|
||||||
|
*/
|
||||||
|
TIME_TAKEN;
|
||||||
|
|
||||||
|
private static final Set<Include> DEFAULT_INCLUDES;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set<Include> defaultIncludes = new LinkedHashSet<>();
|
||||||
|
defaultIncludes.add(Include.TIME_TAKEN);
|
||||||
|
defaultIncludes.add(Include.REQUEST_HEADERS);
|
||||||
|
defaultIncludes.add(Include.RESPONSE_HEADERS);
|
||||||
|
DEFAULT_INCLUDES = Collections.unmodifiableSet(defaultIncludes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认包含信息
|
||||||
|
*
|
||||||
|
* @return 默认包含信息
|
||||||
|
*/
|
||||||
|
public static Set<Include> defaultIncludes() {
|
||||||
|
return DEFAULT_INCLUDES;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.model;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import top.charles7c.continew.starter.log.common.enums.Include;
|
||||||
|
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志信息
|
||||||
|
*
|
||||||
|
* @author Dave Syer(Spring Boot Actuator)
|
||||||
|
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||||
|
* @author Phillip Webb(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LogRecord {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块
|
||||||
|
*/
|
||||||
|
private String module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求信息
|
||||||
|
*/
|
||||||
|
private LogRequest request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应信息
|
||||||
|
*/
|
||||||
|
private LogResponse response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 耗时
|
||||||
|
*/
|
||||||
|
private Duration timeTaken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间戳
|
||||||
|
*/
|
||||||
|
private final Instant timestamp;
|
||||||
|
|
||||||
|
public LogRecord(Instant timestamp, LogRequest request, LogResponse response, Duration timeTaken) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.request = request;
|
||||||
|
this.response = response;
|
||||||
|
this.timeTaken = timeTaken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始记录日志
|
||||||
|
*
|
||||||
|
* @param request 请求信息
|
||||||
|
* @return 日志记录器
|
||||||
|
*/
|
||||||
|
public static Started start(RecordableHttpRequest request) {
|
||||||
|
return start(Clock.systemUTC(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始记录日志
|
||||||
|
*
|
||||||
|
* @param timestamp 开始时间
|
||||||
|
* @param request 请求信息
|
||||||
|
* @return 日志记录器
|
||||||
|
*/
|
||||||
|
public static Started start(Clock timestamp, RecordableHttpRequest request) {
|
||||||
|
return new Started(timestamp, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志记录器
|
||||||
|
*/
|
||||||
|
public static final class Started {
|
||||||
|
|
||||||
|
private final Instant timestamp;
|
||||||
|
|
||||||
|
private final RecordableHttpRequest request;
|
||||||
|
|
||||||
|
private Started(Clock clock, RecordableHttpRequest request) {
|
||||||
|
this.timestamp = Instant.now(clock);
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束日志记录
|
||||||
|
*
|
||||||
|
* @param response 响应信息
|
||||||
|
* @param includes 包含信息
|
||||||
|
* @return 日志记录
|
||||||
|
*/
|
||||||
|
public LogRecord finish(RecordableHttpResponse response, Set<Include> includes) {
|
||||||
|
return finish(Clock.systemUTC(), response, includes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束日志记录
|
||||||
|
*
|
||||||
|
* @param clock 时间
|
||||||
|
* @param response 响应信息
|
||||||
|
* @param includes 包含信息
|
||||||
|
* @return 日志记录
|
||||||
|
*/
|
||||||
|
public LogRecord finish(Clock clock, RecordableHttpResponse response, Set<Include> includes) {
|
||||||
|
LogRequest logRequest = new LogRequest(this.request, includes);
|
||||||
|
LogResponse logResponse = new LogResponse(response, includes);
|
||||||
|
Duration duration = (includes.contains(Include.TIME_TAKEN)) ? Duration.between(this.timestamp, Instant.now(clock)) : null;
|
||||||
|
return new LogRecord(this.timestamp, logRequest, logResponse, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import top.charles7c.continew.starter.core.util.ExceptionUtils;
|
||||||
|
import top.charles7c.continew.starter.core.util.IpUtils;
|
||||||
|
import top.charles7c.continew.starter.core.util.ServletUtils;
|
||||||
|
import top.charles7c.continew.starter.log.common.enums.Include;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求信息
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LogRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求方式
|
||||||
|
*/
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求 URI
|
||||||
|
*/
|
||||||
|
private URI uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP
|
||||||
|
*/
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求头
|
||||||
|
*/
|
||||||
|
private Map<String, List<String>> headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求体(JSON 字符串)
|
||||||
|
*/
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求参数
|
||||||
|
*/
|
||||||
|
private Map<String, Object> param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP 归属地
|
||||||
|
*/
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览器
|
||||||
|
*/
|
||||||
|
private String browser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作系统
|
||||||
|
*/
|
||||||
|
private String os;
|
||||||
|
|
||||||
|
public LogRequest(RecordableHttpRequest request, Set<Include> includes) {
|
||||||
|
this.method = request.getMethod();
|
||||||
|
this.uri = request.getUri();
|
||||||
|
this.ip = request.getIp();
|
||||||
|
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? request.getHeaders() : null;
|
||||||
|
this.body = (includes.contains(Include.REQUEST_BODY)) ? request.getBody() : null;
|
||||||
|
this.param = (includes.contains(Include.RESPONSE_PARAM)) ? request.getParam() : null;
|
||||||
|
this.address = (includes.contains(Include.IP_ADDRESS)) ? IpUtils.getAddress(this.ip) : null;
|
||||||
|
String userAgentString = ExceptionUtils.exToNull(() -> this.headers.get(HttpHeaders.USER_AGENT).get(0));
|
||||||
|
this.browser = (includes.contains(Include.BROWSER)) ? ServletUtils.getBrowser(userAgentString) : null;
|
||||||
|
this.os = (includes.contains(Include.OS)) ? ServletUtils.getOs(userAgentString) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import top.charles7c.continew.starter.log.common.enums.Include;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应信息
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LogResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态码
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应头
|
||||||
|
*/
|
||||||
|
private Map<String, List<String>> headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应体(JSON 字符串)
|
||||||
|
*/
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应参数
|
||||||
|
*/
|
||||||
|
private Map<String, Object> param;
|
||||||
|
|
||||||
|
public LogResponse(RecordableHttpResponse response, Set<Include> includes) {
|
||||||
|
this.status = response.getStatus();
|
||||||
|
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? response.getHeaders() : null;
|
||||||
|
this.body = (includes.contains(Include.REQUEST_BODY)) ? response.getBody() : null;
|
||||||
|
this.param = (includes.contains(Include.RESPONSE_PARAM)) ? response.getParam() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.model;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可记录的 HTTP 请求信息
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||||
|
* @author Phillip Webb(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
* @see RecordableHttpResponse
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public interface RecordableHttpRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求方式
|
||||||
|
*
|
||||||
|
* @return 请求方式
|
||||||
|
*/
|
||||||
|
String getMethod();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 URI
|
||||||
|
*
|
||||||
|
* @return URI
|
||||||
|
*/
|
||||||
|
URI getUri();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 IP
|
||||||
|
*
|
||||||
|
* @return IP
|
||||||
|
*/
|
||||||
|
String getIp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求头
|
||||||
|
*
|
||||||
|
* @return 请求头
|
||||||
|
*/
|
||||||
|
Map<String, List<String>> getHeaders();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求体
|
||||||
|
*
|
||||||
|
* @return 请求体
|
||||||
|
*/
|
||||||
|
String getBody();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求参数
|
||||||
|
*
|
||||||
|
* @return 请求参数
|
||||||
|
*/
|
||||||
|
Map<String, Object> getParam();
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.common.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可记录的 HTTP 响应信息
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
* @see RecordableHttpRequest
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public interface RecordableHttpResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取状态码
|
||||||
|
*
|
||||||
|
* @return 状态码
|
||||||
|
*/
|
||||||
|
int getStatus();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应头
|
||||||
|
*
|
||||||
|
* @return 响应头
|
||||||
|
*/
|
||||||
|
Map<String, List<String>> getHeaders();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应体
|
||||||
|
*
|
||||||
|
* @return 响应体
|
||||||
|
*/
|
||||||
|
String getBody();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应参数
|
||||||
|
*
|
||||||
|
* @return 响应参数
|
||||||
|
*/
|
||||||
|
Map<String, Object> getParam();
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?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>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-log</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-log-httptrace-pro</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>ContiNew Starter 日志模块 - HttpTracePro(Spring Boot Actuator HttpTrace 定制增强版)</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Swagger 注解 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.swagger.core.v3</groupId>
|
||||||
|
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- TTL(线程间传递 ThreadLocal,异步执行时上下文传递的解决方案) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 日志模块 - 公共模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-log-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.httptracepro.autoconfigure;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用日志记录注解
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||||
|
@Documented
|
||||||
|
@ConditionalOnProperty(prefix = "continew-starter.log", name = "enabled", havingValue = "true")
|
||||||
|
public @interface ConditionalOnEnabledLog {}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.httptracepro.autoconfigure;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
import top.charles7c.continew.starter.log.common.dao.LogDao;
|
||||||
|
import top.charles7c.continew.starter.log.common.dao.impl.LogDaoDefaultImpl;
|
||||||
|
import top.charles7c.continew.starter.log.httptracepro.handler.LogFilter;
|
||||||
|
import top.charles7c.continew.starter.log.httptracepro.handler.LogInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志自动配置
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnEnabledLog
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@EnableConfigurationProperties(LogProperties.class)
|
||||||
|
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||||
|
public class LogAutoConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
private final LogProperties properties;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(new LogInterceptor(logDao(), properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志过滤器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public LogFilter logFilter() {
|
||||||
|
return new LogFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志持久层接口
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public LogDao logDao() {
|
||||||
|
return new LogDaoDefaultImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postConstruct() {
|
||||||
|
log.info("[ContiNew Starter] - Auto Configuration 'Log-HttpTracePro' completed initialization.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.httptracepro.autoconfigure;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import top.charles7c.continew.starter.log.common.enums.Include;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志配置属性
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "continew-starter.log")
|
||||||
|
public class LogProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用日志
|
||||||
|
*/
|
||||||
|
private boolean enabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包含信息
|
||||||
|
*/
|
||||||
|
private Set<Include> include = new HashSet<>(Include.defaultIncludes());
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.httptracepro.handler;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||||
|
import org.springframework.web.util.ContentCachingResponseWrapper;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志过滤器
|
||||||
|
*
|
||||||
|
* @author Dave Syer(Spring Boot Actuator)
|
||||||
|
* @author Wallace Wadge(Spring Boot Actuator)
|
||||||
|
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||||
|
* @author Venil Noronha(Spring Boot Actuator)
|
||||||
|
* @author Madhura Bhave(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LogFilter extends OncePerRequestFilter implements Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return Ordered.LOWEST_PRECEDENCE - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
||||||
|
@NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
if (!isRequestValid(request)) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 包装输入、输出流,可重复读取
|
||||||
|
if (!(request instanceof ContentCachingRequestWrapper)) {
|
||||||
|
request = new ContentCachingRequestWrapper(request);
|
||||||
|
}
|
||||||
|
if (!(response instanceof ContentCachingResponseWrapper)) {
|
||||||
|
response = new ContentCachingResponseWrapper(response);
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
// 更新响应(不操作这一步,会导致接口响应空白)
|
||||||
|
updateResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRequestValid(HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
new URI(request.getRequestURL().toString());
|
||||||
|
return true;
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateResponse(HttpServletResponse response) throws IOException {
|
||||||
|
ContentCachingResponseWrapper responseWrapper =
|
||||||
|
WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
|
||||||
|
Objects.requireNonNull(responseWrapper).copyBodyToResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.httptracepro.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
import top.charles7c.continew.starter.log.common.annotation.Log;
|
||||||
|
import top.charles7c.continew.starter.log.common.dao.LogDao;
|
||||||
|
import top.charles7c.continew.starter.log.common.enums.Include;
|
||||||
|
import top.charles7c.continew.starter.log.common.model.LogRecord;
|
||||||
|
import top.charles7c.continew.starter.log.httptracepro.autoconfigure.LogProperties;
|
||||||
|
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志拦截器
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LogInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
|
private final LogDao dao;
|
||||||
|
private final LogProperties properties;
|
||||||
|
private final TransmittableThreadLocal<Clock> timestampTtl = new TransmittableThreadLocal<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
||||||
|
@NonNull Object handler) {
|
||||||
|
if (this.isRequestRecord(handler)) {
|
||||||
|
timestampTtl.set(Clock.systemUTC());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
|
||||||
|
@NonNull Object handler, Exception e) {
|
||||||
|
Clock timestamp = timestampTtl.get();
|
||||||
|
if (null == timestamp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timestampTtl.remove();
|
||||||
|
Set<Include> includeSet = properties.getInclude();
|
||||||
|
RecordableServletHttpRequest sourceRequest = new RecordableServletHttpRequest(request);
|
||||||
|
LogRecord.Started startedLogRecord = LogRecord.start(timestamp, sourceRequest);
|
||||||
|
RecordableServletHttpResponse sourceResponse = new RecordableServletHttpResponse(response, response.getStatus());
|
||||||
|
LogRecord finishedLogRecord = startedLogRecord.finish(sourceResponse, includeSet);
|
||||||
|
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||||
|
if (includeSet.contains(Include.DESCRIPTION)) {
|
||||||
|
// 记录日志描述
|
||||||
|
this.logDescription(finishedLogRecord, handlerMethod);
|
||||||
|
}
|
||||||
|
if (includeSet.contains(Include.MODULE)) {
|
||||||
|
// 记录所属模块
|
||||||
|
this.logModule(finishedLogRecord, handlerMethod);
|
||||||
|
}
|
||||||
|
dao.add(finishedLogRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录描述
|
||||||
|
*
|
||||||
|
* @param logRecord 日志信息
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
*/
|
||||||
|
private void logDescription(LogRecord logRecord, HandlerMethod handlerMethod) {
|
||||||
|
// 例如:@Operation(summary="新增部门") -> 新增部门
|
||||||
|
Operation methodOperation = handlerMethod.getMethodAnnotation(Operation.class);
|
||||||
|
if (null != methodOperation) {
|
||||||
|
logRecord.setDescription(StrUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述"));
|
||||||
|
}
|
||||||
|
// 例如:@Log("新增部门") -> 新增部门
|
||||||
|
Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
|
||||||
|
if (null != methodLog && StrUtil.isNotBlank(methodLog.value())) {
|
||||||
|
logRecord.setDescription(methodLog.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录模块
|
||||||
|
*
|
||||||
|
* @param logRecord 日志信息
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
*/
|
||||||
|
private void logModule(LogRecord logRecord, HandlerMethod handlerMethod) {
|
||||||
|
// 例如:@Tag(name = "部门管理") -> 部门管理
|
||||||
|
Tag classTag = handlerMethod.getBeanType().getDeclaredAnnotation(Tag.class);
|
||||||
|
if (null != classTag) {
|
||||||
|
String name = classTag.name();
|
||||||
|
logRecord.setModule(StrUtil.blankToDefault(name, "请在该接口类上指定所属模块"));
|
||||||
|
}
|
||||||
|
// 例如:@Log(module = "部门管理") -> 部门管理
|
||||||
|
Log classLog = handlerMethod.getBeanType().getDeclaredAnnotation(Log.class);
|
||||||
|
if (null != classLog && StrUtil.isNotBlank(classLog.module())) {
|
||||||
|
logRecord.setModule(classLog.module());
|
||||||
|
}
|
||||||
|
Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
|
||||||
|
if (null != methodLog && StrUtil.isNotBlank(methodLog.module())) {
|
||||||
|
logRecord.setModule(methodLog.module());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否要记录日志
|
||||||
|
*
|
||||||
|
* @param handler 处理器
|
||||||
|
* @return true:需要记录;false:不需要记录
|
||||||
|
*/
|
||||||
|
private boolean isRequestRecord(Object handler) {
|
||||||
|
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 如果接口被隐藏,不记录日志
|
||||||
|
Operation methodOperation = handlerMethod.getMethodAnnotation(Operation.class);
|
||||||
|
if (null != methodOperation && methodOperation.hidden()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 如果接口方法或类上有 @Log 注解,且要求忽略该接口,则不记录日志
|
||||||
|
Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
|
||||||
|
if (null != methodLog && methodLog.ignore()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log classLog = handlerMethod.getBeanType().getDeclaredAnnotation(Log.class);
|
||||||
|
return null == classLog || !classLog.ignore();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.httptracepro.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||||
|
import org.springframework.web.util.UriUtils;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
|
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||||
|
import top.charles7c.continew.starter.log.common.model.RecordableHttpRequest;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可记录的 HTTP 请求信息适配器
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
*/
|
||||||
|
public final class RecordableServletHttpRequest implements RecordableHttpRequest {
|
||||||
|
|
||||||
|
private final HttpServletRequest request;
|
||||||
|
|
||||||
|
public RecordableServletHttpRequest(HttpServletRequest request) {
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethod() {
|
||||||
|
return request.getMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI getUri() {
|
||||||
|
String queryString = request.getQueryString();
|
||||||
|
if (StrUtil.isBlank(queryString)) {
|
||||||
|
return URI.create(request.getRequestURL().toString());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
StringBuffer urlBuffer = this.appendQueryString(queryString);
|
||||||
|
return new URI(urlBuffer.toString());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
String encoded = UriUtils.encodeQuery(queryString, StandardCharsets.UTF_8);
|
||||||
|
StringBuffer urlBuffer = this.appendQueryString(encoded);
|
||||||
|
return URI.create(urlBuffer.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIp() {
|
||||||
|
return JakartaServletUtil.getClientIP(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getHeaders() {
|
||||||
|
return JakartaServletUtil.getHeadersMap(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBody() {
|
||||||
|
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
|
||||||
|
if (null != wrapper) {
|
||||||
|
return StrUtil.utf8Str(wrapper.getContentAsByteArray());
|
||||||
|
}
|
||||||
|
return StringConstants.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getParam() {
|
||||||
|
String body = this.getBody();
|
||||||
|
return StrUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
|
||||||
|
? JSONUtil.toBean(body, Map.class)
|
||||||
|
: Collections.unmodifiableMap(request.getParameterMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuffer appendQueryString(String queryString) {
|
||||||
|
return request.getRequestURL().append(StringConstants.QUESTION_MARK).append(queryString);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.log.httptracepro.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.web.util.ContentCachingResponseWrapper;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
|
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||||
|
import top.charles7c.continew.starter.log.common.model.RecordableHttpResponse;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可记录的 HTTP 响应信息适配器
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||||
|
* @author Charles7c
|
||||||
|
*/
|
||||||
|
public final class RecordableServletHttpResponse implements RecordableHttpResponse {
|
||||||
|
|
||||||
|
private final HttpServletResponse response;
|
||||||
|
|
||||||
|
private final int status;
|
||||||
|
|
||||||
|
public RecordableServletHttpResponse(HttpServletResponse response, int status) {
|
||||||
|
this.response = response;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStatus() {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getHeaders() {
|
||||||
|
Map<String, List<String>> headers = new LinkedHashMap<>();
|
||||||
|
for (String name : response.getHeaderNames()) {
|
||||||
|
headers.put(name, new ArrayList<>(response.getHeaders(name)));
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBody() {
|
||||||
|
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
|
||||||
|
if (null != wrapper) {
|
||||||
|
return StrUtil.utf8Str(wrapper.getContentAsByteArray());
|
||||||
|
}
|
||||||
|
return StringConstants.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getParam() {
|
||||||
|
String body = this.getBody();
|
||||||
|
return StrUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
|
||||||
|
? JSONUtil.toBean(body, Map.class)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
top.charles7c.continew.starter.log.httptracepro.autoconfigure.LogAutoConfiguration
|
||||||
30
continew-starter-log/pom.xml
Normal file
30
continew-starter-log/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?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>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-log</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>ContiNew Starter 日志模块</description>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>continew-starter-log-common</module>
|
||||||
|
<module>continew-starter-log-httptrace-pro</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 核心模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.charles7c.continew</groupId>
|
||||||
|
<artifactId>continew-starter-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
1
pom.xml
1
pom.xml
@@ -72,6 +72,7 @@
|
|||||||
<module>continew-starter-core</module>
|
<module>continew-starter-core</module>
|
||||||
<module>continew-starter-json</module>
|
<module>continew-starter-json</module>
|
||||||
<module>continew-starter-api-doc</module>
|
<module>continew-starter-api-doc</module>
|
||||||
|
<module>continew-starter-log</module>
|
||||||
<module>continew-starter-file</module>
|
<module>continew-starter-file</module>
|
||||||
<module>continew-starter-captcha</module>
|
<module>continew-starter-captcha</module>
|
||||||
<module>continew-starter-cache</module>
|
<module>continew-starter-cache</module>
|
||||||
|
|||||||
Reference in New Issue
Block a user