In the last section we talked about how to use Undertow As our Web Service container , In this section, we will analyze the use of Undertow Another problem , That's how to configure accesslog, as well as accesslog Various placeholders for .
accesslog Related configuration
server:
undertow:
# access log Related configuration
accesslog:
# Storage directory , The default is logs
dir: ./log
# Open or not
enabled: true
# Format , The various placeholders are described in detail later
pattern: '{
"transportProtocol":"%{TRANSPORT_PROTOCOL}",
"scheme":"%{SCHEME}",
"protocol":"%{PROTOCOL}",
"method":"%{METHOD}",
"reqHeaderUserAgent":"%{i,User-Agent}",
"cookieUserId": "%{c,userId}",
"queryTest": "%{q,test}",
"queryString": "%q",
"relativePath": "%R, %{REQUEST_PATH}, %{RESOLVED_PATH}",
"requestLine": "%r",
"uri": "%U",
"thread": "%I",
"hostPort": "%{HOST_AND_PORT}",
"localIp": "%A",
"localPort": "%p",
"localServerName": "%v",
"remoteIp": "%a",
"remoteHost": "%h",
"bytesSent": "%b",
"time":"%{time,yyyy-MM-dd HH:mm:ss.S}",
"status":"%s",
"reason":"%{RESPONSE_REASON_PHRASE}",
"respHeaderUserSession":"%{o,userSession}",
"respCookieUserId":"%{resp-cookie,userId}",
"timeUsed":"%Dms, %Ts, %{RESPONSE_TIME}ms, %{RESPONSE_TIME_MICROS} us, %{RESPONSE_TIME_NANOS} ns",
}'
# File prefix , The default is access_log
prefix: access.
# file extension , The default is log
suffix: log
# Do you want to write another log file access log, The default is true
# At present, only by date rotate, One log file a day
rotate: true
Be careful 1: Log files rotate At present only by date
Undertow Of accesslog Dealing with core class abstraction is io.undertow.server.handlers.accesslog.AccesslogReceiver
. Due to the present Undertow Of AccesslogReceiver
Only one implementation is in use , That is to say io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver
.
see DefaultAccessLogReceiver
Of rotate opportunity :
/**
* Calculation rotate Point in time
*/
private void calculateChangeOverPoint() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.HOUR_OF_DAY, 0);
// Current time date + 1, The next day
calendar.add(Calendar.DATE, 1);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
currentDateString = df.format(new Date());
// if there is an existing default log file, use the date last modified instead of the current date
if (Files.exists(defaultLogFile)) {
try {
currentDateString = df.format(new Date(Files.getLastModifiedTime(defaultLogFile).toMillis()));
} catch(IOException e){
// ignore. use the current date if exception happens.
}
}
//rotate The timing is the next day 0 spot
changeOverPoint = calendar.getTimeInMillis();
}
accesslog Place holder
Actually Undertow Medium accesslog Place holder , That's what we mentioned before Undertow Listener After parsing the request, abstract HTTP server exchange Properties of .
Official document It's not the most complete , And note that there's no explanation , For example, some placeholders must open some Undertow Features can be used and so on . Here's a list of .
First of all, I would like to make a point of attention , Parameter placeholder , for example %{i, What you want to see header value }
see header One of the key Value . Be careful not to have spaces after commas , Because this space will count key You can't get what you want key.
Request related properties
describe | Abbreviation placeholder | Full name placeholder | Parameter placeholder | Source code |
---|---|---|---|---|
Request transport protocol , Equivalent to Request protocol | nothing | %{TRANSPORT_PROTOCOL} |
nothing | TransportProtocolAttribute |
Request mode , for example http、https etc. | %{SCHEME} |
nothing | RequestSchemeAttribute |
|
Request protocol , for example HTTP/1.1 etc. |
%H |
%{PROTOCOL} |
nothing | RequestProtocolAttribute |
Request method , for example GET、POST etc. | %m |
%{METHOD} |
nothing | RequestMethodAttribute |
request Header A certain value of | nothing | nothing | %{i, What you want to see header value } |
RequestHeaderAttribute |
Cookie A certain value of | nothing | nothing | %{c, What you want to see cookie value } perhaps %{req-cookie, What you want to see cookie value } |
They correspond to each other CookieAttribute and RequestCookieAttribute |
Path parameter PathVariable Because it has not been Undertow Of Listener perhaps Handler Parsing , So we can't intercept , Can't confirm if it's a PathVariable Or just url route . therefore ,PathVariable It doesn't work . | nothing | nothing | %{p, The path parameters you want to see key } |
PathParameterAttribute |
Request parameters , namely url Of ? After that, the key value pairs , Here you can choose to view a key Value . | nothing | nothing | %{q, The request parameters you want to see key} |
QueryParameterAttribute |
Request parameter string , namely url Of ? All the characters after } | %q ( It doesn't contain ?) |
%{QUERY_STRING} ( It doesn't contain ?);%{BARE_QUERY_STRING} ( contain ?) |
nothing | QueryStringAttribute |
Request relative path ( stay Spring Boot In the environment , In most cases RequestPath and RelativePath also ResolvedPath It is equivalent. ), Remove host,port, The path to the request parameter string | %R |
%{RELATIVE_PATH} perhaps %{REQUEST_PATH} perhaps %{RESOLVED_PATH} |
nothing | They correspond to each other RelativePathAttribute and RequestPathAttribute and ResolvedPathAttribute |
Request the whole string , Include request methods , Request relative path , Request parameter string , Request protocol , for example Get /test?a=b HTTP/1.1 |
%r |
%{REQUEST_LINE} |
nothing | RequestLineAttribute |
request URI, Including request relative path , Request parameter string | %U |
%{REQUEST_URL} |
nothing | RequestURLAttribute |
The thread that processes the request | %I |
%{THREAD_NAME} |
nothing | ThreadNameAttribute |
Be careful :
- Path parameter PathVariable Because it has not been Undertow Of Listener perhaps Handler Parsing , So we can't intercept , Can't confirm if it's a PathVariable Or just url route . therefore ,PathVariable It doesn't work .
Request address related
describe | Abbreviation placeholder | Full name placeholder | Parameter placeholder | Source code |
---|---|---|---|---|
host and port, It is commonly HTTP request Header Medium Host value , If Host If it is empty, the local address and port will be obtained , If the port is not obtained, the default port will be used according to the protocol (http:80,,https:443) | nothing | %{HOST_AND_PORT} |
nothing | HostAndPortAttribute |
Request local address IP | %A |
%{LOCAL_IP} |
nothing | LocalIPAttribute |
Request local port Port | %p |
%{LOCAL_PORT} |
nothing | LocalPortAttribute |
Request local host name , It is commonly HTTP request Header Medium Host value , If Host If it is empty, the local address will be obtained | %v |
%{LOCAL_SERVER_NAME} |
nothing | LocalServerNameAttribute |
Request remote host name , Get the remote host address through the connection | %h |
%{REMOTE_HOST} |
nothing | RemoteHostAttribute |
Request remote IP, Get the remote IP | %a |
%{REMOTE_IP} |
nothing | RemoteIPAttribute |
Be careful :
- We usually don't get the remote address of the request from the request connection , But through Http Header Inside
X-forwarded-for
perhapsX-real-ip
Etc , Because now requests are made through all kinds of VPN, The load balancer that comes up .
Response related properties
describe | Abbreviation placeholder | Full name placeholder | Parameter placeholder | Source code |
---|---|---|---|---|
The size of the number of bytes sent , except Http Header outside | %b ( If it's empty, it's -) perhaps %B ( If it's empty, it's 0) |
%{BYTES_SENT} ( If it's empty, it's 0) |
nothing | BytesSentAttribute |
accesslog Time , This is not the time to receive the request , It's the response time | %t |
%{DATE_TIME} |
%{time, You customize it java in SimpleDateFormat The format of } |
DateTimeAttribute |
HTTP Response status code | %s |
%{RESPONSE_CODE} |
nothing | ResponseCodeAttribute |
HTTP Response reason | nothing | %{RESPONSE_REASON_PHRASE} |
nothing | ResponseReasonPhraseAttribute |
Respond to Header A certain value of | nothing | nothing | %{o, What you want to see header value } |
ResponseHeaderAttribute |
Respond to Cookie A certain value of | nothing | nothing | %{resp-cookie, What you want to see cookie value } |
ResponseCookieAttribute |
response time , Default undertow There is no open request time statistics , You need to open it to count the response time | %D ( millisecond , for example 56 representative 56ms) %T ( second , for example 5.067 representative 5.067 second ) |
%{RESPONSE_TIME} ( Equivalent to %D ) %{RESPONSE_TIME_MICROS} ( Microsecond ) %{RESPONSE_TIME_NANOS} ( nanosecond ) |
nothing | ResponseTimeAttribute |
Be careful : Default undertow There is no open request time statistics , You need to open it to count the response time , How to turn it on ? By registering a WebServerFactoryCustomizer
To Spring ApplicationContext Then you can . Look at the code below ( Project address :https://github.com/HashZhang/spring-cloud-scaffold/blob/master/spring-cloud-iiford/):
spring.factories
( Omit irrelevant code )
# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.hashjang.spring.cloud.iiford.service.common.auto.UndertowAutoConfiguration
// Set up proxyBeanMethods=false, Because no @Bean You need to return the same Bean, There's no need to be an agent , Turn off to increase startup speed
@Configuration(proxyBeanMethods = false)
@Import(WebServerConfiguration.class)
public class UndertowAutoConfiguration {
}
// Set up proxyBeanMethods=false, Because no @Bean You need to return the same Bean, There's no need to be an agent , Turn off to increase startup speed
@Configuration(proxyBeanMethods = false)
public class WebServerConfiguration {
@Bean
public WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory> undertowWebServerAccessLogTimingEnabler(ServerProperties serverProperties) {
return new DefaultWebServerFactoryCustomizer(serverProperties);
}
}
DefaultWebServerFactoryCustomizer
public class DefaultWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory> {
private final ServerProperties serverProperties;
public DefaultWebServerFactoryCustomizer(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
@Override
public void customize(ConfigurableUndertowWebServerFactory factory) {
String pattern = serverProperties.getUndertow().getAccesslog().getPattern();
// If accesslog The response time is printed in the configuration , Then open the record request start time configuration
if (logRequestProcessingTiming(pattern)) {
factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, true));
}
}
private boolean logRequestProcessingTiming(String pattern) {
if (StringUtils.isBlank(pattern)) {
return false;
}
// Judge accesslog Whether the view response time is configured
return pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_MICROS)
|| pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_MILLIS)
|| pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_NANOS)
|| pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_MILLIS_SHORT)
|| pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_SECONDS_SHORT);
}
}
other
And security related properties (SSL relevant , Login authentication Authentication relevant ), Microservice internal calls are generally not used , We won't go into details here .
Other built-in properties , stay Spring Boot It's not used in the environment , We won't discuss it here .
give an example
What we first configured accesslog The request returns as follows ( JSON The formatted result ):
{
"transportProtocol": "http/1.1",
"scheme": "http",
"protocol": "HTTP/1.1",
"method": "GET",
"reqHeaderUserAgent": "PostmanRuntime/7.26.10",
"cookieUserId": "testRequestCookieUserId",
"queryTest": "1",
"queryString": "?test=1&query=2",
"relativePath": "/test, /test, -",
"requestLine": "GET /test?test=1&query=2 HTTP/1.1",
"uri": "/test",
"thread": "XNIO-2 task-1",
"hostPort": "127.0.0.1:8102",
"localIp": "127.0.0.1",
"localPort": "8102",
"localServerName": "127.0.0.1",
"remoteIp": "127.0.0.1",
"remoteHost": "127.0.0.1",
"bytesSent": "26",
"time": "2021-04-08 00:07:50.410",
"status": "200",
"reason": "OK",
"respHeaderUserSession": "testResponseHeaderUserSession",
"respCookieUserId": "testResponseCookieUserId",
"timeUsed": "3683ms, 3.683s, 3683ms, 3683149 us, 3683149200 ns",
}