openfeign normalized logger
This commit is contained in:
parent
c07d962847
commit
11feba2cb3
2
.gitignore
vendored
2
.gitignore
vendored
@ -24,3 +24,5 @@
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
.idea
|
||||
.idea/**
|
149
README.md
149
README.md
@ -1,3 +1,148 @@
|
||||
# OpenFeign-NormalizedLogger
|
||||
# OpenFeign Normalized Logger
|
||||
|
||||
Normalized Logger for OpenFeign
|
||||
![](https://img.shields.io/maven-central/v/me.bvn13.openfeign.logger/feign-normalized-logger)
|
||||
|
||||
Standard OpenFeign logger provides the only approach to log communications -
|
||||
it logs every header in separated log entries, the body goes into another log entry.
|
||||
|
||||
It is very inconvenient to deal with such logs in production especially in multithreaded systems.
|
||||
|
||||
This 'Normalized Logger' is intended to combine all log entries related to one request-reply
|
||||
communication into one log entry.
|
||||
|
||||
## Old bad Logger
|
||||
|
||||
All parts are separeted from each other:
|
||||
1) Request:
|
||||
1) request headers - every header is put into separated entry
|
||||
2) requst body - at separated entry
|
||||
2) Response:
|
||||
1) response headers - separately
|
||||
2) response body - at separated entry as well
|
||||
|
||||
```
|
||||
|
||||
2022-07-25 14:12:43.572 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] ---> POST https://example.com/api/v1/login HTTP/1.1
|
||||
2022-07-25 14:12:43.573 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] Content-Length: 23
|
||||
2022-07-25 14:12:43.573 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] Content-Type: application/json
|
||||
2022-07-25 14:12:43.574 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4464.5 Safari/537.36
|
||||
2022-07-25 14:12:43.575 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login]
|
||||
2022-07-25 14:12:43.576 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] {"login":"123456789"}
|
||||
2022-07-25 14:12:43.576 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] ---> END HTTP (21-byte body)
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] <--- UNKNOWN 200 (324ms)
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] cache-control: no-cache
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] cf-cache-status: DYNAMIC
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] cf-ray: 730476518ea441ce-AMS
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] content-type: application/json
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] date: Mon, 25 Jul 2022 11:12:43 GMT
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] feature-policy: accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment *; usb 'none'
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] referrer-policy: strict-origin-when-cross-origin
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] server: cloudflare
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] set-cookie: ACCESS_TOKEN=eyJh9uygCUMA659bAZ54SHpSNy_KFXQ; Max-Age=1800; Domain=.example.com; Path=/; Secure; SameSite=None
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] strict-transport-security: max-age=31536000
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] x-content-type-options: nosniff
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] x-frame-options: sameorigin
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] x-xss-protection: 1; mode=block
|
||||
2022-07-25 14:12:43.901 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login]
|
||||
2022-07-25 14:12:43.902 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] {"status":{"code":"OK","message":"OK"},"body":{"id":"20826"}}
|
||||
2022-07-25 14:12:43.902 DEBUG 1032530 --- [Executor] feign.Logger : [AuthApi#login] <--- END HTTP (61-byte body)
|
||||
|
||||
```
|
||||
|
||||
## New Normalized Logger
|
||||
|
||||
The whole communication (request and response parts) is combined into one log entry.
|
||||
|
||||
```
|
||||
|
||||
2022-07-25 14:16:06.217 INFO 1057053 --- [Executor] com.pf.profee.api.NormalizedFeignLogger : normalized feign request {AuthApi#login(LoginRequestDto)=[AuthApi#login] }: [
|
||||
---> POST https://example.com/api/v1/login HTTP/1.1
|
||||
Content-Length: 23
|
||||
Content-Type: application/json
|
||||
user-agent: bvn13 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4464.5 Safari/537.36
|
||||
|
||||
{"phone":"123456789"}
|
||||
---> END HTTP (21-byte body)
|
||||
] has response [
|
||||
<--- UNKNOWN 200 (411ms)
|
||||
cache-control: no-cache
|
||||
cf-cache-status: DYNAMIC
|
||||
cf-ray: 73047b41ffd2fa4c-AMS
|
||||
content-type: application/json
|
||||
date: Mon, 25 Jul 2022 11:16:06 GMT
|
||||
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
||||
feature-policy: accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment *; usb 'none'
|
||||
referrer-policy: strict-origin-when-cross-origin
|
||||
server: cloudflare
|
||||
set-cookie: QA_JWT_ACCESS_TOKEN=eyJhboft6rzD6Be16dXY5lgQNCzOZNFe4ra_NDIdmXlXi19hlvaQ; Max-Age=1800; Domain=.example.com; Path=/; Secure; SameSite=None
|
||||
strict-transport-security: max-age=31536000
|
||||
x-content-type-options: nosniff
|
||||
x-frame-options: sameorigin
|
||||
x-xss-protection: 1; mode=block
|
||||
|
||||
{"status":{"code":"OK","message":"OK"},"body":{"id":"20826"}}
|
||||
<--- END HTTP (61-byte body)
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
# How to use
|
||||
|
||||
In order to user Normalized Logger into the application they must the following.
|
||||
|
||||
## 0) Check the latest version
|
||||
|
||||
at [Maven Central Repo](https://repo1.maven.org/maven2/me/bvn13/openfeign/logger)
|
||||
|
||||
## 1) Add dependency
|
||||
|
||||
for Maven
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>me.bvn13.openfeign.logger</groupId>
|
||||
<artifactId>feign-normalized-logger</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
for Gradle
|
||||
|
||||
```groovy
|
||||
implementation 'me.bvn13.openfeign.logger:feign-normalized-logger:0.1.0'
|
||||
```
|
||||
|
||||
## 2) Create Feign configuration and enable logger
|
||||
|
||||
```java
|
||||
import feign.Logger;
|
||||
|
||||
public class MyFeignConfig {
|
||||
|
||||
@Bean
|
||||
public Logger logger() {
|
||||
return new NormalizedFeignLogger();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### 3) Use this configuration into `@FeignClient` objects
|
||||
|
||||
```java
|
||||
@FeignClient(name = "auth", configuration = MyFeignConfig.class)
|
||||
public interface AuthApi {
|
||||
/*...methods...*/
|
||||
}
|
||||
```
|
||||
|
||||
### 4) Adjust DEBUG level for Normalized Logger
|
||||
|
||||
for Slf4J + Logback
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
level:
|
||||
me.bvn13.openfeign.logger.NormalizedFeignLogger: DEBUG
|
||||
```
|
229
pom.xml
Normal file
229
pom.xml
Normal file
@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>me.bvn13.openfeign.logger</groupId>
|
||||
<artifactId>feign-normalized-logger</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>OpenFeign Normalized Logger</name>
|
||||
<description>Normalized Logger for OpenFeign</description>
|
||||
<url>https://github.com/bvn13/OpenFeign-NormalizedLogger</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<!-- Java -->
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
|
||||
<!-- Encoding -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
||||
<!-- OpenFeign -->
|
||||
<feign.version>11.9.1</feign.version>
|
||||
<slf4j.version>1.7.9</slf4j.version>
|
||||
|
||||
<!-- Publishing -->
|
||||
<nexus.url>https://s01.oss.sonatype.org</nexus.url>
|
||||
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
|
||||
<nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version>
|
||||
<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
|
||||
<gitflow-maven-plugin.version>1.18.0</gitflow-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>internal.repo</id>
|
||||
<name>Temporary Staging Repository</name>
|
||||
<url>file://${project.build.directory}/mvn-repo</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<version>${feign.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>feign-normalized-logger</finalName>
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.amashchenko.maven.plugin</groupId>
|
||||
<artifactId>gitflow-maven-plugin</artifactId>
|
||||
<version>${gitflow-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<pushRemote>false</pushRemote>
|
||||
<gitFlowConfig>
|
||||
<developmentBranch>develop</developmentBranch>
|
||||
</gitFlowConfig>
|
||||
<incrementVersionAtFinish>true</incrementVersionAtFinish>
|
||||
<versionDigitToIncrement>2</versionDigitToIncrement>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>bvn13</id>
|
||||
<name>Vyacheslav Boyko</name>
|
||||
<email>dev@bvn13.me</email>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/bvn13/OpenFeign-NormalizedLogger.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com:bvn13/OpenFeign-NormalizedLogger.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
<url>https://github.com/bvn13/OpenFeign-NormalizedLogger.git</url>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>${maven-deploy-plugin.version}</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>${nexus-staging-maven-plugin.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-deploy</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>deploy</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>${nexus.url}</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>${maven-gpg-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>${nexus.url}/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<url>${nexus.url}/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>jcenter</id>
|
||||
<name>JCenter</name>
|
||||
<url>https://jcenter.bintray.com/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
@ -0,0 +1,122 @@
|
||||
package me.bvn13.openfeign.logger.normalized;
|
||||
|
||||
import feign.Request;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* OpenFeign Logger
|
||||
* combines request and response part into single log entry:
|
||||
* <br></br>
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* normalized feign request (HERE-IS-CLASS-AND-METHOD): [
|
||||
*
|
||||
* ] has response [
|
||||
*
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class NormalizedFeignLogger extends feign.Logger {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(NormalizedFeignLogger.class);
|
||||
|
||||
private final ThreadLocal<Map<String, String>> methodName;
|
||||
private final ThreadLocal<Map<String, List<String>>> logsRequest;
|
||||
private final ThreadLocal<Map<String, List<String>>> logsResponse;
|
||||
private final ThreadLocal<Map<String, Boolean>> isResponse;
|
||||
|
||||
public NormalizedFeignLogger() {
|
||||
methodName = new ThreadLocal<>();
|
||||
isResponse = new ThreadLocal<>();
|
||||
logsRequest = new ThreadLocal<>();
|
||||
logsResponse = new ThreadLocal<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logRequest(String configKey, Level logLevel, Request request) {
|
||||
init();
|
||||
super.logRequest(configKey, logLevel, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String configKey, String format, Object... args) {
|
||||
if (format.startsWith("--->") && !format.startsWith("---> END")) {
|
||||
// the very beginning
|
||||
clean(configKey);
|
||||
}
|
||||
if (!isResponse.get().getOrDefault(configKey, false)) {
|
||||
log(logsRequest, configKey, format, args);
|
||||
} else {
|
||||
log(logsResponse, configKey, format, args);
|
||||
if (format.startsWith("<--- END")) {
|
||||
showLogs(configKey);
|
||||
}
|
||||
}
|
||||
if (format.startsWith("---> END")) {
|
||||
isResponse.get().put(configKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (isResponse.get() == null) {
|
||||
isResponse.set(new ConcurrentHashMap<>());
|
||||
}
|
||||
if (methodName.get() == null) {
|
||||
methodName.set(new ConcurrentHashMap<>());
|
||||
}
|
||||
if (logsRequest.get() == null) {
|
||||
logsRequest.set(new ConcurrentHashMap<>());
|
||||
}
|
||||
if (logsResponse.get() == null) {
|
||||
logsResponse.set(new ConcurrentHashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
private void clean(String configKey) {
|
||||
isResponse.get().put(configKey, false);
|
||||
methodName.get().put(configKey, methodTag(configKey));
|
||||
logsRequest.get().put(configKey, new LinkedList<>());
|
||||
logsResponse.get().put(configKey, new LinkedList<>());
|
||||
}
|
||||
|
||||
private void log(ThreadLocal<Map<String, List<String>>> container, String configKey, String format, Object... args) {
|
||||
extractList(container, configKey)
|
||||
.add(String.format(format, args));
|
||||
}
|
||||
|
||||
private void showLogs(String configKey) {
|
||||
log.debug("normalized feign request " + methodName.get() + ": [\n" +
|
||||
collectionToDelimitedString(logsRequest.get().getOrDefault(configKey, Collections.emptyList()), "\n") +
|
||||
"\n] has response [\n" +
|
||||
collectionToDelimitedString(logsResponse.get().getOrDefault(configKey, Collections.emptyList()), "\n") +
|
||||
"\n]");
|
||||
}
|
||||
|
||||
private List<String> extractList(ThreadLocal<Map<String, List<String>>> container, String configKey) {
|
||||
return container.get().get(configKey);
|
||||
}
|
||||
|
||||
private String collectionToDelimitedString(Collection<String> collection, String delimeter) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Iterator<String> iter = collection.iterator();
|
||||
int i = 0;
|
||||
while (iter.hasNext()) {
|
||||
if (i++ > 0) {
|
||||
sb.append(delimeter);
|
||||
}
|
||||
sb.append(iter.next());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user