Skip to content

Commit 53b29bd

Browse files
phipagsvozza
andauthored
feat(logging): Log buffering support for Logj42 and Logback (#2103)
* Log buffer PoC with log4j2 appender implementation. * Add buffer cleaning to avoid memory leak and flushBufferOnUncaughtError option to Logging aspect. * Call appenders with logevent directly from BufferingAppender. * Extract buffer management logic into KeyBuffer class and keep log4j appender minimal. * Unit tests for BufferingAppender * Rename to Log4jConstants. * Make Appender detection independent of plugin name. * Add assertions in unit test to test multiple appenders. * Make assertion stronger to assert that each message appears twice because we configured two appenders as proxies. * Add logback implementation. Decouple KeyBuffer and BufferingAppender to avoid circular dependency when flushing buffer on error. * Log error in AppStream example. * Update Logging E2E test to use log buffering. * Prepare Logging E2E test to support paramaterized versions by logging backend. * Add Logging E2E tests for both logback and log4j2. * Debug not found appender on Linux. * Isolate log4j context loading in unit tests. * Avoid logging config leakage in log4j unit test. * Avoid config leaking in logback unit tests. * Update Graal metadata for powertools-logging. * Update Graal metadata for powertools-logging-log4j. * Update Graal metadata for powertools-logging-logback. * Address Sonar findings. * Address pmd findings. * Set allowCommentedBlocks=true in pmd rules. * Fix thread-safety issue in double-checked singleton instance creation. * Increase scope of thread-safety tests in KeyBuffer. * Try to satisfy pmd executor service closing. * Try to satisfy pmd executor service closing. * Add PMD suppressions with reason where appropriate. * Add PMD suppressions with reason where appropriate. * Restore original example. * Enable log sampling again. * Restore formatting in pmd ruleset. * Update Javadoc of BufferingAppenders. * Remove unncessary cleanup. * Remove duplicated infrastructure clearnup in LoggingE2E. * Make javadoc for Keybuffer more concise. * Add comment for large event rejection. * Fix grammer in error message. * Remove redundant tests in PowertoolsLoggingTest. * Add developer documentation. * Fix sonar finding. * Clarify log buffering log level configuration with upper and lower bound example. * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> * Update docs/core/logging.md Co-authored-by: Stefano Vozza <svozza@gmail.com> --------- Co-authored-by: Stefano Vozza <svozza@gmail.com>
1 parent be3f37f commit 53b29bd

File tree

76 files changed

+3052
-1043
lines changed

Some content is hidden

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

76 files changed

+3052
-1043
lines changed

.github/pmd-ruleset.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@
350350
</rule>
351351
<rule ref="category/java/errorprone.xml/EmptyCatchBlock">
352352
<priority>1</priority>
353+
<properties>
354+
<property name="allowCommentedBlocks" value="true" />
355+
</properties>
353356
</rule>
354357
<!-- <rule ref="category/java/errorprone.xml/EmptyFinalizer" /> -->
355358
<!-- <rule ref="category/java/errorprone.xml/EmptyFinallyBlock" /> -->
@@ -641,4 +644,4 @@
641644
</property>
642645
</properties>
643646
</rule>
644-
</ruleset>
647+
</ruleset>

docs/core/logging.md

Lines changed: 374 additions & 4 deletions
Large diffs are not rendered by default.

examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/App.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,6 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv
9797
}
9898
}
9999

