blob: d2e55625d14aea151de94134516d9c2d365d14d1 [file] [log] [blame]
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +02001package eu.mulk.quarkus.googlecloud.jsonlogging;
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +02002
3import java.io.PrintWriter;
4import java.io.StringWriter;
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +02005import java.util.ArrayList;
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +02006import java.util.HashMap;
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +02007import java.util.List;
8import java.util.Map;
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +02009import java.util.logging.Level;
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020010import org.jboss.logmanager.ExtFormatter;
11import org.jboss.logmanager.ExtLogRecord;
12
13/**
14 * Formats log records as JSON for consumption by Google Cloud Logging.
15 *
16 * <p>Meant to be used in containers running on Google Kubernetes Engine (GKE).
17 *
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +020018 * @see LogEntry
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020019 */
Matthias Andreas Benkardc066d892021-05-11 21:27:23 +020020public class Formatter extends ExtFormatter {
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020021
22 private static final String TRACE_LEVEL = "TRACE";
23 private static final String DEBUG_LEVEL = "DEBUG";
24 private static final String INFO_LEVEL = "INFO";
25 private static final String WARNING_LEVEL = "WARNING";
26 private static final String ERROR_LEVEL = "ERROR";
27
28 private static final String ERROR_EVENT_TYPE =
29 "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent";
30
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020031 @Override
32 public String format(ExtLogRecord logRecord) {
33 var message = formatMessageWithStackTrace(logRecord);
34
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +020035 List<StructuredParameter> parameters = new ArrayList<>();
36 Map<String, String> labels = new HashMap<>();
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020037 if (logRecord.getParameters() != null) {
38 for (var parameter : logRecord.getParameters()) {
Matthias Andreas Benkard121a6312021-05-12 05:41:25 +020039 if (parameter instanceof StructuredParameter) {
40 parameters.add((StructuredParameter) parameter);
41 } else if (parameter instanceof Label) {
42 var label = (Label) parameter;
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020043 labels.put(label.key(), label.value());
44 }
45 }
46 }
47
48 var mdc = logRecord.getMdcCopy();
49 var ndc = logRecord.getNdc();
50
51 var sourceLocation =
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +020052 new LogEntry.SourceLocation(
Matthias Andreas Benkard4bae5f12021-05-03 19:16:48 +020053 logRecord.getSourceFileName(),
54 String.valueOf(logRecord.getSourceLineNumber()),
55 String.format(
56 "%s.%s", logRecord.getSourceClassName(), logRecord.getSourceMethodName()));
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020057
58 var entry =
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +020059 new LogEntry(
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020060 message,
61 severityOf(logRecord.getLevel()),
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +020062 new LogEntry.Timestamp(logRecord.getInstant()),
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020063 null,
64 null,
65 sourceLocation,
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +020066 labels,
67 parameters,
68 mdc,
69 ndc,
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020070 logRecord.getLevel().intValue() >= 1000 ? ERROR_EVENT_TYPE : null);
71
Matthias Andreas Benkardb8fbc372021-05-11 06:50:45 +020072 return entry.json().build().toString() + "\n";
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020073 }
74
75 /**
76 * Formats the log message corresponding to {@code logRecord} including a stack trace of the
77 * {@link ExtLogRecord#getThrown()} exception if any.
78 */
79 private String formatMessageWithStackTrace(ExtLogRecord logRecord) {
80 var messageStringWriter = new StringWriter();
81 var messagePrintWriter = new PrintWriter(messageStringWriter);
82 messagePrintWriter.append(this.formatMessage(logRecord));
83
84 if (logRecord.getThrown() != null) {
85 messagePrintWriter.println();
86 logRecord.getThrown().printStackTrace(messagePrintWriter);
87 }
88
89 messagePrintWriter.close();
90 return messageStringWriter.toString();
91 }
92
Matthias Andreas Benkard4bae5f12021-05-03 19:16:48 +020093 /** Computes the Google Cloud Logging severity corresponding to a given {@link Level}. */
Matthias Andreas Benkardc8144a92021-05-03 08:04:53 +020094 private static String severityOf(Level level) {
95 if (level.intValue() < 500) {
96 return TRACE_LEVEL;
97 } else if (level.intValue() < 700) {
98 return DEBUG_LEVEL;
99 } else if (level.intValue() < 900) {
100 return INFO_LEVEL;
101 } else if (level.intValue() < 1000) {
102 return WARNING_LEVEL;
103 } else {
104 return ERROR_LEVEL;
105 }
106 }
107}