Skip to content

Commit dcbcb76

Browse files
pankajagrawal16Pankaj Agrawal
and
Pankaj Agrawal
authored
Logging enhancements (#49)
* Util method to add multiple custom keys to logs * test case for log level overrride via env variable Co-authored-by: Pankaj Agrawal <[email protected]>
1 parent 4a5a11b commit dcbcb76

File tree

6 files changed

+92
-3
lines changed

6 files changed

+92
-3
lines changed

docs/content/core/logging.mdx

+22
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ Powertools extends the functionality of Log4J. Below is an example log4j2.xml fi
3737
</Configuration>
3838
```
3939

40+
You can also override log level by setting `LOG_LEVEL` env var - Here is an example using AWS Serverless Application Model (SAM)
41+
42+
```yaml:title=template.yaml
43+
Resources:
44+
HelloWorldFunction:
45+
Type: AWS::Serverless::Function
46+
Properties:
47+
...
48+
Runtime: java8
49+
Environment:
50+
Variables:
51+
LOG_LEVEL: DEBUG # highlight-line
52+
```
53+
4054
You can also explicitly set a service name via `POWERTOOLS_SERVICE_NAME` env var. This sets **service** key that will be present across all log statements.
4155

4256
## Standard structured keys
@@ -137,6 +151,14 @@ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatew
137151
...
138152
PowertoolsLogger.appendKey("test", "willBeLogged");
139153
...
154+
155+
...
156+
Map<String, String> customKeys = new HashMap<>();
157+
customKeys.put("test", "value");
158+
customKeys.put("test1", "value1");
159+
160+
PowertoolsLogger.appendKeys(customKeys);
161+
...
140162
}
141163
}
142164
```

powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/PowertoolsLogger.java

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
*/
1414
package software.amazon.lambda.powertools.logging;
1515

16+
import java.util.Map;
17+
1618
import org.apache.logging.log4j.ThreadContext;
1719

1820
/**
@@ -32,4 +34,15 @@ public class PowertoolsLogger {
3234
public static void appendKey(String key, String value) {
3335
ThreadContext.put(key, value);
3436
}
37+
38+
39+
/**
40+
* Appends additional key and value to each log entry made. Duplicate values
41+
* for the same key will be replaced with the latest.
42+
*
43+
* @param customKeys Map of custom keys values to be appended to logs
44+
*/
45+
public static void appendKeys(Map<String, String> customKeys) {
46+
ThreadContext.putAll(customKeys);
47+
}
3548
}

powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,21 @@
4949
@Aspect
5050
public final class LambdaLoggingAspect {
5151
private static final ObjectMapper mapper = new ObjectMapper();
52-
private static final String LOG_LEVEL = System.getenv("LOG_LEVEL");
52+
private static String LOG_LEVEL = System.getenv("LOG_LEVEL");
5353

5454
static {
55+
resetLogLevels();
56+
}
57+
58+
private static void resetLogLevels() {
5559
if (LOG_LEVEL != null) {
5660
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
5761
Configurator.setAllLevels(LogManager.getRootLogger().getName(), Level.getLevel(LOG_LEVEL));
5862
ctx.updateLoggers();
5963
}
6064
}
6165

62-
@SuppressWarnings({"EmptyMethod", "unused"})
66+
@SuppressWarnings({"EmptyMethod"})
6367
@Pointcut("@annotation(powertoolsLogging)")
6468
public void callAt(PowertoolsLogging powertoolsLogging) {
6569
}

powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java

+33-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
package org.apache.logging.log4j.core.layout;
1515

1616
import java.io.IOException;
17+
import java.lang.reflect.InvocationTargetException;
18+
import java.lang.reflect.Method;
1719
import java.nio.channels.FileChannel;
1820
import java.nio.file.Files;
1921
import java.nio.file.Paths;
@@ -28,8 +30,10 @@
2830
import org.junit.jupiter.api.Test;
2931
import org.mockito.Mock;
3032
import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabled;
33+
import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect;
3134

3235
import static java.util.Collections.emptyMap;
36+
import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField;
3337
import static org.assertj.core.api.Assertions.assertThat;
3438
import static org.assertj.core.api.Assertions.fail;
3539
import static org.mockito.Mockito.when;
@@ -43,11 +47,13 @@ class LambdaJsonLayoutTest {
4347
private Context context;
4448

4549
@BeforeEach
46-
void setUp() throws IOException {
50+
void setUp() throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
4751
initMocks(this);
4852
setupContext();
4953
//Make sure file is cleaned up before running full stack logging regression
5054
FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close();
55+
writeStaticField(LambdaLoggingAspect.class, "LOG_LEVEL", "INFO", true);
56+
resetLogLevel();
5157
}
5258

5359
@Test
@@ -66,6 +72,32 @@ void shouldLogInStructuredFormat() throws IOException {
6672
.containsKey("service"));
6773
}
6874

75+
@Test
76+
void shouldModifyLogLevelBasedOnEnvVariable() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException {
77+
writeStaticField(LambdaLoggingAspect.class, "LOG_LEVEL", "DEBUG", true);
78+
resetLogLevel();
79+
80+
handler.handleRequest("test", context);
81+
82+
assertThat(Files.lines(Paths.get("target/logfile.json")))
83+
.hasSize(2)
84+
.satisfies(line -> {
85+
assertThat(parseToMap(line.get(0)))
86+
.containsEntry("level", "INFO")
87+
.containsEntry("message", "Test event");
88+
89+
assertThat(parseToMap(line.get(1)))
90+
.containsEntry("level", "DEBUG")
91+
.containsEntry("message", "Test debug event");
92+
});
93+
}
94+
95+
private void resetLogLevel() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
96+
Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels");
97+
resetLogLevels.setAccessible(true);
98+
resetLogLevels.invoke(null);
99+
}
100+
69101
private Map<String, Object> parseToMap(String stringAsJson) {
70102
try {
71103
return new ObjectMapper().readValue(stringAsJson, Map.class);

powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/PowertoolsLoggerTest.java

+17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
*/
1414
package software.amazon.lambda.powertools.logging;
1515

16+
import java.util.HashMap;
17+
import java.util.Map;
18+
1619
import org.apache.logging.log4j.ThreadContext;
1720
import org.junit.jupiter.api.BeforeEach;
1821
import org.junit.jupiter.api.Test;
@@ -35,4 +38,18 @@ void shouldSetCustomKeyOnThreadContext() {
3538
.hasSize(1)
3639
.containsEntry("test", "value");
3740
}
41+
42+
@Test
43+
void shouldSetCustomKeyAsMapOnThreadContext() {
44+
Map<String, String> customKeys = new HashMap<>();
45+
customKeys.put("test", "value");
46+
customKeys.put("test1", "value1");
47+
48+
PowertoolsLogger.appendKeys(customKeys);
49+
50+
assertThat(ThreadContext.getImmutableContext())
51+
.hasSize(2)
52+
.containsEntry("test", "value")
53+
.containsEntry("test1", "value1");
54+
}
3855
}

powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class PowerLogToolEnabled implements RequestHandler<Object, Object> {
2626
@PowertoolsLogging
2727
public Object handleRequest(Object input, Context context) {
2828
LOG.info("Test event");
29+
LOG.debug("Test debug event");
2930
return null;
3031
}
3132

0 commit comments

Comments
 (0)