100-
@Tracing
101-
private void log() {
102-
log.info("inside threaded logging for function");
103-
}
104-
105100
@Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED)
106101
private String getPageContents(String address) throws IOException {
107102
URL url = new URL(address);

examples/powertools-examples-core-utilities/sam/src/main/java/helloworld/AppStream.java

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,46 +14,49 @@
1414

1515
package helloworld;
1616

17-
import com.amazonaws.services.lambda.runtime.Context;
18-
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
19-
import com.fasterxml.jackson.databind.ObjectMapper;
20-
17+
import java.io.BufferedReader;
18+
import java.io.BufferedWriter;
2119
import java.io.IOException;
2220
import java.io.InputStream;
21+
import java.io.InputStreamReader;
2322
import java.io.OutputStream;
23+
import java.io.OutputStreamWriter;
24+
import java.io.PrintWriter;
2425
import java.nio.charset.StandardCharsets;
2526

26-
import org.apache.logging.log4j.LogManager;
27-
import org.apache.logging.log4j.Logger;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
30+
import com.amazonaws.services.lambda.runtime.Context;
31+
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
32+
import com.fasterxml.jackson.databind.ObjectMapper;
33+
2834
import software.amazon.lambda.powertools.logging.Logging;
2935
import software.amazon.lambda.powertools.metrics.FlushMetrics;
3036

31-
import java.io.InputStreamReader;
32-
import java.io.BufferedReader;
33-
import java.io.BufferedWriter;
34-
import java.io.OutputStreamWriter;
35-
import java.io.PrintWriter;
36-
3737
public class AppStream implements RequestStreamHandler {
3838
private static final ObjectMapper mapper = new ObjectMapper();
39-
private final static Logger log = LogManager.getLogger(AppStream.class);
39+
private static final Logger log = LoggerFactory.getLogger(AppStream.class);
4040

4141
@Override
4242
@Logging(logEvent = true)
4343
@FlushMetrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true)
44-
// RequestStreamHandler can be used instead of RequestHandler for cases when you'd like to deserialize request body or serialize response body yourself, instead of allowing that to happen automatically
45-
// Note that you still need to return a proper JSON for API Gateway to handle
46-
// See Lambda Response format for examples: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
44+
// RequestStreamHandler can be used instead of RequestHandler for cases when you'd like to deserialize request body
45+
// or serialize response body yourself, instead of allowing that to happen automatically
46+
// Note that you still need to return a proper JSON for API Gateway to handle
47+
// See Lambda Response format for examples:
48+
// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
4749
public void handleRequest(InputStream input, OutputStream output, Context context) {
4850
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
49-
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) {
51+
PrintWriter writer = new PrintWriter(
52+
new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) {
5053

51-
log.info("Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader)));
54+
log.info(
55+
"Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader)));
5256

5357
writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} ");
5458
} catch (IOException e) {
5559
log.error("Something has gone wrong: ", e);
5660
}
5761
}
5862
}
59-

examples/powertools-examples-core-utilities/sam/src/main/resources/log4j2.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
<Console name="JsonAppender" target="SYSTEM_OUT">
55
<JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" />
66
</Console>
7+
<BufferingAppender name="BufferedAppender" bufferAtVerbosity="DEBUG">
8+
<AppenderRef ref="JsonAppender" />
9+
</BufferingAppender>
710
</Appenders>
811
<Loggers>
9-
<Logger name="JsonLogger" level="INFO" additivity="false">
10-
<AppenderRef ref="JsonAppender"/>
11-
</Logger>
12-
<Root level="info">
13-
<AppenderRef ref="JsonAppender"/>
12+
<Root level="debug">
13+
<AppenderRef ref="BufferedAppender" />
1414
</Root>
1515
</Loggers>
16-
</Configuration>
16+
</Configuration>

examples/powertools-examples-core-utilities/sam/template.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
AWSTemplateFormatVersion: '2010-09-09'
1+
AWSTemplateFormatVersion: "2010-09-09"
22
Transform: AWS::Serverless-2016-10-31
33
Description: >
44
CoreUtilities
@@ -13,7 +13,7 @@ Globals:
1313
Environment:
1414
Variables:
1515
# Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables
16-
POWERTOOLS_LOG_LEVEL: INFO
16+
POWERTOOLS_LOG_LEVEL: DEBUG # We use log buffering to buffer DEBUG logs (see log4j2.xml)
1717
POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1
1818
POWERTOOLS_LOGGER_LOG_EVENT: true
1919
POWERTOOLS_METRICS_NAMESPACE: Coreutilities

powertools-e2e-tests/handlers/logging/pom.xml renamed to powertools-e2e-tests/handlers/logging-log4j/pom.xml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,15 @@
88
<version>2.3.0</version>
99
</parent>
1010

11-
<artifactId>e2e-test-handler-logging</artifactId>
11+
<artifactId>e2e-test-handler-logging-log4j</artifactId>
1212
<packaging>jar</packaging>
13-
<name>E2E test handler – Logging</name>
13+
<name>E2E test handler – Logging Log4j</name>
1414

1515
<dependencies>
1616
<dependency>
1717
<groupId>software.amazon.lambda</groupId>
1818
<artifactId>powertools-logging-log4j</artifactId>
1919
</dependency>
20-
<dependency>
21-
<groupId>org.apache.logging.log4j</groupId>
22-
<artifactId>log4j-layout-template-json</artifactId>
23-
<version>2.25.1</version>
24-
</dependency>
2520
<dependency>
2621
<groupId>org.aspectj</groupId>
2722
<artifactId>aspectjrt</artifactId>
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414

1515
package software.amazon.lambda.powertools.e2e;
1616

17-
import com.amazonaws.services.lambda.runtime.Context;
18-
import com.amazonaws.services.lambda.runtime.RequestHandler;
1917
import org.slf4j.Logger;
2018
import org.slf4j.LoggerFactory;
2119
import org.slf4j.MDC;
20+
21+
import com.amazonaws.services.lambda.runtime.Context;
22+
import com.amazonaws.services.lambda.runtime.RequestHandler;
23+
2224
import software.amazon.lambda.powertools.logging.Logging;
25+
import software.amazon.lambda.powertools.logging.PowertoolsLogging;
2326

2427
public class Function implements RequestHandler<Input, String> {
2528
private static final Logger LOG = LoggerFactory.getLogger(Function.class);
@@ -29,6 +32,9 @@ public String handleRequest(Input input, Context context) {
2932
input.getKeys().forEach(MDC::put);
3033
LOG.info(input.getMessage());
3134

35+
// Flush buffer manually since we buffer at INFO level to test log buffering
36+
PowertoolsLogging.flushBuffer();
37+
3238
return "OK";
3339
}
34-
}
40+
}
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ public class Input {
2020
private String message;
2121
private Map<String, String> keys;
2222

23-
public Input() {
24-
}
25-
2623
public String getMessage() {
2724
return message;
2825
}

0 commit comments

Comments
 (0)