Skip to content

Commit

Permalink
Merge pull request #576 from geoserver/backport-575-to-release/1.9.x
Browse files Browse the repository at this point in the history
[Backport release/1.9.x] Add access log configuration for geoserver
  • Loading branch information
groldan authored Dec 18, 2024
2 parents 44917bb + 3c8eba5 commit 3a20c94
Show file tree
Hide file tree
Showing 34 changed files with 916 additions and 452 deletions.
2 changes: 1 addition & 1 deletion compose/.env
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ GEOSERVER_BASE_PATH=/geoserver/cloud
# logging profile, either "default" or "json-logs"
#LOGGING_PROFILE=json-logs
LOGGING_PROFILE=default
GEOSERVER_DEFAULT_PROFILES="${LOGGING_PROFILE},acl,logging_debug_events"
GEOSERVER_DEFAULT_PROFILES="${LOGGING_PROFILE},acl"

GATEWAY_DEFAULT_PROFILES=${LOGGING_PROFILE}
DISCOVERY_SERVER_DEFAULT_PROFILES=${LOGGING_PROFILE}
Expand Down
2 changes: 1 addition & 1 deletion config
1 change: 0 additions & 1 deletion src/starters/observability/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
<groupId>com.github.f4b6a3</groupId>
<artifactId>ulid-creator</artifactId>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.autoconfigure.logging.accesslog;

