Skip to content

Commit 29fb286

Browse files
ennoellerennoellerrammrainERICH JAGOMAGIS
authored
feature/enable-custom-http-logger-interceptor (#23)
Co-authored-by: ennoeller <enno@bitweb.ee> Co-authored-by: Rain Ramm <rain@bitweb.ee> Co-authored-by: ERICH JAGOMAGIS <erich.jagomagis@bitweb.ee>
1 parent 5d8f089 commit 29fb286

47 files changed

Lines changed: 2228 additions & 108 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/build.yml‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,21 @@ jobs:
3333
- name: Run tests and generate reports
3434
run: ./gradlew testAndReport
3535

36-
- name: Run Sonar analysis
37-
if: matrix.Java == '17'
38-
env:
39-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40-
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
41-
run: ./gradlew sonar -x test --no-watch-fs
42-
4336
- name: Upload Artifact
4437
uses: actions/upload-artifact@v4
38+
if: always()
4539
with:
4640
name: report-java-${{ matrix.Java }}
4741
path: build/reports/**
4842
retention-days: 5
4943

44+
- name: Run Sonar analysis
45+
if: matrix.Java == '17'
46+
env:
47+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
49+
run: ./gradlew sonar -x test --no-watch-fs
50+
5051
build:
5152
runs-on: ubuntu-latest
5253
needs: [test]

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Library providing basic generic functionality required by any HTTP web service.
1010

1111
```
1212
// https://mvnrepository.com/artifact/ee.bitweb/spring-core
13-
implementation group: 'ee.bitweb', name: 'spring-core', version: '3.2.0'
13+
implementation group: 'ee.bitweb', name: 'spring-core', version: '4.0.0'
1414
```
1515

1616
Review available versions in [Maven Central](https://mvnrepository.com/artifact/ee.bitweb/spring-core).

‎build.gradle‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ plugins {
1919
}
2020

2121
group 'ee.bitweb'
22-
version '3.4.0'
22+
version '4.0.0'
2323
java {
2424
sourceCompatibility = '17'
2525
}

‎src/main/java/ee/bitweb/core/audit/writers/AuditLogLoggerWriterAdapter.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public void write(Map<String, String> container) {
4141

4242
if (currentContext != null) {
4343
MDC.setContextMap(currentContext);
44+
} else {
45+
MDC.clear();
4446
}
4547
}
4648

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package ee.bitweb.core.retrofit;
2+
3+
import ee.bitweb.core.exception.CoreException;
4+
import org.springframework.boot.context.properties.bind.Binder;
5+
import org.springframework.context.annotation.Condition;
6+
import org.springframework.context.annotation.ConditionContext;
7+
import org.springframework.context.annotation.Conditional;
8+
import org.springframework.core.type.AnnotatedTypeMetadata;
9+
10+
import java.lang.annotation.ElementType;
11+
import java.lang.annotation.Retention;
12+
import java.lang.annotation.RetentionPolicy;
13+
import java.lang.annotation.Target;
14+
import java.util.Map;
15+
16+
@Target({ElementType.METHOD, ElementType.TYPE})
17+
@Retention(RetentionPolicy.RUNTIME)
18+
@Conditional(value = ConditionalOnEnabledRetrofitMapper.MapperEnabled.class)
19+
public @interface ConditionalOnEnabledRetrofitMapper {
20+
21+
String mapper();
22+
23+
class MapperEnabled implements Condition {
24+
25+
@Override
26+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
27+
Map<String, Object> attributes = metadata.getAnnotationAttributes(
28+
ConditionalOnEnabledRetrofitMapper.class.getName()
29+
);
30+
if (attributes == null) return false;
31+
32+
Object mapperKeyOb = attributes.get("mapper");
33+
34+
if (mapperKeyOb == null) return false;
35+
36+
String mapperKey = (String) mapperKeyOb;
37+
38+
RetrofitProperties config = Binder.get(
39+
context.getEnvironment()
40+
).bind(
41+
RetrofitProperties.PREFIX, RetrofitProperties.class
42+
).orElseThrow(
43+
() -> new CoreException("Error occurred while trying to bind environment to RetrofitProperties")
44+
);
45+
46+
return config.getLogging().getMappers().contains(mapperKey);
47+
}
48+
}
49+
}

‎src/main/java/ee/bitweb/core/retrofit/RetrofitAutoConfiguration.java‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
11
package ee.bitweb.core.retrofit;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import ee.bitweb.core.retrofit.builder.LoggingLevel;
45
import ee.bitweb.core.retrofit.interceptor.auth.AuthTokenInjectInterceptor;
56
import ee.bitweb.core.retrofit.interceptor.auth.TokenProvider;
67
import ee.bitweb.core.retrofit.interceptor.auth.criteria.AuthTokenCriteria;
78
import ee.bitweb.core.retrofit.interceptor.auth.criteria.WhitelistCriteria;
9+
import ee.bitweb.core.retrofit.logging.NoopRetrofitLoggingInterceptor;
10+
import ee.bitweb.core.retrofit.logging.RetrofitLoggingInterceptor;
11+
import ee.bitweb.core.retrofit.logging.RetrofitLoggingInterceptorImplementation;
12+
import ee.bitweb.core.retrofit.logging.mappers.*;
13+
import ee.bitweb.core.retrofit.logging.writers.RetrofitLogLoggerWriterAdapter;
814
import lombok.RequiredArgsConstructor;
915
import lombok.extern.slf4j.Slf4j;
1016
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
1117
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
18+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
1219
import org.springframework.context.annotation.Bean;
1320
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.context.annotation.Primary;
1422
import retrofit2.Converter;
1523
import retrofit2.converter.jackson.JacksonConverterFactory;
1624

1725
import java.util.ArrayList;
26+
import java.util.HashSet;
1827
import java.util.List;
1928
import java.util.regex.Pattern;
2029

2130
@Slf4j
2231
@Configuration
2332
@RequiredArgsConstructor
2433
@ConditionalOnProperty(value = RetrofitProperties.PREFIX + ".auto-configuration", havingValue = "true")
34+
@EnableConfigurationProperties({RetrofitProperties.class})
2535
public class RetrofitAutoConfiguration {
2636

2737
@Bean
@@ -64,4 +74,105 @@ public AuthTokenCriteria defaultCriteria(RetrofitProperties properties) {
6474

6575
return criteria;
6676
}
77+
78+
@Bean("defaultRetrofitLoggingInterceptor")
79+
@Primary
80+
public RetrofitLoggingInterceptor defaultRetrofitLoggingInterceptor(
81+
List<RetrofitLoggingMapper> mappers,
82+
RetrofitProperties properties
83+
) {
84+
if (properties.getLogging().getLevel() == LoggingLevel.NONE) {
85+
log.info("Create Default Retrofit Logging Interceptor for logging level NONE");
86+
87+
return new NoopRetrofitLoggingInterceptor();
88+
}
89+
90+
log.info(
91+
"Create Default Retrofit Logging Interceptor with writer {}",
92+
RetrofitLogLoggerWriterAdapter.class.getSimpleName()
93+
);
94+
95+
for (RetrofitLoggingMapper mapper : mappers) {
96+
log.info("Applying Retrofit Log Data Mapper: {}", mapper.getClass());
97+
}
98+
99+
return new RetrofitLoggingInterceptorImplementation(mappers, new RetrofitLogLoggerWriterAdapter());
100+
}
101+
102+
@Bean
103+
@ConditionalOnMissingBean
104+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitRequestMethodMapper.KEY)
105+
public RetrofitRequestMethodMapper retrofitRequestMethodMapper() {
106+
return new RetrofitRequestMethodMapper();
107+
}
108+
109+
@Bean
110+
@ConditionalOnMissingBean
111+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitRequestUrlMapper.KEY)
112+
public RetrofitRequestUrlMapper retrofitRequestUrlMapper() {
113+
return new RetrofitRequestUrlMapper();
114+
}
115+
116+
@Bean
117+
@ConditionalOnMissingBean
118+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitRequestHeadersMapper.KEY)
119+
public RetrofitRequestHeadersMapper retrofitRequestHeadersMapper(
120+
RetrofitProperties retrofitProperties
121+
) {
122+
return new RetrofitRequestHeadersMapper(new HashSet<>(retrofitProperties.getLogging().getSuppressedHeaders()));
123+
}
124+
125+
@Bean
126+
@ConditionalOnMissingBean
127+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitRequestBodySizeMapper.KEY)
128+
public RetrofitRequestBodySizeMapper retrofitRequestBodySizeMapper() {
129+
return new RetrofitRequestBodySizeMapper();
130+
}
131+
132+
@Bean
133+
@ConditionalOnMissingBean
134+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitRequestBodyMapper.KEY)
135+
public RetrofitRequestBodyMapper retrofitRequestBodyMapper(
136+
RetrofitProperties retrofitProperties
137+
) {
138+
return new RetrofitRequestBodyMapper(
139+
retrofitProperties.getLogging().getMaxLoggableRequestBodySize().intValue(),
140+
new HashSet<>(retrofitProperties.getLogging().getRedactedBodyUrls())
141+
);
142+
}
143+
144+
@Bean
145+
@ConditionalOnMissingBean
146+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitResponseStatusCodeMapper.KEY)
147+
public RetrofitResponseStatusCodeMapper retrofitResponseStatusCodeMapper() {
148+
return new RetrofitResponseStatusCodeMapper();
149+
}
150+
151+
@Bean
152+
@ConditionalOnMissingBean
153+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitResponseHeadersMapper.KEY)
154+
public RetrofitResponseHeadersMapper retrofitResponseHeadersMapper(
155+
RetrofitProperties retrofitProperties
156+
) {
157+
return new RetrofitResponseHeadersMapper(new HashSet<>(retrofitProperties.getLogging().getSuppressedHeaders()));
158+
}
159+
160+
@Bean
161+
@ConditionalOnMissingBean
162+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitResponseBodySizeMapper.KEY)
163+
public RetrofitResponseBodySizeMapper retrofitResponseBodySizeMapper() {
164+
return new RetrofitResponseBodySizeMapper();
165+
}
166+
167+
@Bean
168+
@ConditionalOnMissingBean
169+
@ConditionalOnEnabledRetrofitMapper(mapper = RetrofitResponseBodyMapper.KEY)
170+
public RetrofitResponseBodyMapper responseBodyMapper(
171+
RetrofitProperties retrofitProperties
172+
) {
173+
return new RetrofitResponseBodyMapper(
174+
new HashSet<>(retrofitProperties.getLogging().getRedactedBodyUrls()),
175+
retrofitProperties.getLogging().getMaxLoggableResponseBodySize().intValue()
176+
);
177+
}
67178
}

‎src/main/java/ee/bitweb/core/retrofit/RetrofitProperties.java‎

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
package ee.bitweb.core.retrofit;
22

33
import ee.bitweb.core.retrofit.builder.LoggingLevel;
4+
import jakarta.validation.Valid;
5+
import jakarta.validation.constraints.AssertTrue;
6+
import jakarta.validation.constraints.NotBlank;
7+
import jakarta.validation.constraints.NotNull;
48
import lombok.Getter;
59
import lombok.Setter;
610
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
711
import org.springframework.boot.context.properties.ConfigurationProperties;
812
import org.springframework.http.HttpHeaders;
9-
import org.springframework.stereotype.Component;
1013
import org.springframework.util.StringUtils;
1114
import org.springframework.validation.annotation.Validated;
1215

13-
import jakarta.validation.Valid;
14-
import jakarta.validation.constraints.AssertTrue;
15-
import jakarta.validation.constraints.NotBlank;
16-
import jakarta.validation.constraints.NotNull;
17-
import java.util.ArrayList;
18-
import java.util.List;
16+
import java.util.*;
1917

2018
import static ee.bitweb.core.retrofit.RetrofitProperties.PREFIX;
2119

22-
@Component
2320
@Setter
2421
@Getter
2522
@Validated
@@ -47,7 +44,28 @@ public static class Logging {
4744

4845
@NotNull
4946
private LoggingLevel level = LoggingLevel.BASIC;
47+
48+
@NotNull
49+
private Long maxLoggableRequestBodySize = 1024 * 10L;
50+
51+
@NotNull
52+
private Long maxLoggableResponseBodySize = 1024 * 10L;
53+
5054
private List<@NotBlank String> suppressedHeaders = new ArrayList<>();
55+
private List<@NotBlank String> redactedBodyUrls = new ArrayList<>();
56+
57+
private List<@NotBlank String> mappers = new ArrayList<>();
58+
59+
public List<@NotBlank String> getMappers() {
60+
if (level == LoggingLevel.CUSTOM) {
61+
return mappers;
62+
} else {
63+
Set<String> enabledMappers = new HashSet<>(level.getMappers());
64+
enabledMappers.addAll(mappers);
65+
66+
return enabledMappers.stream().toList();
67+
}
68+
}
5169
}
5270

5371
@Getter

‎src/main/java/ee/bitweb/core/retrofit/RetrofitRequestExecutor.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class RetrofitRequestExecutor {
1616
public static <T> T execute(Call<Response<T>> request) {
1717
retrofit2.Response<Response<T>> response = doRequest(request);
1818

19-
if (response.body().getData() == null) {
19+
if (response.body() == null || response.body().getData() == null) {
2020
throw RetrofitException.of(EMPTY_RESPONSE_BODY_ERROR, request, response);
2121
}
2222

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,52 @@
11
package ee.bitweb.core.retrofit.builder;
22

3+
import ee.bitweb.core.retrofit.logging.mappers.*;
34
import lombok.AccessLevel;
45
import lombok.AllArgsConstructor;
56
import lombok.Getter;
6-
import okhttp3.logging.HttpLoggingInterceptor;
77

8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
@Getter
812
@AllArgsConstructor(access = AccessLevel.PRIVATE)
913
public enum LoggingLevel {
1014

11-
NONE(HttpLoggingInterceptor.Level.NONE),
12-
BASIC(HttpLoggingInterceptor.Level.BASIC),
13-
HEADERS(HttpLoggingInterceptor.Level.HEADERS),
14-
BODY(HttpLoggingInterceptor.Level.BODY);
15+
NONE(List.of()),
16+
BASIC(basicMappers()),
17+
HEADERS(headerMappers()),
18+
BODY(bodyMappers()),
19+
CUSTOM(List.of());
20+
21+
private final List<String> mappers;
22+
23+
private static List<String> basicMappers() {
24+
return new ArrayList<>(List.of(
25+
RetrofitRequestMethodMapper.KEY,
26+
RetrofitRequestUrlMapper.KEY,
27+
RetrofitRequestBodySizeMapper.KEY,
28+
RetrofitResponseStatusCodeMapper.KEY,
29+
RetrofitResponseBodySizeMapper.KEY
30+
));
31+
}
32+
33+
private static List<String> headerMappers() {
34+
List<String> mappers = basicMappers();
35+
mappers.addAll(List.of(
36+
RetrofitRequestHeadersMapper.KEY,
37+
RetrofitResponseHeadersMapper.KEY
38+
));
39+
40+
return mappers;
41+
}
42+
43+
private static List<String> bodyMappers() {
44+
List<String> mappers = headerMappers();
45+
mappers.addAll(List.of(
46+
RetrofitRequestBodyMapper.KEY,
47+
RetrofitResponseBodyMapper.KEY
48+
));
1549

16-
@Getter(AccessLevel.PACKAGE)
17-
private HttpLoggingInterceptor.Level level;
50+
return mappers;
51+
}
1852
}

0 commit comments

Comments
 (0)