log request and response for http
HttpLog会根据@HttpLog
中定义的日志表的注解及字段名,自动记录相关HTTP信息.
业务日志表定义,根据具体业务需要,必须字段为主键id
(名字固定), 示例: mysql, oracle
日志表建表规范
字段注释包含 | 或者字段名 | 说明 |
---|---|---|
内置类: | ||
httplog:"id" |
id | 日志记录ID |
httplog:"created" |
created | 创建时间 |
httplog:"ip" |
ip | 当前机器IP |
httplog:"hostname" |
hostname | 当前机器名称 |
httplog:"pid" |
pid | 应用程序PID |
httplog:"started" |
start | 开始时间(yyyy-MM-dd HH:mm:ss.SSS) |
httplog:"end" |
end | 结束时间(yyyy-MM-dd HH:mm:ss.SSS) |
httplog:"cost" |
cost | 花费时间(ms) |
httplog:"biz" |
biz | 业务名称,对应到HttpLog注解的biz |
httplog:"exception" |
exception | 异常信息 |
请求类: | ||
httplog:"req_head_xxx" |
req_head_xxx | 请求中的xxx头 |
httplog:"req_heads" |
req_heads | 请求中的所有头 |
httplog:"req_method" |
req_method | 请求method |
httplog:"req_url" |
req_url | 请求URL |
httplog:"req_path_xxx" |
req_path_xxx | 请求URL中的xxx路径参数 |
httplog:"req_paths" |
req_paths | 请求URL中的所有路径参数 |
httplog:"req_query_xxx" |
req_query_xxx | 请求URl中的xxx查询参数 |
httplog:"req_queries" |
req_queries | 请求URl中的所有查询参数 |
httplog:"req_param_xxx" |
req_param_xxx | 请求中query/form的xxx参数 |
httplog:"req_params" |
req_params | 请求中query/form的所有参数 |
httplog:"req_body" |
req_body | 请求体 |
httplog:"req_json" |
req_json | 请求体(当Content-Type为JSON时) |
httplog:"req_json_xxx" |
req_json_xxx | 请求体JSON中的xxx属性 |
响应类: | ||
httplog:"rsp_head_xxx" |
rsp_head_xxx | 响应中的xxx头 |
httplog:"rsp_heads" |
rsp_heads | 响应中的所有头 |
httplog:"rsp_body" |
rsp_body | 响应体 |
httplog:"rsp_json" |
rsp_json | 响应体JSON(当Content-Type为JSON时) |
httplog:"rsp_json_xxx" |
rsp_json_xxx | 请求体JSON中的xxx属性 |
httplog:"rsp_status" |
rsp_status | 响应编码 |
上下文: | ||
httplog:"ctx_xxx" |
ctx_xxx | 上下文对象xxx的值 |
固定值: | ||
httplog:"fix_xxx" |
fix_xxx | 由fix参数指定的固定值 |
扩展类: | ||
httplog:"pre_xxx" |
pre_xxx | 由自定义扩展器pre给出属性值,见示例 |
httplog:"post_xxx" |
post_xxx | 由自定义扩展器post给出属性值 |
自定义: | 自助引入HttpLogCustom实例 | |
httplog:"custom_xxx" |
custom_xxx | 由HttpLogCustom提供的自定义xxx值, eg: HttpLogCustom.get().put("xxx", "bingoo"); |
如果存在数据库多版本,可以直接在代码工程中配置yaml,此优先级高于数据库表的字段注释元信息:
biz_log: # 表名
created: # 字段名
comment: 创建时间 httplog:"-" # 字段注释
body2:
comment: 响应体 httplog:"rsp_body"
bizdesc:
comment: 业务名称 httplog:"fix_desc"
在用户没有权限查询数据库表字段元信息,或者元信息查询还未适配新使用的数据库时,可以通过完全手工配置日志表字段元信息,如下所示,
注意设置:manual_schema: true
manual_schema: true
biz_log:
id:
data_type: bigint
nullable: true
extra: auto_increment
comment: 日志记录ID
created:
data_type: datetime
comment: 创建时间 httplog:"-"
started:
data_type: datetime
comment: 请求时间
end:
data_type: datetime
comment: 结束时间
cost:
data_type: int
comment: 费时毫秒
ip:
data_type: varchar
max_length: 60
comment: 当前机器IP
hostname:
data_type: varchar
max_length: 60
comment: 当前机器名称
pid:
data_type: int
comment: 应用程序PID
biz:
data_type: varchar
max_length: 60
comment: 当前业务名称
req_path_id:
data_type: varchar
max_length: 60
comment: 请求路径变量id
req_url:
data_type: varchar
max_length: 60
comment: 请求url
req_heads:
data_type: varchar
max_length: 600
comment: 请求头
req_method:
data_type: varchar
max_length: 60
comment: 请求方法
rsp_body:
data_type: varchar
max_length: 300
comment: 响应体
body2:
data_type: varchar
max_length: 300
comment: 响应体 httplog:"rsp_body"
pre_hi:
data_type: varchar
max_length: 60
comment: hi
post_bye:
data_type: varchar
max_length: 60
comment: bye
bizdesc:
comment: 业务名称 httplog:"fix_desc"
然后定义Spring的Bean:
@Bean @SneakyThrows
public HttpLogYml httpLogYml() {
@Cleanup val is = new ClassPathResource("httplog.yml").getInputStream();
return HttpLogYml.parseYml(is);
}
Spring配置示例
@HttpLogEnabled
@SpringBootApplication
public class App {
}
SpringMVC Controller使用示例
import com.github.gobars.httplog.HttpLog;
import com.github.gobars.httplog.spring.TestDto;
import com.github.gobars.httplog.spring.TestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping(value = "/test")
public class TestController {
/**
* test get.
*
* @param id id
* @return id
*/
@HttpLog(tables = "biz_log", fix = "desc:ID查找")
@GetMapping(value = "/{id}")
public String get(@PathVariable Integer id) {
return "test id : " + id;
}
/**
* test post.
*
* @param testDto testDto
* @return testDto
*/
@HttpLog(tables = "biz_log_post")
@PostMapping
public TestDto post(@RequestBody TestDto testDto) {
return testDto;
}
/**
* test put error.
*
* @param testDto testDto
*/
@PutMapping
public void error(@RequestBody TestDto testDto) {
log.warn("error TestException will be thrown");
throw new TestException(testDto.toString());
}
}
日志表记录日志示例
sample biz_log records:
[
{
"id": 928019202048,
"created": "2020-06-09 16:41:51",
"start": "2020-06-09 16:41:51",
"end": "2020-06-09 16:41:52",
"cost": 592,
"ip": "192.168.224.20",
"hostname": "bingoobjcadeMacBook-Pro.local",
"pid": 73362,
"biz": null,
"req_path_id": "10",
"req_url": "/test/10",
"req_heads": "{host=localhost:53865, connection=keep-alive, accept=text/plain, application/json, application/*+json, */*, user-agent=Java/11.0.7}",
"req_method": "GET",
"rsp_body": "",
"bizdesc": "ID查找"
}
]
sample biz_log_post records:
[
{
"id": 931332702208,
"created": "2020-06-09 16:41:52",
"start": "2020-06-09 16:41:52",
"end": "2020-06-09 16:41:52",
"cost": 31,
"ip": "192.168.224.20",
"hostname": "bingoobjcadeMacBook-Pro.local",
"pid": 73362,
"biz": null,
"req_path_id": null,
"req_url": "/test",
"req_heads": "{content-length=9, host=localhost:53865, content-type=application/json, connection=keep-alive, accept=application/json, application/*+json, user-agent=Java/11.0.7}",
"req_method": "POST",
"rsp": "{\"id\":10}",
"dtoid": "10"
}
]
@org.springframework.context.annotation.Configuration
public class HttpLogFilterBean extends com.github.gobars.httplog.HttpLogFilter {}
请求响应日志
GET:
2020-06-04 20:43:31.665 INFO 81103 --- [o-auto-1-exec-1] c.github.gobars.httplog.HttpLogFilter : req: Req(super=ReqRsp(headers={host=localhost:57413, connection=keep-alive, accept=text/plain, application/json, application/*+json, */*, user-agent=Java/11.0.7}, startNs=13514944034314, tookMs=0, bodyBytes=0, body=, error=null), method=GET, requestUri=/test/10, protocol=HTTP/1.1)
2020-06-04 20:43:31.669 INFO 81103 --- [o-auto-1-exec-1] c.github.gobars.httplog.HttpLogFilter : rsp: Rsp(super=ReqRsp(headers={Keep-Alive=timeout=60, Connection=keep-alive, Content-Length=12, Date=Thu, 04 Jun 2020 12:43:31 GMT, Content-Type=text/plain;charset=UTF-8}, startNs=13514944034314, tookMs=51, bodyBytes=12, body=test id : 10, error=null), status=200, reasonPhrase=OK)
POST:
2020-06-04 20:43:31.900 INFO 81103 --- [o-auto-1-exec-2] c.github.gobars.httplog.HttpLogFilter : req: Req(super=ReqRsp(headers={content-length=9, host=localhost:57413, content-type=application/json, connection=keep-alive, accept=application/json, application/*+json, user-agent=Java/11.0.7}, startNs=13515206496154, tookMs=0, bodyBytes=9, body={"id":10}, error=null), method=POST, requestUri=/test, protocol=HTTP/1.1)
2020-06-04 20:43:31.900 INFO 81103 --- [o-auto-1-exec-2] c.github.gobars.httplog.HttpLogFilter : rsp: Rsp(super=ReqRsp(headers={Keep-Alive=timeout=60, Connection=keep-alive, Content-Length=9, Date=Thu, 04 Jun 2020 12:43:31 GMT, Content-Type=application/json}, startNs=13515206496154, tookMs=29, bodyBytes=9, body={"id":10}, error=null), status=200, reasonPhrase=OK)
PUT exception:
2020-06-04 20:43:31.917 INFO 81103 --- [o-auto-1-exec-3] c.github.gobars.httplog.HttpLogFilter : req: Req(super=ReqRsp(headers={content-length=9, host=localhost:57413, content-type=application/json, connection=keep-alive, accept=application/json, application/*+json, user-agent=Java/11.0.7}, startNs=13515248933063, tookMs=0, bodyBytes=9, body={"id":10}, error=null), method=PUT, requestUri=/test, protocol=HTTP/1.1)
2020-06-04 20:43:31.917 INFO 81103 --- [o-auto-1-exec-3] c.github.gobars.httplog.HttpLogFilter : rsp: Rsp(super=ReqRsp(headers=null, startNs=13515248933063, tookMs=4, bodyBytes=0, body=null, error=org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.github.gobars.httplog.spring.TestException: TestDto(id=10)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:920)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:663)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.github.gobars.httplog.HttpLogFilter.doFilterInternal(HttpLogFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: com.github.gobars.httplog.spring.TestException: TestDto(id=10)
at com.github.gobars.httplog.spring.mysql.TestController.error(TestController.java:40)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
... 41 more
), status=500, reasonPhrase=Internal Server Error)
GPG_TTY=$(tty) LANGUAGE=en mvn clean install -Prelease -Dgpg.passphrase=thephrase