Skip to content

Commit

Permalink
Support userContext for additional info in access log (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
kingster authored Aug 8, 2024
1 parent 9655952 commit 346e261
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 14 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ subprojects {
commons_io : 'commons-io:commons-io:2.6',
junit4 : 'junit:junit:4.12',
assertj : 'org.assertj:assertj-core:3.12.1',
mockito : 'org.mockito:mockito-core:4.11.0',
object_mapper : "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}",
jackson_data_format : "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonVersion}",
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

/**
* Represents the context for access logs, encapsulating details such as client IP, resource path,
Expand All @@ -21,10 +22,13 @@ public class AccessLogContext {
Integer contentLength;
Long responseTime;
Integer responseStatus;
String method;
String protocol;
Supplier<Map<String,String>> userContext;

@Builder.Default
Map<String,String> headers = new HashMap<>();


/**
* Retrieves a map of field names to their values for the current instance.
* This includes the fields of the class and the headers map, as well as the current thread name.
Expand All @@ -43,6 +47,8 @@ private Map<String,Object> getValueMap() {
params.put("headers." + entry.getKey(), entry.getValue());
}
params.put("thread", Thread.currentThread().getName());
if (userContext != null)
params.putAll(userContext.get());
return params;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public class RequestParams<M> {
// Path of the resource being requested.
String resourcePath;

// method of the request.
String method;

// Metadata associated with the request, of generic type M.
M metadata;
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ public class AccessLogGrpcFilter<R extends GeneratedMessageV3, S extends Generat
extends GrpcFilter<R, S> implements Logging {

// The start time of the request processing.
private long startTime;
protected long startTime;

// AccessLogContext of the request being processed
private AccessLogContext.AccessLogContextBuilder accessLogContextBuilder;
protected AccessLogContext.AccessLogContextBuilder accessLogContextBuilder;

// The format string for the access log message.
private static String format;
protected static String format;

// Logger instance for logging access log messages.
private static final Logger logger = Logging.loggerWithName("ACCESS-LOG");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ public class GrpcFilterConfig {
private boolean enableAccessLogs = true;

@JsonProperty("accessLogFormat")
private String accessLogFormat = "{clientIp} {resourcePath} {contentLength} {responseStatus} {responseTime}";
private String accessLogFormat = "{clientIp} {resourcePath} {responseStatus} {contentLength} {responseTime}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@
public class AccessLogHttpFilter extends HttpFilter implements Logging {

// Time when the request processing started.
private long startTime;
protected long startTime;

// Access log context.
private AccessLogContext.AccessLogContextBuilder accessLogContextBuilder;
protected AccessLogContext.AccessLogContextBuilder accessLogContextBuilder;

// Logger instance for logging access log messages.
private static final Logger logger = Logging.loggerWithName("ACCESS-LOG");

// The format string for the access log message.
private static String format;
protected static String format;

public AccessLogHttpFilter() {

Expand Down Expand Up @@ -63,6 +63,8 @@ public void doProcessRequest(ServletRequest req, RequestParams<Map<String,String
accessLogContextBuilder = AccessLogContext.builder()
.clientIp(requestParamsInput.getClientIp())
.resourcePath(requestParamsInput.getResourcePath())
.protocol(req.getProtocol())
.method(requestParamsInput.getMethod())
.headers(requestParamsInput.getMetadata());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ public class HttpFilterConfig {
private boolean enableAccessLogs = true;

@JsonProperty("accessLogFormat")
private String accessLogFormat = "{clientIp} {resourcePath} {contentLength} {responseStatus} {responseTime}";
private String accessLogFormat = "{clientIp} \"{method} {resourcePath} {protocol}\" {responseStatus} {contentLength} {responseTime}";

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ public class AccessLogContextTest {
public void setUp() {
}

private String template = "Formatted {clientIp} {resourcePath} {contentLength} {responseTime} {headers.x-header}";

@Test
public void testSimpleRender() {
String template = "Formatted {clientIp} {resourcePath} {contentLength} {responseTime} {headers.x-header}";
AccessLogContext context =
AccessLogContext.builder()
.clientIp("127.0.0.1")
Expand All @@ -28,4 +28,20 @@ public void testSimpleRender() {
String result = context.format(template);
Assert.assertEquals("Formatted 127.0.0.1 /path 100 1000 value", result);
}

@Test
public void testRenderUserContext() {
String template = "Formatted {clientIp} - {user} {resourcePath} {contentLength} {responseTime} {headers.x-header}";
AccessLogContext context =
AccessLogContext.builder()
.clientIp("127.0.0.1")
.resourcePath("/path")
.responseTime(1000L)
.contentLength(100)
.headers(Collections.singletonMap("x-header", "value"))
.userContext(() -> Collections.singletonMap("user", "username"))
.build();
String result = context.format(template);
Assert.assertEquals("Formatted 127.0.0.1 - username /path 100 1000 value", result);
}
}
2 changes: 1 addition & 1 deletion examples/src/main/resources/hello_world_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Api:
healthcheck.path: "/elb-healthcheck"
filterConfig:
enableAccessLogs: true
accessLogFormat: "{clientIp} {resourcePath} {contentLength} {responseStatus} {responseTime}"
accessLogFormat: '{clientIp} - "{method} {resourcePath} {protocol}" {responseStatus} {contentLength} {responseTime}'

Tracing:
collector.endpoint: http://localhost:9411/api/v2/spans
Expand Down
2 changes: 2 additions & 0 deletions guice/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ dependencies {

testImplementation libraries.junit4
testImplementation libraries.assertj
testImplementation libraries.mockito

}

task sourcesJar(type: Jar, dependsOn: classes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ public final void doFilter(ServletRequest request, ServletResponse response,
.stream().collect(Collectors.toMap(h -> h, httpServletRequest::getHeader));
requestParamsBuilder.metadata(headers);
requestParamsBuilder.clientIp(getClientIp(request));
requestParamsBuilder.resourcePath(httpServletRequest.getRequestURI());
requestParamsBuilder.method(httpServletRequest.getMethod());
requestParamsBuilder.resourcePath(getFullURL(httpServletRequest));
}
RequestParams<Map<String, String>> requestParams = requestParamsBuilder.build();
filters.forEach(filter -> filter.doProcessRequest(request, requestParams));
Expand All @@ -89,14 +90,31 @@ public final void doFilter(ServletRequest request, ServletResponse response,
}
}

/**
* Constructs the full URL from the HttpServletRequest.
*
* @param request The HttpServletRequest object.
* @return The full URL as a string.
*/
protected static String getFullURL(HttpServletRequest request) {
StringBuilder requestURL = new StringBuilder(request.getRequestURL().toString());
String queryString = request.getQueryString();

if (queryString == null) {
return requestURL.toString();
} else {
return requestURL.append('?').append(queryString).toString();
}
}

/**
* Utility method to extract the real client IP address from the ServletRequest. It checks for the
* "X-Forwarded-For" header to support clients connecting through a proxy.
*
* @param request The ServletRequest object containing the client's request
* @return The real IP address of the client
*/
protected String getClientIp(ServletRequest request) {
protected static String getClientIp(ServletRequest request) {
String remoteAddr = request.getRemoteAddr();
String xForwardedFor = ((HttpServletRequest) request).getHeader("X-Forwarded-For");
if (xForwardedFor != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
import org.junit.Before;
import org.junit.Test;

import javax.servlet.http.HttpServletRequest;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class HttpFilterInterceptorTest {

Expand Down Expand Up @@ -52,4 +55,37 @@ public void registerFiltersAddsFiltersToMap2() {
}


@Test
public void getFullURLReturnsURLWithoutQueryString() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURL()).thenReturn(new StringBuffer("http://example.com/test"));
when(request.getQueryString()).thenReturn(null);

String result = HttpFilterInterceptor.getFullURL(request);

assertEquals("http://example.com/test", result);
}

@Test
public void getFullURLReturnsURLWithQueryString() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURL()).thenReturn(new StringBuffer("http://example.com/test"));
when(request.getQueryString()).thenReturn("param1=value1&param2=value2");

String result = HttpFilterInterceptor.getFullURL(request);

assertEquals("http://example.com/test?param1=value1&param2=value2", result);
}

@Test
public void getFullURLHandlesEmptyQueryString() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURL()).thenReturn(new StringBuffer("http://example.com/test"));
when(request.getQueryString()).thenReturn("");

String result = HttpFilterInterceptor.getFullURL(request);

assertEquals("http://example.com/test?", result);
}

}

0 comments on commit 346e261

Please # to comment.