import org.geoserver.cloud.logging.accesslog.AccessLogFilterConfig;
import org.geoserver.cloud.logging.accesslog.AccessLogServletFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@ConditionalOnProperty(name = AccessLogFilterConfig.ENABLED_KEY, havingValue = "true", matchIfMissing = false)
@EnableConfigurationProperties(AccessLogFilterConfig.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class AccessLogServletAutoConfiguration {

@Bean
AccessLogServletFilter accessLogFilter(AccessLogFilterConfig conf) {
return new AccessLogServletFilter(conf);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.autoconfigure.observability;
package org.geoserver.cloud.autoconfigure.logging.mdc;

import org.geoserver.cloud.observability.logging.config.MDCConfigProperties;
import org.geoserver.cloud.observability.logging.ows.MDCDispatcherCallback;
import org.geoserver.cloud.logging.mdc.config.MDCConfigProperties;
import org.geoserver.cloud.logging.mdc.ows.OWSMdcDispatcherCallback;
import org.geoserver.ows.Dispatcher;
import org.geoserver.ows.DispatcherCallback;
import org.springframework.boot.autoconfigure.AutoConfiguration;
Expand All @@ -19,7 +19,7 @@
* {@link AutoConfiguration @AutoConfiguration} to enable logging MDC (Mapped Diagnostic Context)
* for the GeoSever {@link Dispatcher} events using a {@link DispatcherCallback}
*
* @see MDCDispatcherCallback
* @see OWSMdcDispatcherCallback
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({
Expand All @@ -31,7 +31,7 @@
class GeoServerDispatcherMDCConfiguration {

@Bean
MDCDispatcherCallback mdcDispatcherCallback(MDCConfigProperties config) {
return new MDCDispatcherCallback(config);
OWSMdcDispatcherCallback mdcDispatcherCallback(MDCConfigProperties config) {
return new OWSMdcDispatcherCallback(config.getGeoserver().getOws());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.autoconfigure.observability;
package org.geoserver.cloud.autoconfigure.logging.mdc;

import java.util.Optional;
import org.geoserver.cloud.observability.logging.config.MDCConfigProperties;
import org.geoserver.cloud.observability.logging.servlet.HttpRequestMdcConfigProperties;
import org.geoserver.cloud.observability.logging.servlet.HttpRequestMdcFilter;
import org.geoserver.cloud.observability.logging.servlet.MDCAuthenticationFilter;
import org.geoserver.cloud.observability.logging.servlet.MDCCleaningFilter;
import org.geoserver.cloud.observability.logging.servlet.SpringEnvironmentMdcConfigProperties;
import org.geoserver.cloud.observability.logging.servlet.SpringEnvironmentMdcFilter;
import org.geoserver.cloud.logging.mdc.config.MDCConfigProperties;
import org.geoserver.cloud.logging.mdc.servlet.HttpRequestMdcFilter;
import org.geoserver.cloud.logging.mdc.servlet.MDCAuthenticationFilter;
import org.geoserver.cloud.logging.mdc.servlet.MDCCleaningFilter;
import org.geoserver.cloud.logging.mdc.servlet.SpringEnvironmentMdcFilter;
import org.geoserver.security.GeoServerSecurityFilterChainProxy;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.AutoConfiguration;
Expand All @@ -36,36 +34,31 @@
* @see GeoServerDispatcherMDCConfiguration
*/
@AutoConfiguration
@EnableConfigurationProperties({
MDCConfigProperties.class,
HttpRequestMdcConfigProperties.class,
SpringEnvironmentMdcConfigProperties.class
})
@EnableConfigurationProperties({MDCConfigProperties.class})
@Import(GeoServerDispatcherMDCConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class LoggingMDCAutoConfiguration {
public class LoggingMDCServletAutoConfiguration {

@Bean
MDCCleaningFilter mdcCleaningServletFilter() {
return new MDCCleaningFilter();
}

/**
* @return servlet filter to {@link MDC#clear() clear} the MDC after the servlet request is
* executed
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
HttpRequestMdcFilter httpMdcFilter(HttpRequestMdcConfigProperties config) {
return new HttpRequestMdcFilter(config);
}

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
MDCCleaningFilter mdcCleaningServletFilter() {
return new MDCCleaningFilter();
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
HttpRequestMdcFilter httpMdcFilter(MDCConfigProperties config) {
return new HttpRequestMdcFilter(config.getHttp());
}

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
SpringEnvironmentMdcFilter springEnvironmentMdcFilter(
Environment env, SpringEnvironmentMdcConfigProperties config, Optional<BuildProperties> buildProperties) {
return new SpringEnvironmentMdcFilter(env, buildProperties, config);
Environment env, MDCConfigProperties config, Optional<BuildProperties> buildProperties) {
return new SpringEnvironmentMdcFilter(env, buildProperties, config.getApplication());
}

/**
Expand All @@ -80,7 +73,7 @@ FilterRegistrationBean<MDCAuthenticationFilter> mdcAuthenticationPropertiesServl
MDCConfigProperties config) {
FilterRegistrationBean<MDCAuthenticationFilter> registration = new FilterRegistrationBean<>();

var filter = new MDCAuthenticationFilter(config);
var filter = new MDCAuthenticationFilter(config.getUser());
registration.setMatchAfter(true);

registration.addUrlPatterns("/*");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.logging.accesslog;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* Configuration to set white/black list over the request URL to determine if
* the access log filter will log an entry for it.
*/
@Data
@ConfigurationProperties(prefix = "logging.accesslog")
@Slf4j(topic = "org.geoserver.cloud.accesslog")
public class AccessLogFilterConfig {

public static final String ENABLED_KEY = "logging.accesslog.enabled";

/**
* A list of java regular expressions applied to the request URL for logging at
* trace level
*/
List<Pattern> trace = new ArrayList<>();

/**
* A list of java regular expressions applied to the request URL for logging at
* debug level
*/
List<Pattern> debug = new ArrayList<>();

/**
* A list of java regular expressions applied to the request URL for logging at
* info level
*/
List<Pattern> info = new ArrayList<>();

private enum Level {
OFF {
@Override
void log(String message, Object... args) {
// no-op
}
},
TRACE {
@Override
void log(String message, Object... args) {
log.trace(message, args);
}
},
DEBUG {
@Override
void log(String message, Object... args) {
log.debug(message, args);
}
},
INFO {
@Override
void log(String message, Object... args) {
log.info(message, args);
}
};

abstract void log(String message, Object... args);
}

public void log(String method, int statusCode, String uri) {
Level level = getLogLevel(uri);
level.log("{} {} {} ", method, statusCode, uri);
}

Level getLogLevel(String uri) {
if (log.isInfoEnabled() && matches(uri, info)) return Level.INFO;
if (log.isDebugEnabled() && matches(uri, debug)) return Level.INFO;
if (log.isTraceEnabled() && matches(uri, trace)) return Level.INFO;

return Level.OFF;
}

private boolean matches(String url, List<Pattern> patterns) {
return (patterns == null || patterns.isEmpty())
? false
: patterns.stream().anyMatch(pattern -> pattern.matcher(url).matches());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.logging.accesslog;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.NonNull;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import org.springframework.web.filter.OncePerRequestFilter;

/** Similar to {@link CommonsRequestLoggingFilter} but uses slf4j */
@Order(Ordered.HIGHEST_PRECEDENCE + 3)
public class AccessLogServletFilter extends OncePerRequestFilter {

private final @NonNull AccessLogFilterConfig config;

public AccessLogServletFilter(@NonNull AccessLogFilterConfig conf) {
this.config = conf;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

try {
filterChain.doFilter(request, response);
} finally {
String uri = request.getRequestURI();
String method = request.getMethod();
int statusCode = response.getStatus();
config.log(method, statusCode, uri);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.logging.mdc.config;

import lombok.Data;

@Data
public class AuthenticationMdcConfigProperties {

/** Whether to append the enduser.id MDC property from the Authentication name */
private boolean id = false;

/**
* Whether to append the enduser.roles MDC property from the Authentication granted authorities
*/
private boolean roles = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.logging.mdc.config;

import lombok.Data;

@Data
public class GeoServerMdcConfigProperties {

private OWSMdcConfigProperties ows = new OWSMdcConfigProperties();

/** Configuration properties to contribute GeoServer OWS request properties to the MDC */
@Data
public static class OWSMdcConfigProperties {
/**
* Whether to append the gs.ows.service.name MDC property from the OWS dispatched request
*/
private boolean serviceName = true;

/**
* Whether to append the gs.ows.service.version MDC property from the OWS dispatched request
*/
private boolean serviceVersion = true;

/**
* Whether to append the gs.ows.service.format MDC property from the OWS dispatched request
*/
private boolean serviceFormat = true;

/**
* Whether to append the gs.ows.service.operation MDC property from the OWS dispatched
* request
*/
private boolean operationName = true;
}
}
Loading

0 comments on commit 3a20c94

Please sign in to comment.