refactor(log): continew-starter-log-common => continew-starter-log-core

This commit is contained in:
2024-02-19 21:04:17 +08:00
parent 4ffc5dc1d4
commit 56a22c4bce
19 changed files with 34 additions and 34 deletions

View File

@@ -0,0 +1,14 @@
<?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-core</artifactId>
<description>ContiNew Starter 日志模块 - 核心模块</description>
</project>

View File

@@ -0,0 +1,65 @@
/*
* 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.core.annotation;
import top.charles7c.continew.starter.log.core.enums.Include;
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 "";
/**
* 包含信息(在全局配置基础上扩展包含信息)
*/
Include[] includes() default {};
/**
* 排除信息(在全局配置基础上减少包含信息)
*/
Include[] excludes() default {};
/**
* 是否忽略日志记录(用于接口方法或类上)
*/
boolean ignore() default false;
}

View File

@@ -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.core.dao;
import top.charles7c.continew.starter.log.core.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);
}

View File

@@ -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.core.dao.impl;
import top.charles7c.continew.starter.log.core.dao.LogDao;
import top.charles7c.continew.starter.log.core.model.LogRecord;
import java.util.LinkedList;
import java.util.List;
/**
* 日志持久层接口默认实现类(基于内存)
*
* @author Dave SyerSpring Boot Actuator
* @author Olivier BourgainSpring 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;
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.core.enums;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 日志包含信息
*
* @author Wallace WadgeSpring Boot Actuator
* @author Emily TsanovaSpring Boot Actuator
* @author Joseph BeetonSpring 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,;
private static final Set<Include> DEFAULT_INCLUDES;
static {
Set<Include> defaultIncludes = new LinkedHashSet<>();
defaultIncludes.add(Include.REQUEST_HEADERS);
defaultIncludes.add(Include.RESPONSE_HEADERS);
defaultIncludes.add(Include.REQUEST_PARAM);
defaultIncludes.add(Include.RESPONSE_PARAM);
DEFAULT_INCLUDES = Collections.unmodifiableSet(defaultIncludes);
}
/**
* 获取默认包含信息
*
* @return 默认包含信息
*/
public static Set<Include> defaultIncludes() {
return DEFAULT_INCLUDES;
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.core.model;
import top.charles7c.continew.starter.log.core.enums.Include;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Set;
/**
* 日志信息
*
* @author Dave SyerSpring Boot Actuator
* @author Andy WilkinsonSpring Boot Actuator
* @author Phillip WebbSpring Boot Actuator
* @author Charles7c
* @since 1.1.0
*/
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 = Duration.between(this.timestamp, Instant.now(clock));
return new LogRecord(this.timestamp, logRequest, logResponse, duration);
}
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getModule() {
return module;
}
public void setModule(String module) {
this.module = module;
}
public LogRequest getRequest() {
return request;
}
public void setRequest(LogRequest request) {
this.request = request;
}
public LogResponse getResponse() {
return response;
}
public void setResponse(LogResponse response) {
this.response = response;
}
public Duration getTimeTaken() {
return timeTaken;
}
public void setTimeTaken(Duration timeTaken) {
this.timeTaken = timeTaken;
}
public Instant getTimestamp() {
return timestamp;
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.core.model;
import cn.hutool.core.text.CharSequenceUtil;
import org.springframework.http.HttpHeaders;
import top.charles7c.continew.starter.core.util.IpUtils;
import top.charles7c.continew.starter.log.core.enums.Include;
import top.charles7c.continew.starter.web.util.ServletUtils;
import java.net.URI;
import java.util.Map;
import java.util.Set;
/**
* 请求信息
*
* @author Charles7c
* @since 1.1.0
*/
public class LogRequest {
/**
* 请求方式
*/
private String method;
/**
* 请求 URL
*/
private URI url;
/**
* IP
*/
private String ip;
/**
* 请求头
*/
private Map<String, 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.url = request.getUrl();
this.ip = request.getIp();
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? request.getHeaders() : null;
if (includes.contains(Include.REQUEST_BODY)) {
this.body = request.getBody();
} else if (includes.contains(Include.REQUEST_PARAM)) {
this.param = request.getParam();
}
this.address = (includes.contains(Include.IP_ADDRESS)) ? IpUtils.getAddress(this.ip) : null;
if (null == this.headers) {
return;
}
String userAgentString = this.headers.entrySet()
.stream()
.filter(h -> HttpHeaders.USER_AGENT.equalsIgnoreCase(h.getKey()))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
if (CharSequenceUtil.isNotBlank(userAgentString)) {
this.browser = (includes.contains(Include.BROWSER)) ? ServletUtils.getBrowser(userAgentString) : null;
this.os = (includes.contains(Include.OS)) ? ServletUtils.getOs(userAgentString) : null;
}
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public URI getUrl() {
return url;
}
public void setUrl(URI url) {
this.url = url;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public Map<String, Object> getParam() {
return param;
}
public void setParam(Map<String, Object> param) {
this.param = param;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
}

View File

@@ -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.core.model;
import top.charles7c.continew.starter.log.core.enums.Include;
import java.util.*;
/**
* 响应信息
*
* @author Charles7c
* @since 1.1.0
*/
public class LogResponse {
/**
* 状态码
*/
private Integer status;
/**
* 响应头
*/
private Map<String, 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.RESPONSE_HEADERS)) ? response.getHeaders() : null;
if (includes.contains(Include.RESPONSE_BODY)) {
this.body = response.getBody();
} else if (includes.contains(Include.RESPONSE_PARAM)) {
this.param = response.getParam();
}
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public Map<String, Object> getParam() {
return param;
}
public void setParam(Map<String, Object> param) {
this.param = param;
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.core.model;
import java.net.URI;
import java.util.Map;
/**
* 可记录的 HTTP 请求信息
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Phillip WebbSpring Boot Actuator
* @author Charles7c
* @see RecordableHttpResponse
* @since 1.1.0
*/
public interface RecordableHttpRequest {
/**
* 获取请求方式
*
* @return 请求方式
*/
String getMethod();
/**
* 获取 URL
*
* @return URL
*/
URI getUrl();
/**
* 获取 IP
*
* @return IP
*/
String getIp();
/**
* 获取请求头
*
* @return 请求头
*/
Map<String, String> getHeaders();
/**
* 获取请求体
*
* @return 请求体
*/
String getBody();
/**
* 获取请求参数
*
* @return 请求参数
*/
Map<String, Object> getParam();
}

View File

@@ -0,0 +1,58 @@
/*
* 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.core.model;
import java.util.Map;
/**
* 可记录的 HTTP 响应信息
*
* @author Andy WilkinsonSpring Boot Actuator
* @author Charles7c
* @see RecordableHttpRequest
* @since 1.1.0
*/
public interface RecordableHttpResponse {
/**
* 获取状态码
*
* @return 状态码
*/
int getStatus();
/**
* 获取响应头
*
* @return 响应头
*/
Map<String, String> getHeaders();
/**
* 获取响应体
*
* @return 响应体
*/
String getBody();
/**
* 获取响应参数
*
* @return 响应参数
*/
Map<String, Object> getParam();
}