Skip to content

Commit 3df0760

Browse files
authored
Merge pull request #575 from groldan/logging_mdc_improvements
Add access log configuration for geoserver
2 parents 767861d + 45b2d1b commit 3df0760

34 files changed

+916
-452
lines changed

compose/.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ GEOSERVER_BASE_PATH=/geoserver/cloud
1515
# logging profile, either "default" or "json-logs"
1616
#LOGGING_PROFILE=json-logs
1717
LOGGING_PROFILE=default
18-
GEOSERVER_DEFAULT_PROFILES="${LOGGING_PROFILE},acl,logging_debug_events"
18+
GEOSERVER_DEFAULT_PROFILES="${LOGGING_PROFILE},acl"
1919

2020
GATEWAY_DEFAULT_PROFILES=${LOGGING_PROFILE}
2121
DISCOVERY_SERVER_DEFAULT_PROFILES=${LOGGING_PROFILE}

config

src/starters/observability/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
<groupId>com.github.f4b6a3</groupId>
2121
<artifactId>ulid-creator</artifactId>
2222
</dependency>
23-
2423
<dependency>
2524
<groupId>javax.servlet</groupId>
2625
<artifactId>javax.servlet-api</artifactId>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
3+
* GPL 2.0 license, available at the root application directory.
4+
*/
5+
package org.geoserver.cloud.autoconfigure.logging.accesslog;
6+
7+
import org.geoserver.cloud.logging.accesslog.AccessLogFilterConfig;
8+
import org.geoserver.cloud.logging.accesslog.AccessLogServletFilter;
9+
import org.springframework.boot.autoconfigure.AutoConfiguration;
10+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
12+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
13+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
14+
import org.springframework.context.annotation.Bean;
15+
16+
@AutoConfiguration
17+
@ConditionalOnProperty(name = AccessLogFilterConfig.ENABLED_KEY, havingValue = "true", matchIfMissing = false)
18+
@EnableConfigurationProperties(AccessLogFilterConfig.class)
19+
@ConditionalOnWebApplication(type = Type.SERVLET)
20+
public class AccessLogServletAutoConfiguration {
21+
22+
@Bean
23+
AccessLogServletFilter accessLogFilter(AccessLogFilterConfig conf) {
24+
return new AccessLogServletFilter(conf);
25+
}
26+
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
33
* GPL 2.0 license, available at the root application directory.
44
*/
5-
package org.geoserver.cloud.autoconfigure.observability;
5+
package org.geoserver.cloud.autoconfigure.logging.mdc;
66

7-
import org.geoserver.cloud.observability.logging.config.MDCConfigProperties;
8-
import org.geoserver.cloud.observability.logging.ows.MDCDispatcherCallback;
7+
import org.geoserver.cloud.logging.mdc.config.MDCConfigProperties;
8+
import org.geoserver.cloud.logging.mdc.ows.OWSMdcDispatcherCallback;
99
import org.geoserver.ows.Dispatcher;
1010
import org.geoserver.ows.DispatcherCallback;
1111
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -19,7 +19,7 @@
1919
* {@link AutoConfiguration @AutoConfiguration} to enable logging MDC (Mapped Diagnostic Context)
2020
* for the GeoSever {@link Dispatcher} events using a {@link DispatcherCallback}
2121
*
22-
* @see MDCDispatcherCallback
22+
* @see OWSMdcDispatcherCallback
2323
*/
2424
@Configuration(proxyBeanMethods = false)
2525
@ConditionalOnClass({
@@ -31,7 +31,7 @@
3131
class GeoServerDispatcherMDCConfiguration {
3232

3333
@Bean
34-
MDCDispatcherCallback mdcDispatcherCallback(MDCConfigProperties config) {
35-
return new MDCDispatcherCallback(config);
34+
OWSMdcDispatcherCallback mdcDispatcherCallback(MDCConfigProperties config) {
35+
return new OWSMdcDispatcherCallback(config.getGeoserver().getOws());
3636
}
3737
}
Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@
22
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
33
* GPL 2.0 license, available at the root application directory.
44
*/
5-
package org.geoserver.cloud.autoconfigure.observability;
5+
package org.geoserver.cloud.autoconfigure.logging.mdc;
66

77
import java.util.Optional;
8-
import org.geoserver.cloud.observability.logging.config.MDCConfigProperties;
9-
import org.geoserver.cloud.observability.logging.servlet.HttpRequestMdcConfigProperties;
10-
import org.geoserver.cloud.observability.logging.servlet.HttpRequestMdcFilter;
11-
import org.geoserver.cloud.observability.logging.servlet.MDCAuthenticationFilter;
12-
import org.geoserver.cloud.observability.logging.servlet.MDCCleaningFilter;
13-
import org.geoserver.cloud.observability.logging.servlet.SpringEnvironmentMdcConfigProperties;
14-
import org.geoserver.cloud.observability.logging.servlet.SpringEnvironmentMdcFilter;
8+
import org.geoserver.cloud.logging.mdc.config.MDCConfigProperties;
9+
import org.geoserver.cloud.logging.mdc.servlet.HttpRequestMdcFilter;
10+
import org.geoserver.cloud.logging.mdc.servlet.MDCAuthenticationFilter;
11+
import org.geoserver.cloud.logging.mdc.servlet.MDCCleaningFilter;
12+
import org.geoserver.cloud.logging.mdc.servlet.SpringEnvironmentMdcFilter;
1513
import org.geoserver.security.GeoServerSecurityFilterChainProxy;
1614
import org.slf4j.MDC;
1715
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -36,36 +34,31 @@
3634
* @see GeoServerDispatcherMDCConfiguration
3735
*/
3836
@AutoConfiguration
39-
@EnableConfigurationProperties({
40-
MDCConfigProperties.class,
41-
HttpRequestMdcConfigProperties.class,
42-
SpringEnvironmentMdcConfigProperties.class
43-
})
37+
@EnableConfigurationProperties({MDCConfigProperties.class})
4438
@Import(GeoServerDispatcherMDCConfiguration.class)
4539
@ConditionalOnWebApplication(type = Type.SERVLET)
46-
public class LoggingMDCAutoConfiguration {
40+
public class LoggingMDCServletAutoConfiguration {
41+
42+
@Bean
43+
MDCCleaningFilter mdcCleaningServletFilter() {
44+
return new MDCCleaningFilter();
45+
}
4746

4847
/**
4948
* @return servlet filter to {@link MDC#clear() clear} the MDC after the servlet request is
5049
* executed
5150
*/
5251
@Bean
53-
@Order(Ordered.HIGHEST_PRECEDENCE)
54-
HttpRequestMdcFilter httpMdcFilter(HttpRequestMdcConfigProperties config) {
55-
return new HttpRequestMdcFilter(config);
56-
}
57-
58-
@Bean
59-
@Order(Ordered.HIGHEST_PRECEDENCE)
60-
MDCCleaningFilter mdcCleaningServletFilter() {
61-
return new MDCCleaningFilter();
52+
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
53+
HttpRequestMdcFilter httpMdcFilter(MDCConfigProperties config) {
54+
return new HttpRequestMdcFilter(config.getHttp());
6255
}
6356

6457
@Bean
65-
@Order(Ordered.HIGHEST_PRECEDENCE)
58+
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
6659
SpringEnvironmentMdcFilter springEnvironmentMdcFilter(
67-
Environment env, SpringEnvironmentMdcConfigProperties config, Optional<BuildProperties> buildProperties) {
68-
return new SpringEnvironmentMdcFilter(env, buildProperties, config);
60+
Environment env, MDCConfigProperties config, Optional<BuildProperties> buildProperties) {
61+
return new SpringEnvironmentMdcFilter(env, buildProperties, config.getApplication());
6962
}
7063

7164
/**
@@ -80,7 +73,7 @@ FilterRegistrationBean<MDCAuthenticationFilter> mdcAuthenticationPropertiesServl
8073
MDCConfigProperties config) {
8174
FilterRegistrationBean<MDCAuthenticationFilter> registration = new FilterRegistrationBean<>();
8275

83-
var filter = new MDCAuthenticationFilter(config);
76+
var filter = new MDCAuthenticationFilter(config.getUser());
8477
registration.setMatchAfter(true);
8578

8679
registration.addUrlPatterns("/*");
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
3+
* GPL 2.0 license, available at the root application directory.
4+
*/
5+
package org.geoserver.cloud.logging.accesslog;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.regex.Pattern;
10+
import lombok.Data;
11+
import lombok.extern.slf4j.Slf4j;
12+
import org.springframework.boot.context.properties.ConfigurationProperties;
13+
14+
/**
15+
* Configuration to set white/black list over the request URL to determine if
16+
* the access log filter will log an entry for it.
17+
*/
18+
@Data
19+
@ConfigurationProperties(prefix = "logging.accesslog")
20+
@Slf4j(topic = "org.geoserver.cloud.accesslog")
21+
public class AccessLogFilterConfig {
22+
23+
public static final String ENABLED_KEY = "logging.accesslog.enabled";
24+
25+
/**
26+
* A list of java regular expressions applied to the request URL for logging at
27+
* trace level
28+
*/
29+
List<Pattern> trace = new ArrayList<>();
30+
31+
/**
32+
* A list of java regular expressions applied to the request URL for logging at
33+
* debug level
34+
*/
35+
List<Pattern> debug = new ArrayList<>();
36+
37+
/**
38+
* A list of java regular expressions applied to the request URL for logging at
39+
* info level
40+
*/
41+
List<Pattern> info = new ArrayList<>();
42+
43+
private enum Level {
44+
OFF {
45+
@Override
46+
void log(String message, Object... args) {
47+
// no-op
48+
}
49+
},
50+
TRACE {
51+
@Override
52+
void log(String message, Object... args) {
53+
log.trace(message, args);
54+
}
55+
},
56+
DEBUG {
57+
@Override
58+
void log(String message, Object... args) {
59+
log.debug(message, args);
60+
}
61+
},
62+
INFO {
63+
@Override
64+
void log(String message, Object... args) {
65+
log.info(message, args);
66+
}
67+
};
68+
69+
abstract void log(String message, Object... args);
70+
}
71+
72+
public void log(String method, int statusCode, String uri) {
73+
Level level = getLogLevel(uri);
74+
level.log("{} {} {} ", method, statusCode, uri);
75+
}
76+
77+
Level getLogLevel(String uri) {
78+
if (log.isInfoEnabled() && matches(uri, info)) return Level.INFO;
79+
if (log.isDebugEnabled() && matches(uri, debug)) return Level.INFO;
80+
if (log.isTraceEnabled() && matches(uri, trace)) return Level.INFO;
81+
82+
return Level.OFF;
83+
}
84+
85+
private boolean matches(String url, List<Pattern> patterns) {
86+
return (patterns == null || patterns.isEmpty())
87+
? false
88+
: patterns.stream().anyMatch(pattern -> pattern.matcher(url).matches());
89+
}
90+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
3+
* GPL 2.0 license, available at the root application directory.
4+
*/
5+
package org.geoserver.cloud.logging.accesslog;
6+
7+
import java.io.IOException;
8+
import javax.servlet.FilterChain;
9+
import javax.servlet.ServletException;
10+
import javax.servlet.http.HttpServletRequest;
11+
import javax.servlet.http.HttpServletResponse;
12+
import lombok.NonNull;
13+
import org.springframework.core.Ordered;
14+
import org.springframework.core.annotation.Order;
15+
import org.springframework.web.filter.CommonsRequestLoggingFilter;
16+
import org.springframework.web.filter.OncePerRequestFilter;
17+
18+
/** Similar to {@link CommonsRequestLoggingFilter} but uses slf4j */
19+
@Order(Ordered.HIGHEST_PRECEDENCE + 3)
20+
public class AccessLogServletFilter extends OncePerRequestFilter {
21+
22+
private final @NonNull AccessLogFilterConfig config;
23+
24+
public AccessLogServletFilter(@NonNull AccessLogFilterConfig conf) {
25+
this.config = conf;
26+
}
27+
28+
@Override
29+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
30+
throws ServletException, IOException {
31+
32+
try {
33+
filterChain.doFilter(request, response);
34+
} finally {
35+
String uri = request.getRequestURI();
36+
String method = request.getMethod();
37+
int statusCode = response.getStatus();
38+
config.log(method, statusCode, uri);
39+
}
40+
}
41+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
3+
* GPL 2.0 license, available at the root application directory.
4+
*/
5+
package org.geoserver.cloud.logging.mdc.config;
6+
7+
import lombok.Data;
8+
9+
@Data
10+
public class AuthenticationMdcConfigProperties {
11+
12+
/** Whether to append the enduser.id MDC property from the Authentication name */
13+
private boolean id = false;
14+
15+
/**
16+
* Whether to append the enduser.roles MDC property from the Authentication granted authorities
17+
*/
18+
private boolean roles = false;
19+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
3+
* GPL 2.0 license, available at the root application directory.
4+
*/
5+
package org.geoserver.cloud.logging.mdc.config;
6+
7+
import lombok.Data;
8+
9+
@Data
10+
public class GeoServerMdcConfigProperties {
11+
12+
private OWSMdcConfigProperties ows = new OWSMdcConfigProperties();
13+
14+
/** Configuration properties to contribute GeoServer OWS request properties to the MDC */
15+
@Data
16+
public static class OWSMdcConfigProperties {
17+
/**
18+
* Whether to append the gs.ows.service.name MDC property from the OWS dispatched request
19+
*/
20+
private boolean serviceName = true;
21+
22+
/**
23+
* Whether to append the gs.ows.service.version MDC property from the OWS dispatched request
24+
*/
25+
private boolean serviceVersion = true;
26+
27+
/**
28+
* Whether to append the gs.ows.service.format MDC property from the OWS dispatched request
29+
*/
30+
private boolean serviceFormat = true;
31+
32+
/**
33+
* Whether to append the gs.ows.service.operation MDC property from the OWS dispatched
34+
* request
35+
*/
36+
private boolean operationName = true;
37+
}
38+
}

0 commit comments

Comments
 (0)