Skip to content

Commit 192425f

Browse files
committed
Initial draft to support tenant id
We've got several requests to support an additional (header) field that would allow applications to attribute individual logs to a "tenant". This is an initial set of changes to support that where the HTTP header field (name) is fixed ("tenantid"). As we have seen different ways (names) to refer to the same concept it seems worthwhile to investigate whether the mapping could be made configurable.
1 parent 6165e74 commit 192425f

File tree

10 files changed

+126
-33
lines changed

10 files changed

+126
-33
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ hs_err_pid*
2828
import
2929
target
3030
gen
31+
.factorypath

cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/Fields.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public interface Fields {
1212
public String WRITTEN_AT = "written_at";
1313
public String WRITTEN_TS = "written_ts";
1414
public String CORRELATION_ID = "correlation_id";
15+
public String TENANT_ID = "tenant_id";
1516
public String COMPONENT_ID = "component_id";
1617
public String COMPONENT_NAME = "component_name";
1718
public String COMPONENT_TYPE = "component_type";
@@ -50,4 +51,6 @@ public interface Fields {
5051
public String RESPONSE_CONTENT_TYPE = "response_content_type";
5152
public String REFERER = "referer";
5253
public String X_FORWARDED_FOR = "x_forwarded_for";
54+
55+
5356
}

cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/HttpHeaders.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.sap.hcp.cf.logging.common;
22

3+
import java.util.*;
4+
35
public interface HttpHeaders {
46

57
public String CONTENT_LENGTH = "content-length";
@@ -8,5 +10,22 @@ public interface HttpHeaders {
810
public String X_FORWARDED_FOR = "x-forwarded-for";
911
public String X_VCAP_REQUEST_ID = "x-vcap-request-id";
1012
public String CORRELATION_ID = "X-CorrelationID";
13+
public String TENANT_ID = "tenantid";
14+
15+
public List<String> PROPAGATED_HEADERS = Arrays.asList(
16+
CORRELATION_ID,
17+
TENANT_ID
18+
);
1119

20+
public Map<String, List<String>> ALIASES = new HashMap<String, List<String>>() {
21+
{
22+
put(CONTENT_LENGTH, Arrays.asList(CONTENT_LENGTH));
23+
put(CONTENT_TYPE, Arrays.asList((CONTENT_TYPE)));
24+
put(REFERER, Arrays.asList(REFERER));
25+
put(X_FORWARDED_FOR, Arrays.asList(X_FORWARDED_FOR));
26+
put(X_VCAP_REQUEST_ID, Arrays.asList(X_VCAP_REQUEST_ID));
27+
put(CORRELATION_ID, Arrays.asList(CORRELATION_ID, X_VCAP_REQUEST_ID));
28+
put(TENANT_ID, Arrays.asList(TENANT_ID));
29+
}
30+
};
1231
}

cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/LogContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class LogContext {
1919
private static Map<String, String> CTX_FIELDS = new HashMap<String, String>() {
2020
{
2121
put(Fields.CORRELATION_ID, Defaults.UNKNOWN);
22+
put(Fields.TENANT_ID, Defaults.UNKNOWN);
2223
put(Fields.REQUEST_ID, null);
2324
put(Fields.COMPONENT_ID, Defaults.UNKNOWN);
2425
put(Fields.COMPONENT_NAME, Defaults.UNKNOWN);
@@ -33,6 +34,7 @@ public class LogContext {
3334
};
3435

3536
public static final String HTTP_HEADER_CORRELATION_ID = HttpHeaders.CORRELATION_ID;
37+
public static final String HTTP_HEADER_TENANT_ID = HttpHeaders.TENANT_ID;
3638

3739
public static void loadContextFields(boolean override) {
3840
/*
@@ -80,6 +82,10 @@ public static void initializeContext(String correlationIdFromHeader) {
8082
setOrGenerateCorrelationId(correlationIdFromHeader);
8183
}
8284

85+
public static String get(String key) {
86+
return MDC.get(key);
87+
}
88+
8389
public static String add(String key, String value) {
8490
MDC.put(key, value);
8591
return value;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.sap.hcp.cf.logging.common;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class LogContextAdapter {
7+
8+
private static Map<String, String> HEADER_2_FIELDS = new HashMap<String, String>() {
9+
{
10+
put(HttpHeaders.CORRELATION_ID, Fields.CORRELATION_ID);
11+
put(HttpHeaders.TENANT_ID, Fields.TENANT_ID);
12+
}
13+
};
14+
15+
public static String getValue(String header) {
16+
String field = getField(header);
17+
if (field != null) {
18+
return LogContext.get(field);
19+
} else {
20+
return Defaults.UNKNOWN;
21+
}
22+
}
23+
24+
public static String getField(String header) {
25+
return HEADER_2_FIELDS.get(header);
26+
}
27+
}

cf-java-logging-support-jersey/src/main/java/com/sap/hcp/cf/logging/jersey/filter/ClientRequestUtils.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.sap.hcp.cf.logging.common.HttpHeaders;
66
import com.sap.hcp.cf.logging.common.LogContext;
7+
import com.sap.hcp.cf.logging.common.LogContextAdapter;
78

89
public class ClientRequestUtils {
910

@@ -12,7 +13,9 @@ public static Invocation.Builder propagate(Invocation.Builder builder, javax.ws.
1213
LogContext.initializeContext(reqHeaders != null ? reqHeaders.getHeaderString(HttpHeaders.CORRELATION_ID)
1314
: null);
1415
}
15-
builder.header(HttpHeaders.CORRELATION_ID, LogContext.getCorrelationId());
16+
for (String header: HttpHeaders.PROPAGATED_HEADERS) {
17+
builder.header(header, LogContextAdapter.getValue(header));
18+
}
1619
return builder;
1720
}
1821

cf-java-logging-support-jersey/src/main/java/com/sap/hcp/cf/logging/jersey/filter/RequestHandler.java

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@
22

33
import java.net.URI;
44

5-
import com.sap.hcp.cf.logging.common.Defaults;
6-
import com.sap.hcp.cf.logging.common.Fields;
7-
import com.sap.hcp.cf.logging.common.HttpHeaders;
8-
import com.sap.hcp.cf.logging.common.LogContext;
9-
import com.sap.hcp.cf.logging.common.LogOptionalFieldsSettings;
10-
import com.sap.hcp.cf.logging.common.LongValue;
11-
import com.sap.hcp.cf.logging.common.RequestRecord;
12-
import com.sap.hcp.cf.logging.common.RequestRecordBuilder;
5+
import com.sap.hcp.cf.logging.common.*;
136

147
public class RequestHandler {
158
final LogOptionalFieldsSettings logOptionalFieldsSettings;
@@ -21,21 +14,7 @@ public RequestHandler() {
2114

2215
public RequestRecord handle(RequestContextAdapter adapter) {
2316

24-
/*
25-
* -- This might be an outgoing call and we may already have a
26-
* correlation -- id in the LogContext
27-
*/
28-
String correlationId = LogContext.getCorrelationId();
29-
if (correlationId == null) {
30-
correlationId = getCorrelationIdFromHeader(adapter);
31-
LogContext.initializeContext(correlationId);
32-
/*
33-
* -- it was not in the header, then propagate
34-
*/
35-
if (correlationId == null) {
36-
adapter.setHeader(HttpHeaders.CORRELATION_ID, LogContext.getCorrelationId());
37-
}
38-
}
17+
propagateHeaders(adapter);
3918

4019
boolean isSensitiveConnectionData = logOptionalFieldsSettings.isLogSensitiveConnectionData();
4120
boolean isLogRemoteUserField = logOptionalFieldsSettings.isLogRemoteUserField();
@@ -59,6 +38,21 @@ public RequestRecord handle(RequestContextAdapter adapter) {
5938

6039
}
6140

41+
private void propagateHeaders(RequestContextAdapter adapter) {
42+
/*
43+
* This might be an outgoing call and we may already have a
44+
* correlation id in the LogContext
45+
*/
46+
if (LogContextAdapter.getValue(HttpHeaders.CORRELATION_ID) == null) {
47+
LogContext.initializeContext(getCorrelationIdFromHeader(adapter));
48+
}
49+
for (String header: HttpHeaders.PROPAGATED_HEADERS) {
50+
if (adapter.getHeader(header) == null) {
51+
adapter.setHeader(header, LogContextAdapter.getValue(header));
52+
}
53+
}
54+
}
55+
6256
private String getValue(String value) {
6357
return value != null ? value : Defaults.UNKNOWN;
6458
}

cf-java-logging-support-jersey/src/test/java/com/sap/hcp/cf/logging/jersey/filter/RequestMetricsClientFilterTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
package com.sap.hcp.cf.logging.jersey.filter;
22

33
import static com.sap.hcp.cf.logging.common.LogContext.HTTP_HEADER_CORRELATION_ID;
4+
import static com.sap.hcp.cf.logging.common.LogContext.HTTP_HEADER_TENANT_ID;
45
import static org.hamcrest.Matchers.greaterThan;
56
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
67
import static org.hamcrest.core.Is.is;
78
import static org.hamcrest.core.IsNot.not;
89
import static org.hamcrest.core.IsNull.nullValue;
910
import static org.junit.Assert.assertThat;
11+
import static org.mockito.Mockito.mock;
12+
import static org.mockito.Mockito.when;
1013

1114
import java.util.HashSet;
1215
import java.util.Set;
1316

17+
import javax.ws.rs.client.Invocation;
1418
import javax.ws.rs.core.Application;
1519
import javax.ws.rs.core.Response;
1620

21+
import com.sap.hcp.cf.logging.common.LogContext;
1722
import org.glassfish.jersey.client.ClientConfig;
1823
import org.glassfish.jersey.server.ResourceConfig;
1924
import org.junit.Test;
@@ -69,6 +74,7 @@ public void ChainedResourcePerformanceLogTest() {
6974
correlationIds.add(id);
7075
}
7176
assertThat(correlationIds.size(), is(1));
77+
assertThat(getField(Fields.TENANT_ID), is(Defaults.UNKNOWN));
7278
}
7379

7480
@Test
@@ -85,6 +91,7 @@ public void PerformanceLogTest() {
8591
assertThat(getField(Fields.DIRECTION), is(Direction.OUT.toString()));
8692
assertThat(getField(Fields.METHOD), is(TestResource.EXPECTED_REQUEST_METHOD));
8793
assertThat(getField(Fields.LAYER), is(ClientRequestContextAdapter.LAYER_NAME));
94+
assertThat(getField(Fields.TENANT_ID), is(Defaults.UNKNOWN));
8895
}
8996

9097
@Test
@@ -93,6 +100,14 @@ public void ResponseTimeTest() {
93100
final Response response = target("testresource").request().delete();
94101

95102
assertThat(new Double(getField(Fields.RESPONSE_TIME_MS)), greaterThan(TestResource.EXPECTED_REQUEST_TIME));
103+
assertThat(getField(Fields.TENANT_ID), is(Defaults.UNKNOWN));
96104

97105
}
106+
@Test
107+
public void TenantPropagationTest() {
108+
LogContext.add(Fields.TENANT_ID, "tenant1");
109+
@SuppressWarnings("unused")
110+
final Response response = ClientRequestUtils.propagate(target("testchainedresource").request(), null).get();
111+
assertThat(getField(Fields.TENANT_ID), is("tenant1"));
112+
}
98113
}

cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestRecordFactory.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44

55
import javax.servlet.http.HttpServletRequest;
66

7-
import com.sap.hcp.cf.logging.common.Defaults;
8-
import com.sap.hcp.cf.logging.common.Fields;
9-
import com.sap.hcp.cf.logging.common.HttpHeaders;
10-
import com.sap.hcp.cf.logging.common.LogOptionalFieldsSettings;
11-
import com.sap.hcp.cf.logging.common.RequestRecord;
7+
import com.sap.hcp.cf.logging.common.*;
8+
9+
import java.util.List;
1210

1311
public class RequestRecordFactory {
1412

@@ -22,7 +20,7 @@ public RequestRecord create(HttpServletRequest request) {
2220
boolean isSensitiveConnectionData = logOptionalFieldsSettings.isLogSensitiveConnectionData();
2321
boolean isLogRemoteUserField = logOptionalFieldsSettings.isLogRemoteUserField();
2422
boolean isLogRefererField = logOptionalFieldsSettings.isLogRefererField();
25-
return requestRecord("[SERVLET]").addTag(Fields.REQUEST, getFullRequestUri(request))
23+
RequestRecordBuilder rrb = requestRecord("[SERVLET]").addTag(Fields.REQUEST, getFullRequestUri(request))
2624
.addTag(Fields.METHOD, request.getMethod())
2725
.addTag(Fields.PROTOCOL, getValue(request.getProtocol()))
2826
.addContextTag(Fields.REQUEST_ID, getHeader(request, HttpHeaders.X_VCAP_REQUEST_ID))
@@ -33,8 +31,11 @@ public RequestRecord create(HttpServletRequest request) {
3331
.addOptionalTag(isSensitiveConnectionData, Fields.X_FORWARDED_FOR,
3432
getHeader(request, HttpHeaders.X_FORWARDED_FOR))
3533
.addOptionalTag(isLogRemoteUserField, Fields.REMOTE_USER, getValue(request.getRemoteUser()))
36-
.addOptionalTag(isLogRefererField, Fields.REFERER, getHeader(request, HttpHeaders.REFERER))
37-
.build();
34+
.addOptionalTag(isLogRefererField, Fields.REFERER, getHeader(request, HttpHeaders.REFERER));
35+
for(String header: HttpHeaders.PROPAGATED_HEADERS) {
36+
rrb.addContextTag(LogContextAdapter.getField(header), getHeader(request, header));
37+
}
38+
return rrb.build();
3839
}
3940

4041
private String getFullRequestUri(HttpServletRequest request) {
@@ -44,7 +45,17 @@ private String getFullRequestUri(HttpServletRequest request) {
4445
}
4546

4647
private String getHeader(HttpServletRequest request, String headerName) {
47-
return getValue(request.getHeader(headerName));
48+
List<String> headers = HttpHeaders.ALIASES.get(headerName);
49+
if (headers == null) {
50+
return getValue(request.getHeader(headerName));
51+
}
52+
for (String header: headers) {
53+
String value = request.getHeader(header);
54+
if (value != null) {
55+
return value;
56+
}
57+
}
58+
return Defaults.UNKNOWN;
4859
}
4960

5061
private String getValue(String value) {

cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingFilterTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class RequestLoggingFilterTest {
4242

4343
private static final String REQUEST_ID = "1234-56-7890-xxx";
4444
private static final String CORRELATION_ID = "xxx-56-7890-xxx";
45+
private static final String TENANT_ID = "tenant1";
4546
private static final String REQUEST = "/foobar";
4647
private static final String QUERY_STRING = "baz=bla";
4748
private static final String FULL_REQUEST = REQUEST + "?" + QUERY_STRING;
@@ -134,6 +135,7 @@ public void doFilter(ServletRequest request, ServletResponse response)
134135
assertThat(getField(Fields.COMPONENT_ID), is(Defaults.UNKNOWN));
135136
assertThat(getField(Fields.CONTAINER_ID), is(Defaults.UNKNOWN));
136137
assertThat(getField(Fields.REQUEST_SIZE_B), is("1"));
138+
assertThat(getField(Fields.TENANT_ID), is(Defaults.UNKNOWN));
137139
}
138140

139141
@Test
@@ -159,6 +161,7 @@ public void testWithActivatedOptionalFields() throws IOException, ServletExcepti
159161
assertThat(getField(Fields.COMPONENT_ID), is(Defaults.UNKNOWN));
160162
assertThat(getField(Fields.CONTAINER_ID), is(Defaults.UNKNOWN));
161163
assertThat(getField(Fields.REFERER), is(REFERER));
164+
assertThat(getField(Fields.TENANT_ID), is(Defaults.UNKNOWN));
162165
}
163166

164167
@Test
@@ -184,6 +187,7 @@ public void testWithSuppressedOptionalFields() throws IOException, ServletExcept
184187
assertThat(getField(Fields.REMOTE_HOST), is(Defaults.REDACTED));
185188
assertThat(getField(Fields.COMPONENT_ID), is(Defaults.UNKNOWN));
186189
assertThat(getField(Fields.CONTAINER_ID), is(Defaults.UNKNOWN));
190+
assertThat(getField(Fields.TENANT_ID), is(Defaults.UNKNOWN));
187191
}
188192

189193
@Test
@@ -194,8 +198,18 @@ public void testExplicitCorrelationId() throws IOException, ServletException {
194198
new RequestLoggingFilter().doFilter(mockReq, mockResp, mockFilterChain);
195199
assertThat(getField(Fields.CORRELATION_ID), is(CORRELATION_ID));
196200
assertThat(getField(Fields.CORRELATION_ID), not(REQUEST_ID));
201+
assertThat(getField(Fields.TENANT_ID), is(Defaults.UNKNOWN));
197202
}
198203

204+
@Test
205+
public void testExplicitTenantId() throws IOException, ServletException {
206+
when(mockReq.getHeader(HttpHeaders.TENANT_ID)).thenReturn(TENANT_ID);
207+
when(mockReq.getHeader(HttpHeaders.X_VCAP_REQUEST_ID)).thenReturn(REQUEST_ID);
208+
FilterChain mockFilterChain = mock(FilterChain.class);
209+
new RequestLoggingFilter().doFilter(mockReq, mockResp, mockFilterChain);
210+
assertThat(getField(Fields.TENANT_ID), is(TENANT_ID));
211+
}
212+
199213
protected String getField(String fieldName) throws JSONObjectException, IOException {
200214
return JSON.std.mapFrom(getLastLine()).get(fieldName).toString();
201215
}

0 commit comments

Comments
 (0)