From 5b3a9f8f310bcbaab317cb34b78dcba95e9b9ced Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Fri, 9 Oct 2015 17:07:01 -0600 Subject: [PATCH] Align feign logging. Since all feign clients now have access to the type, logging can apply a type regardless. Fixes gh-431 --- .../main/asciidoc/spring-cloud-netflix.adoc | 85 +++++++++++++------ .../netflix/feign/FeignClientFactoryBean.java | 9 +- .../feign/FeignClientsConfiguration.java | 12 +-- .../netflix/feign/SpringDecoderTests.java | 1 + .../netflix/feign/valid/FeignClientTests.java | 25 ++++-- 5 files changed, 88 insertions(+), 44 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index f9e95727..b3adfd4b 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -606,10 +606,10 @@ declaring additional configuration (on top of the [source,java,indent=0] ---- - @Configuration - @RibbonClient(name = "foo", configuration = FooConfiguration.class) - public class TestConfiguration { - } +@Configuration +@RibbonClient(name = "foo", configuration = FooConfiguration.class) +public class TestConfiguration { +} ---- In this case the client is composed from the components already in @@ -640,13 +640,13 @@ one of the beans described. Example: [source,java,indent=0] ---- - @Configuration - public class FooConfiguration { - @Bean - public IPing ribbonPing(IClientConfig config) { - return new PingUrl(); - } - } +@Configuration +public class FooConfiguration { + @Bean + public IPing ribbonPing(IClientConfig config) { + return new PingUrl(); + } +} ---- This replaces the `NoOpPing` with `PingUrl`. @@ -777,10 +777,10 @@ Spring Cloud lets you take full control of the feign client by declaring additio [source,java,indent=0] ---- - @FeignClient(name = "stores", configuration = FooConfiguration.class) - public interface StoreClient { - //.. - } +@FeignClient(name = "stores", configuration = FooConfiguration.class) +public interface StoreClient { + //.. +} ---- In this case the client is composed from the components already in `FeignClientsConfiguration` together with any in `FooConfiguration` (where the latter will override the former). @@ -810,18 +810,18 @@ Creating a bean of one of those type and placing it in a `@FeignClient` configur [source,java,indent=0] ---- - @Configuration - public class FooConfiguration { - @Bean - public Contract feignContractg() { - return new feign.Contract.Default(); - } +@Configuration +public class FooConfiguration { + @Bean + public Contract feignContractg() { + return new feign.Contract.Default(); + } - @Bean - public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { - return new BasicAuthRequestInterceptor("user", "password"); - } - } + @Bean + public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { + return new BasicAuthRequestInterceptor("user", "password"); + } +} ---- This replaces the `SpringMvcContract` with `feign.Contract.Default` and adds a `RequestInterceptor` to the collection of `RequestInterceptor`. @@ -858,6 +858,8 @@ public class UserResource implements UserService { .UserClient.java [source,java,indent=0] ---- +package project.user; + @FeignClient("users") public interface UserClient extends UserService { @@ -886,6 +888,37 @@ feign.compression.request.min-request-size=2048 These properties allow you to be selective about the compressed media types and minimum request threshold length. +=== Feign logging + +A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the `DEBUG` level. + +.application.yml + +[source,yaml] +---- +logging.level.project.user.UserClient: DEBUG +---- + +The `Logger.Level` object that you may configure per client, tells Feign how much to log. Choices are: + +* `NONE`, No logging (*DEFAULT*). +* `BASIC`, Log only the request method and URL and the response status code and execution time. +* `HEADERS`, Log the basic information along with request and response headers. +* `FULL`, Log the headers, body, and metadata for both requests and responses. + +For example, the following would set the `Logger.Level` to `FULL`: + +[source,java,indent=0] +---- +@Configuration +public class FooConfiguration { + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } +} +---- + == External Configuration: Archaius https://github.com/Netflix/archaius[Archaius] is the Netflix client side configuration library. It is the library used by all of the Netflix OSS components for configuration. Archaius is an extension of the http://commons.apache.org/proper/commons-configuration[Apache Commons Configuration] project. It allows updates to configuration by either polling a source for changes or for a source to push changes to the client. Archaius uses DynamicProperty classes as handles to properties. diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java index 9ef39df6..16f0d51b 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java @@ -67,10 +67,16 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, A } protected Feign.Builder feign(FeignClientFactory factory) { + Logger logger = getOptional(factory, Logger.class); + + if (logger == null) { + logger = new Slf4jLogger(this.type); + } + // @formatter:off Feign.Builder builder = Feign.builder() // required values - .logger(get(factory, Logger.class)) + .logger(logger) .encoder(get(factory, Encoder.class)) .decoder(get(factory, Decoder.class)) .contract(get(factory, Contract.class)); @@ -114,7 +120,6 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, A } protected T loadBalance(Feign.Builder builder, FeignClientFactory factory, Class type, String url) { - builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here? Client client = getOptional(factory, Client.class); if (client != null) { return builder.client(client).target(type, url); diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java index f7c3c40c..6e3dcda8 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.cloud.netflix.feign; -import feign.Client; -import feign.httpclient.ApacheHttpClient; import org.apache.http.client.HttpClient; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -31,11 +29,11 @@ import org.springframework.cloud.netflix.feign.support.SpringMvcContract; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import feign.Client; import feign.Contract; -import feign.Logger; import feign.codec.Decoder; import feign.codec.Encoder; -import feign.slf4j.Slf4jLogger; +import feign.httpclient.ApacheHttpClient; /** * @author Dave Syer @@ -58,12 +56,6 @@ public class FeignClientsConfiguration { return new SpringEncoder(messageConverters); } - @Bean - @ConditionalOnMissingBean - public Logger feignLogger() { - return new Slf4jLogger(); - } - @Bean @ConditionalOnMissingBean public Contract feignContract() { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java index faba4236..b7de5e97 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java @@ -66,6 +66,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean { } public TestClient testClient() { + setType(this.getClass()); return feign(factory).target(TestClient.class, "http://localhost:" + this.port); } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java index 9d3b2e6a..994372f1 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java @@ -29,6 +29,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -56,11 +60,9 @@ import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; import feign.Client; +import feign.Logger; import feign.RequestInterceptor; import feign.RequestTemplate; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; /** * @author Spencer Gibb @@ -68,8 +70,10 @@ import lombok.NoArgsConstructor; */ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = FeignClientTests.Application.class) -@WebIntegrationTest(randomPort = true, value = { "spring.application.name=feignclienttest", - "feign.httpclient.enabled=false"}) +@WebIntegrationTest(randomPort = true, value = { + "spring.application.name=feignclienttest", + "logging.level.org.springframework.cloud.netflix.feign.valid=DEBUG", + "feign.httpclient.enabled=false"}) @DirtiesContext public class FeignClientTests { @@ -113,7 +117,8 @@ public class FeignClientTests { @Configuration @EnableAutoConfiguration @RestController - @EnableFeignClients(clients = {TestClientServiceId.class, TestClient.class}) + @EnableFeignClients(clients = {TestClientServiceId.class, TestClient.class}, + defaultConfiguration = TestDefaultFeignConfig.class) @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class) protected static class Application { @@ -248,6 +253,14 @@ public class FeignClientTests { private String message; } + @Configuration + public static class TestDefaultFeignConfig { + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } + } + // Load balancer with fixed server list for "local" pointing to localhost @Configuration public static class LocalRibbonClientConfiguration {