[TOC]
# **1. 启用注解**
`@GoEnableLogger`
`@GoLogger`
# **2. 注意事项**
> 如需使用,需开启@GoEnableLogger,类、方法上加入@GoLogger注解即可,注解优先级 `方法 > 类`
print:是否打印,默认开启true
format:是否格式化,默认关闭false
operation:方法说明,默认空""
storage:日志存储,默认LogStorageProvider(可实现LogStorage接口自定义保存)
# **3. 示例说明**
## **3.1 不输出**
```
@GoLogger(print = false)
@RequestMapping("/noprint")
public Result noprint(BaseVo base, PageVo page) {
return R.succ();
}
```
```
日志无输出
```
## **3.2 默认输出**
```
@GoLogger
@RequestMapping("/print")
public Result print(BaseVo base, PageVo page) {
return R.succ();
}
```
```
[FastBoot][ INFO][06-27 01:02:19]-->[http-nio-8080-exec-1:3995353][loggerAroundAspect(LoggerAspect.java:109)] | - | request print | [{"id":"-1","keyword":""},{"limit":25,"page":0,"size":25}]
[FastBoot][ INFO][06-27 01:02:19]-->[http-nio-8080-exec-1:3995366][loggerAroundAspect(LoggerAspect.java:120)] | - | response time 11ms | print | {"code":0,"msg":"操作成功","status":true}
```
## **3.3 格式化输出**
```
@GoLogger(format = true)
@RequestMapping("/format")
public Result format(BaseVo base, PageVo page) {
return R.succ();
}
```
```
[FastBoot][ INFO][06-27 01:03:55]-->[http-nio-8080-exec-5:4091420][loggerAroundAspect(LoggerAspect.java:109)] | - | request format |
[
{
"id":"-1",
"keyword":""
},
{
"limit":25,
"page":0,
"size":25
}
]
[FastBoot][ INFO][06-27 01:03:55]-->[http-nio-8080-exec-5:4091421][loggerAroundAspect(LoggerAspect.java:120)] | - | response time 1ms | format |
{
"code":0,
"msg":"操作成功",
"status":true
}
```
## **3.4 格式化输出、存储**
```
@GoLogger(format = true, storage = LogStorageMysqlProvider.class)
@RequestMapping("/storage")
public Result storage(BaseVo base, PageVo page) {
return R.succ();
}
```
```
[FastBoot][ INFO][06-27 01:06:17]-->[http-nio-8080-exec-1:4234217][loggerAroundAspect(LoggerAspect.java:109)] | - | request storage |
[
{
"id":"-1",
"keyword":""
},
{
"limit":25,
"page":0,
"size":25
}
]
[FastBoot][ INFO][06-27 01:06:17]-->[http-nio-8080-exec-1:4234232][loggerAroundAspect(LoggerAspect.java:120)] | - | response time 14ms | storage |
{
"code":0,
"msg":"操作成功",
"status":true
}
[FastBoot][ INFO][06-27 01:06:17]-->[http-nio-8080-exec-1:4234267][logSQL(Slf4JLogger.java:60)] | - | time 0ms | statement | insert into xx_log (create_date, ip, is_del, method, req, res, time, type, update_date, url, id) values ('2021-06-27T01:06:17.907+0800', '0:0:0:0:0:0:0:1', false, 'storage', '{}', '{"code":0,"msg":"操作成功","status":true}', 14, 'GET', '2021-06-27T01:06:17.907+0800', '//logger/storage', 'L1408834011744309248')
[FastBoot][ INFO][06-27 01:06:17]-->[http-nio-8080-exec-1:4234284][logSQL(Slf4JLogger.java:60)] | - | time 14ms | commit |
[FastBoot][ INFO][06-27 01:06:17]-->[http-nio-8080-exec-1:4234285][record(LogStorageMysqlProvider.java:49)] | - log record id L1408834011744309248
```
## **3.5 优先级**
```
@RestController
@RequestMapping("/body")
@GoLogger(storage = LogStorageMysqlProvider.class)
public class BodyController extends BaseController {
@RequestMapping(value = "result")
public Result result() {
return R.succ(MockData.map());
}
}
```
```
[FastBoot][ INFO][06-27 01:33:14]-->[http-nio-8080-exec-5:5850503][loggerAroundAspect(LoggerAspect.java:109)] | - | request result | []
[FastBoot][ INFO][06-27 01:33:14]-->[http-nio-8080-exec-5:5850504][loggerAroundAspect(LoggerAspect.java:120)] | - | response time 0ms | result | {"code":0,"msg":"操作成功","data":{"k1":"1","k2":2,"k3":"3","k4":4.1,"k5":5.2,"k6":true,"k7":"7","k8":1624728794169},"status":true}
[FastBoot][ INFO][06-27 01:33:14]-->[http-nio-8080-exec-5:5850510][logSQL(Slf4JLogger.java:60)] | - | time 1ms | statement | insert into xx_log (create_date, ip, is_del, method, req, res, time, type, update_date, url, id) values ('2021-06-27T01:33:14.172+0800', '0:0:0:0:0:0:0:1', false, 'result', '{}', '{"code":0,"msg":"操作成功","data":{"k1":"1","k2":2,"k3":"3","k4":4.1,"k5":5.2,"k6":true,"k7":"7","k8":1624728794169},"status":true}', 0, 'GET', '2021-06-27T01:33:14.172+0800', '/body/result', 'L1408840790767177728')
[FastBoot][ INFO][06-27 01:33:14]-->[http-nio-8080-exec-5:5850516][logSQL(Slf4JLogger.java:60)] | - | time 4ms | commit |
[FastBoot][ INFO][06-27 01:33:14]-->[http-nio-8080-exec-5:5850518][record(LogStorageMysqlProvider.java:49)] | - log record id L1408840790767177728
```
# **4. 自定义存储**
## **4.1 表结构**
```
CREATE TABLE `xx_log` (
`id` varchar(255) NOT NULL COMMENT '主键',
`create_date` datetime(6) NOT NULL COMMENT '创建时间',
`update_date` datetime NOT NULL COMMENT '更新时间',
`ip` varchar(255) DEFAULT NULL COMMENT '请求ip',
`url` varchar(255) DEFAULT NULL COMMENT '请求地址',
`method` varchar(255) DEFAULT NULL COMMENT '请求方法',
`type` varchar(255) DEFAULT NULL COMMENT '请求类型',
`req` longtext COMMENT '请求参数',
`res` longtext COMMENT '响应结果',
`time` bigint(20) DEFAULT NULL COMMENT '请求耗时',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
```
## **4.2 实体类**
```
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@FieldNameConstants(innerTypeName = "FIELDS")
@Table(name = "xx_log")
@Entity
@EntityListeners(AuditingEntityListener.class)
@DynamicInsert
@DynamicUpdate
@Where(clause = "is_del=0")
@SQLDelete(sql = "update xx_log set is_del=1 where id = ?")
@SQLDeleteAll(sql = "update xx_log set is_del=1 where id = ?")
public class LogRecord extends JpaPlusEntity<LogRecord> {
private static final long serialVersionUID = 1L;
/**
* 主键,例(L1408447004119666688)
*/
@Id
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", // 名称
strategy = "com.xiesx.FastBoot.db.jpa.identifier.IdWorkerGenerator", // 生成策略
parameters = {// 生成参数
@Parameter(name = "prefix", value = "L"), // 前缀,L
@Parameter(name = "workerId", value = "1"), // 终端ID,默认0
@Parameter(name = "centerId", value = "1") // 数据中心ID,默认0
})
@JSONField(ordinal = 1)
private String id;
/**
* 创建时间
*/
@CreatedDate
@Column(updatable = false, nullable = false)
@JSONField(ordinal = 2)
private Date createDate;
/**
* 修改时间
*/
@LastModifiedDate
@Column(nullable = false)
@JSONField(ordinal = 3)
private Date updateDate;
/**
* 请求IP
*/
@Column
@JSONField(ordinal = 4)
private String ip;
/**
* 方法
*/
@Column
@JSONField(ordinal = 5)
private String method;
/**
* 方式
*/
@Column
@JSONField(ordinal = 6)
private String type;
/**
* 地址
*/
@Column
@JSONField(ordinal = 7)
private String url;
/**
* 请求参数
*/
@JSONField(serialize = false)
private String req;
/**
* 响应结果
*/
@JSONField(serialize = false)
private String res;
/**
* 执行时间(毫秒)
*/
@Column
@JSONField(ordinal = 10)
private Long time;
/**
* 是否删除
*/
@Column
@JSONField(serialize = false)
private Boolean isDel = false;
```
该实体类,可以通过 `fast-generator`生成
## **4.3 持久类**
```
public interface LogRecordRepository extends JpaPlusRepository<LogRecord, String> {
}
```
该持久类,可以通过 `fast-generator`生成
## **4.4 实现类**
```
@Log4j2
public class LogStorageMysqlProvider extends LogStorageProvider {
private final static String UNKNOWN = "unknown";
private static final String[] HEAD_INFO = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR",
"PROXY_FORWARDED_FOR",
"X-Real-IP"};
LogRecordRepository mLogRecordRepository;
public LogStorageMysqlProvider(String operation, String method, Object[] args, Long time) {
super(operation, method, args, time);
mLogRecordRepository = SpringHelper.getBean(LogRecordRepository.class);
}
@Override
public void record(HttpServletRequest request, Object result) {
super.record(request, result);
// 构造日志
LogRecord logRecord = new LogRecord()//
.setIp(getIpAddr(request))//
.setMethod(method)//
.setType(type)//
.setUrl(uri)//
.setReq(JSON.toJSONString(parameters))//
.setRes(JSON.toJSONString(result))//
.setTime(time);
// 保存日志
logRecord = mLogRecordRepository.insertOrUpdate(logRecord);
// 打印日志编号
log.info("log record id {}", logRecord.getId());
}
/**
* 获取ip
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
for (String header : HEAD_INFO) {
String ip = request.getHeader(header);
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
if (ip.contains(",")) {
String[] ips = ip.split(",");
for (String s : ips) {
if (!(UNKNOWN.equalsIgnoreCase(s))) {
ip = s;
break;
}
}
}
return ip;
}
}
return request.getRemoteAddr();
}
}
```
> 示例默认实现了mysql的存储,可以存MongoDB...等其他数据库
# **5. p6spy输出**
## **5.1 配置文件**(内置)
spy.properties
```
module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.xiesx.springboot.core.logger.pkaq.P6SpyLogger
# 使用日志系统记录sql
appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 配置记录Log例外
excludecategories=info,debug,result,batc,resultset
# 设置使用p6spy driver代理
deregisterdrivers=true
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动
driverlist=com.mysql.cj.jdbc.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 (秒)
outagedetectioninterval=2
```
## **5.2 使用环境**
实际使用中建议本地开发环境`application-local`使用`p6spy`
### **5.2.1 开发环境**
```
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3307/dbname?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
username: xxxx
password: xxxx
```
### **5.2.1 生产环境**
```
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/dbname?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
username: xxxx
password: xxxx
```
## **5.3 验证打印**
### **5.3.1 默认?**
```
[FastBoot][ INFO][06-27 01:43:14]-->[http-nio-9090-exec-3:2007963][logSQL(Slf4JLogger.java:60)] | - | took 1ms | | statement | select * from sys_config where k=?
```
### **5.3.2 p6spy**
```
[FastBoot][ INFO][06-27 01:43:14]-->[http-nio-9090-exec-3:2007963][logSQL(Slf4JLogger.java:60)] | - | took 1ms | | statement | select * from sys_config where k='update'
```