diff --git a/README.md b/README.md index 49793d5ee..83f1595b4 100644 --- a/README.md +++ b/README.md @@ -19,38 +19,38 @@ requirements or investigating individual historic traffic issues. Logbook is ready to use out of the box for most common setups. Even for uncommon applications and technologies, it should be simple to implement the necessary interfaces to connect a library/framework/etc. to it. -**Contents:** - - - -- [Features](#features) -- [Dependencies](#dependencies) - * [Jackson Version Support](#jackson-version-support) -- [Installation](#installation) -- [Usage](#usage) - * [Strategy](#strategy) - * [Attribute Extractor](#attribute-extractor) - * [Phases](#phases) - * [Servlet](#servlet) - * [HTTP Client](#http-client) - * [HTTP Client 5](#http-client-5) - * [JAX-RS 3.x (aka Jakarta RESTful Web Services)](#jax-rs-3x-aka-jakarta-restful-web-services) - * [JDK HTTP Server](#jdk-http-server) - * [Netty](#netty) - * [OkHttp v2.x](#okhttp-v2x) - * [OkHttp v3.x](#okhttp-v3x) - * [Ktor](#ktor) - * [Spring](#spring) - * [Spring Boot Starter](#spring-boot-starter) - * [logstash-logback-encoder](#logstash-logback-encoder) -- [Known Issues](#known-issues) -- [Getting Help with Logbook](#getting-help-with-logbook) -- [Getting Involved/Contributing](#getting-involvedcontributing) -- [Alternatives](#alternatives) -- [Credits and References](#credits-and-references) - - - +**Contents:** + + + +- [Features](#features) +- [Dependencies](#dependencies) + * [Jackson Version Support](#jackson-version-support) +- [Installation](#installation) +- [Usage](#usage) + * [Strategy](#strategy) + * [Attribute Extractor](#attribute-extractor) + * [Phases](#phases) + * [Servlet](#servlet) + * [HTTP Client](#http-client) + * [HTTP Client 5](#http-client-5) + * [JAX-RS 3.x (aka Jakarta RESTful Web Services)](#jax-rs-3x-aka-jakarta-restful-web-services) + * [JDK HTTP Server](#jdk-http-server) + * [Netty](#netty) + * [OkHttp v2.x](#okhttp-v2x) + * [OkHttp v3.x](#okhttp-v3x) + * [Ktor](#ktor) + * [Spring](#spring) + * [Spring Boot Starter](#spring-boot-starter) + * [logstash-logback-encoder](#logstash-logback-encoder) +- [Known Issues](#known-issues) +- [Getting Help with Logbook](#getting-help-with-logbook) +- [Getting Involved/Contributing](#getting-involvedcontributing) +- [Alternatives](#alternatives) +- [Credits and References](#credits-and-references) + + + ## Features - **Logging**: of HTTP requests and responses, including the body; partial logging (no body) for unauthorized requests @@ -1036,6 +1036,18 @@ public BodyFilter bodyFilter() { } ``` +If extra custom configuration is wanted, it is possible to implement a LogbookCustomizer bean, for example: + +```java +@Bean +public LogbookCustomizer customizer(Environment env) { + if (env.matchesProfiles("test")) { + return builder -> builder.strategy(new StatusAtLeastStrategy(0)); + } + return builder -> {}; +} +``` + Please refer to [`LogbookAutoConfiguration`](logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookAutoConfiguration.java) or the following table to see a list of possible integration points: @@ -1216,4 +1228,4 @@ more details, check the [contribution guidelines](.github/CONTRIBUTING.md). ![Creative Commons (Attribution-Share Alike 3.0 Unported](https://licensebuttons.net/l/by-sa/3.0/80x15.png) [*Grand Turk, a replica of a three-masted 6th rate frigate from Nelson's days - logbook and charts*](https://commons.wikimedia.org/wiki/File:Grand_Turk(34).jpg) by [JoJan](https://commons.wikimedia.org/wiki/User:JoJan) is licensed under a -[Creative Commons (Attribution-Share Alike 3.0 Unported)](http://creativecommons.org/licenses/by-sa/3.0/). +[Creative Commons (Attribution-Share Alike 3.0 Unported)](http://creativecommons.org/licenses/by-sa/3.0/). diff --git a/logbook-jmh/src/main/java/org/zalando/logbook/benchmark/LogbookState.java b/logbook-jmh/src/main/java/org/zalando/logbook/benchmark/LogbookState.java index ada079234..773fa4838 100644 --- a/logbook-jmh/src/main/java/org/zalando/logbook/benchmark/LogbookState.java +++ b/logbook-jmh/src/main/java/org/zalando/logbook/benchmark/LogbookState.java @@ -8,6 +8,7 @@ import org.zalando.logbook.Logbook; import org.zalando.logbook.Sink; import org.zalando.logbook.autoconfigure.LogbookAutoConfiguration; +import org.zalando.logbook.autoconfigure.LogbookCustomizer; import org.zalando.logbook.autoconfigure.LogbookProperties; import org.zalando.logbook.json.CompactingJsonBodyFilter; import org.zalando.logbook.logstash.LogstashLogbackSink; @@ -28,15 +29,20 @@ public void setUp(final HttpLogFormatterState state) { final LogbookProperties properties = new LogbookProperties(); final LogbookAutoConfiguration ac = new LogbookAutoConfiguration(properties); - autoconfigurationLogbook = ac.logbook(ac.requestCondition(), ac.correlationId(), Collections.singletonList(ac.headerFilter()), Collections.singletonList(ac.pathFilter()), Collections.singletonList(ac.queryFilter()), Collections.singletonList(ac.bodyFilter()), Collections.singletonList(ac.requestFilter()), Collections.singletonList(ac.responseFilter()), ac.strategy(), null, ac.sink(ac.httpFormatter(), ac.writer())); - + autoconfigurationLogbook = setup(ac.defaultLogbookConfiguration(ac.requestCondition(), ac.correlationId(), Collections.singletonList(ac.headerFilter()), Collections.singletonList(ac.pathFilter()), Collections.singletonList(ac.queryFilter()), Collections.singletonList(ac.bodyFilter()), Collections.singletonList(ac.requestFilter()), Collections.singletonList(ac.responseFilter()), ac.strategy(), null, ac.sink(ac.httpFormatter(), ac.writer()))); final Sink sink = new LogstashLogbackSink(state.getJsonHttpLogFormatter()); - autoconfigurationLogstashLogbook = ac.logbook(ac.requestCondition(), ac.correlationId(), Collections.singletonList(ac.headerFilter()), Collections.singletonList(ac.pathFilter()), Collections.singletonList(ac.queryFilter()), List.of(ac.bodyFilter(), new CompactingJsonBodyFilter()), Collections.singletonList(ac.requestFilter()), Collections.singletonList(ac.responseFilter()), ac.strategy(), null, sink); + autoconfigurationLogstashLogbook = setup(ac.defaultLogbookConfiguration(ac.requestCondition(), ac.correlationId(), Collections.singletonList(ac.headerFilter()), Collections.singletonList(ac.pathFilter()), Collections.singletonList(ac.queryFilter()), List.of(ac.bodyFilter(), new CompactingJsonBodyFilter()), Collections.singletonList(ac.requestFilter()), Collections.singletonList(ac.responseFilter()), ac.strategy(), null, sink)); final Sink noop = new LogstashLogbackSink(state.getNoopHttpLogFormatter()); - noopHttpLogFormatterLogbook = ac.logbook(ac.requestCondition(), ac.correlationId(), Collections.singletonList(ac.headerFilter()), Collections.singletonList(ac.pathFilter()), Collections.singletonList(ac.queryFilter()), List.of(ac.bodyFilter(), new CompactingJsonBodyFilter()), Collections.singletonList(ac.requestFilter()), Collections.singletonList(ac.responseFilter()), ac.strategy(), null, noop); + noopHttpLogFormatterLogbook = setup(ac.defaultLogbookConfiguration(ac.requestCondition(), ac.correlationId(), Collections.singletonList(ac.headerFilter()), Collections.singletonList(ac.pathFilter()), Collections.singletonList(ac.queryFilter()), List.of(ac.bodyFilter(), new CompactingJsonBodyFilter()), Collections.singletonList(ac.requestFilter()), Collections.singletonList(ac.responseFilter()), ac.strategy(), null, noop)); + } + + private Logbook setup(LogbookCustomizer customizer) { + var builder = Logbook.builder(); + customizer.customize(builder); + return builder.build(); } diff --git a/logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookAutoConfiguration.java b/logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookAutoConfiguration.java index 0208c46f5..eebcce582 100644 --- a/logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookAutoConfiguration.java +++ b/logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookAutoConfiguration.java @@ -7,6 +7,7 @@ import lombok.Generated; import org.apache.http.client.HttpClient; import org.apiguardian.api.API; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -22,21 +23,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Scope; import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.security.web.SecurityFilterChain; -import org.zalando.logbook.BodyFilter; -import org.zalando.logbook.CorrelationId; -import org.zalando.logbook.HeaderFilter; -import org.zalando.logbook.HttpLogFormatter; -import org.zalando.logbook.HttpLogWriter; -import org.zalando.logbook.HttpRequest; -import org.zalando.logbook.Logbook; -import org.zalando.logbook.PathFilter; -import org.zalando.logbook.QueryFilter; -import org.zalando.logbook.RequestFilter; -import org.zalando.logbook.ResponseFilter; -import org.zalando.logbook.Sink; -import org.zalando.logbook.Strategy; +import org.zalando.logbook.*; import org.zalando.logbook.attributes.AttributeExtractor; import org.zalando.logbook.core.attributes.CompositeAttributeExtractor; import org.zalando.logbook.attributes.NoOpAttributeExtractor; @@ -76,7 +67,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.function.Predicate; -import java.util.stream.Collectors; import java.util.stream.Stream; import static jakarta.servlet.DispatcherType.ASYNC; @@ -111,7 +101,24 @@ public LogbookAutoConfiguration(final LogbookProperties properties) { @API(status = INTERNAL) @Bean @ConditionalOnMissingBean(Logbook.class) - public Logbook logbook( + public Logbook logbook(LogbookCreator.Builder builder) { + return builder.build(); + } + + @API(status = INTERNAL) + @Bean + @ConditionalOnMissingBean(LogbookCreator.Builder.class) + @Scope("prototype") + public LogbookCreator.Builder logbookBuilder(final ObjectProvider customizers) { + var builder = Logbook.builder(); + customizers.orderedStream().forEach(customizer -> customizer.customize(builder)); + return builder; + } + + @Bean + @Order(0) + @ConditionalOnMissingBean(name = "defaultLogbookConfiguration") + public LogbookCustomizer defaultLogbookConfiguration( final Predicate condition, final CorrelationId correlationId, final List headerFilters, @@ -123,8 +130,7 @@ public Logbook logbook( final Strategy strategy, final AttributeExtractor attributeExtractor, final Sink sink) { - - return Logbook.builder() + return builder -> builder .condition(mergeWithExcludes(mergeWithIncludes(condition))) .correlationId(correlationId) .headerFilters(headerFilters) @@ -135,8 +141,7 @@ public Logbook logbook( .responseFilters(responseFilters) .strategy(strategy) .attributeExtractor(attributeExtractor) - .sink(sink) - .build(); + .sink(sink); } private Collection mergeWithTruncation(List bodyFilters) { diff --git a/logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookCustomizer.java b/logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookCustomizer.java new file mode 100644 index 000000000..5820ef17b --- /dev/null +++ b/logbook-spring-boot-autoconfigure/src/main/java/org/zalando/logbook/autoconfigure/LogbookCustomizer.java @@ -0,0 +1,8 @@ +package org.zalando.logbook.autoconfigure; + +import org.zalando.logbook.LogbookCreator; + +@FunctionalInterface +public interface LogbookCustomizer { + void customize(LogbookCreator.Builder builder); +}