[bs-80] Add configurable / switchable web request trace logging (headers etc)

* Added a bean post processor for the Spring Security filter chain
(so you only get traces by default if security is on)
* Every request is logged at trace level if the dump requests flag is
on
* Requests are also dumped to a TraceRepository for later analysis (very
useful for tracing problems in real time when a support call comes in)

[Fixes #48976001]
This commit is contained in:
Dave Syer
2013-04-30 13:46:46 +01:00
parent dd1fc3f992
commit 833b13bbbc
20 changed files with 652 additions and 97 deletions

View File

@@ -28,6 +28,7 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.PropertySources;
import org.springframework.util.Assert;
import org.springframework.validation.BindException;
@@ -68,6 +69,8 @@ public class PropertiesConfigurationFactory<T> implements FactoryBean<T>,
private String targetName;
private ConversionService conversionService;
/**
* @param target the target object to bind too
* @see #PropertiesConfigurationFactory(Class)
@@ -142,6 +145,13 @@ public class PropertiesConfigurationFactory<T> implements FactoryBean<T>,
this.propertySources = propertySources;
}
/**
* @param conversionService the conversionService to set
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* @param validator the validator to set
*/
@@ -176,6 +186,9 @@ public class PropertiesConfigurationFactory<T> implements FactoryBean<T>,
if (this.validator != null) {
dataBinder.setValidator(this.validator);
}
if (this.conversionService != null) {
dataBinder.setConversionService(this.conversionService);
}
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
customizeBinder(dataBinder);

View File

@@ -21,6 +21,7 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.context.annotation.ConfigurationProperties;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.PropertySources;
import org.springframework.validation.Validator;
@@ -33,6 +34,8 @@ public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {
private Validator validator;
private ConversionService conversionService;
/**
* @param propertySources
*/
@@ -47,6 +50,13 @@ public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {
this.validator = validator;
}
/**
* @param conversionService the conversionService to set
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
@@ -63,6 +73,7 @@ public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {
bean);
factory.setPropertySources(this.propertySources);
factory.setValidator(this.validator);
factory.setConversionService(this.conversionService);
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
String targetName = "".equals(annotation.value()) ? ("".equals(annotation

View File

@@ -56,7 +56,6 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
@Override
public synchronized void stop() {
try {
this.server.setGracefulShutdown(10000);
this.server.stop();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();

View File

@@ -32,8 +32,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.bootstrap.bind.RelaxedDataBinderTests.OAuthConfiguration.OAuthConfigurationValidator;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.validation.BindingResult;
@@ -55,6 +56,8 @@ public class RelaxedDataBinderTests {
@Rule
public ExpectedException expected = ExpectedException.none();
private ConversionService conversionService;
@Test
public void testBindString() throws Exception {
VanillaTarget target = new VanillaTarget();
@@ -108,6 +111,23 @@ public class RelaxedDataBinderTests {
assertEquals(123, target.getNested().getValue());
}
@Test
public void testBindNestedList() throws Exception {
TargetWithNestedList target = new TargetWithNestedList();
bind(target, "nested: bar,foo");
bind(target, "nested[0]: bar");
bind(target, "nested[1]: foo");
assertEquals("[bar, foo]", target.getNested().toString());
}
@Test
public void testBindNestedListCommaDelimitedONly() throws Exception {
TargetWithNestedList target = new TargetWithNestedList();
this.conversionService = new DefaultConversionService();
bind(target, "nested: bar,foo");
assertEquals("[bar, foo]", target.getNested().toString());
}
@Test
public void testBindNestedMap() throws Exception {
TargetWithNestedMap target = new TargetWithNestedMap();
@@ -174,96 +194,13 @@ public class RelaxedDataBinderTests {
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
validatorFactoryBean.afterPropertiesSet();
binder.setValidator(validatorFactoryBean);
binder.setConversionService(this.conversionService);
binder.bind(new MutablePropertyValues(properties));
binder.validate();
return binder.getBindingResult();
}
@Documented
@Target({ ElementType.TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = OAuthConfigurationValidator.class)
public @interface ValidOAuthConfiguration {
}
@ValidOAuthConfiguration
public static class OAuthConfiguration {
private Client client;
private Map<String, OAuthClient> clients;
public Client getClient() {
return this.client;
}
public void setClient(Client client) {
this.client = client;
}
public Map<String, OAuthClient> getClients() {
return this.clients;
}
public void setClients(Map<String, OAuthClient> clients) {
this.clients = clients;
}
public static class Client {
private List<String> autoapprove;
public List<String> getAutoapprove() {
return this.autoapprove;
}
public void setAutoapprove(List<String> autoapprove) {
this.autoapprove = autoapprove;
}
}
public static class OAuthClient {
private String id;
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
}
public static class OAuthConfigurationValidator implements
ConstraintValidator<ValidOAuthConfiguration, OAuthConfiguration> {
@Override
public void initialize(ValidOAuthConfiguration constraintAnnotation) {
}
@Override
public boolean isValid(OAuthConfiguration value,
ConstraintValidatorContext context) {
boolean valid = true;
if (value.client != null && value.client.autoapprove != null) {
if (value.clients != null) {
context.buildConstraintViolationWithTemplate(
"Please use oauth.clients to specifiy autoapprove not client.autoapprove")
.addConstraintViolation();
valid = false;
}
}
return valid;
}
}
}
@Documented
@Target({ ElementType.FIELD })
@Retention(RUNTIME)
@@ -332,6 +269,18 @@ public class RelaxedDataBinderTests {
}
}
public static class TargetWithNestedList {
private List<String> nested;
public List<String> getNested() {
return this.nested;
}
public void setNested(List<String> nested) {
this.nested = nested;
}
}
public static class TargetWithNestedObject {
private VanillaTarget nested;