Skip to content

Commit b202f5c

Browse files
bpcreechBen Creech
andauthored
feat: Add support for JSON logging to stderr (#1027)
Co-authored-by: Ben Creech <[email protected]>
1 parent fd39291 commit b202f5c

File tree

3 files changed

+187
-71
lines changed

3 files changed

+187
-71
lines changed

google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingConfig.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.collect.ImmutableList;
2424
import java.util.ArrayList;
2525
import java.util.List;
26+
import java.util.Optional;
2627
import java.util.logging.Filter;
2728
import java.util.logging.Formatter;
2829
import java.util.logging.Level;
@@ -44,6 +45,7 @@ class LoggingConfig {
4445
private static final String USE_INHERITED_CONTEXT = "useInheritedContext";
4546
private static final String AUTO_POPULATE_METADATA = "autoPopulateMetadata";
4647
private static final String REDIRECT_TO_STDOUT = "redirectToStdout";
48+
private static final String LOG_TARGET = "logTarget";
4749

4850
public LoggingConfig(String className) {
4951
this.className = className;
@@ -87,6 +89,10 @@ Boolean getRedirectToStdout() {
8789
return getBooleanProperty(REDIRECT_TO_STDOUT, null);
8890
}
8991

92+
Optional<LoggingHandler.LogTarget> getLogTarget() {
93+
return Optional.ofNullable(getProperty(LOG_TARGET)).map(LoggingHandler.LogTarget::valueOf);
94+
}
95+
9096
MonitoredResource getMonitoredResource(String projectId) {
9197
String resourceType = getProperty(RESOURCE_TYPE_TAG, "");
9298
return MonitoredResourceUtil.getResource(projectId, resourceType);

google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,15 @@
115115
* Logging (defaults to {@code true}).
116116
* <li>{@code com.google.cloud.logging.LoggingHandler.redirectToStdout} is a boolean flag that
117117
* opts-in redirecting the output of the handler to STDOUT instead of ingesting logs to Cloud
118-
* Logging using Logging API (defaults to {@code true}). Redirecting logs can be used in
118+
* Logging using Logging API (defaults to {@code false}). Redirecting logs can be used in
119119
* Google Cloud environments with installed logging agent to delegate log ingestions to the
120120
* agent. Redirected logs are formatted as one line Json string following the structured
121-
* logging guidelines.
121+
* logging guidelines. This flag is deprecated; use {@code
122+
* com.google.cloud.logging.LoggingHandler.logTarget} instead.
123+
* <li>{@code com.google.cloud.logging.LoggingHandler.logTarget} is an enumeration controlling log
124+
* routing (defaults to {@code CLOUD_LOGGING}). If set to STDOUT or STDERR, logs will be
125+
* printed to the corresponding stream in the Json format that can be parsed by the logging
126+
* agent. If set to CLOUD_LOGGING, logs will be sent directly to the Google Cloud Logging API.
122127
* </ul>
123128
*
124129
* <p>To add a {@code LoggingHandler} to an existing {@link Logger} and be sure to avoid infinite
@@ -136,6 +141,16 @@ public class LoggingHandler extends Handler {
136141
private static final String LEVEL_NAME_KEY = "levelName";
137142
private static final String LEVEL_VALUE_KEY = "levelValue";
138143

144+
/** Where to send logs. */
145+
public enum LogTarget {
146+
/** Sends logs to the Cloud Logging API. */
147+
CLOUD_LOGGING,
148+
/** Sends JSON-formatted logs to stdout, for use with the Google Cloud logging agent. */
149+
STDOUT,
150+
/** Sends JSON-formatted logs to stderr, for use with the Google Cloud logging agent. */
151+
STDERR
152+
}
153+
139154
private final List<LoggingEnhancer> enhancers;
140155
private final LoggingOptions loggingOptions;
141156

@@ -152,7 +167,7 @@ public class LoggingHandler extends Handler {
152167
private volatile Level flushLevel;
153168

154169
private volatile Boolean autoPopulateMetadata;
155-
private volatile Boolean redirectToStdout;
170+
private volatile LogTarget logTarget;
156171

157172
private final WriteOption[] defaultWriteOptions;
158173

@@ -243,7 +258,13 @@ public LoggingHandler(
243258
Boolean f1 = loggingOptions.getAutoPopulateMetadata();
244259
Boolean f2 = config.getAutoPopulateMetadata();
245260
autoPopulateMetadata = isTrueOrNull(f1) && isTrueOrNull(f2);
246-
redirectToStdout = firstNonNull(config.getRedirectToStdout(), Boolean.FALSE);
261+
logTarget =
262+
config
263+
.getLogTarget()
264+
.orElse(
265+
firstNonNull(config.getRedirectToStdout(), Boolean.FALSE)
266+
? LogTarget.STDOUT
267+
: LogTarget.CLOUD_LOGGING);
247268
String logName = log != null ? log : config.getLogName();
248269
MonitoredResource resource =
249270
firstNonNull(
@@ -310,18 +331,24 @@ public void publish(LogRecord record) {
310331
if (logEntry != null) {
311332
try {
312333
Iterable<LogEntry> logEntries =
313-
redirectToStdout
314-
? Instrumentation.populateInstrumentationInfo(ImmutableList.of(logEntry)).y()
315-
: ImmutableList.of(logEntry);
334+
logTarget == LogTarget.CLOUD_LOGGING
335+
? ImmutableList.of(logEntry)
336+
: Instrumentation.populateInstrumentationInfo(ImmutableList.of(logEntry)).y();
316337
if (autoPopulateMetadata) {
317338
logEntries =
318339
logging.populateMetadata(
319340
logEntries, getMonitoredResource(), "com.google.cloud.logging", "java");
320341
}
321-
if (redirectToStdout) {
322-
logEntries.forEach(log -> System.out.println(log.toStructuredJsonString()));
323-
} else {
324-
logging.write(logEntries, defaultWriteOptions);
342+
switch (logTarget) {
343+
case STDOUT:
344+
logEntries.forEach(log -> System.out.println(log.toStructuredJsonString()));
345+
break;
346+
case STDERR:
347+
logEntries.forEach(log -> System.err.println(log.toStructuredJsonString()));
348+
break;
349+
case CLOUD_LOGGING:
350+
logging.write(logEntries, defaultWriteOptions);
351+
break;
325352
}
326353
} catch (RuntimeException ex) {
327354
getErrorManager().error(null, ex, ErrorManager.WRITE_FAILURE);
@@ -425,13 +452,37 @@ public Boolean getAutoPopulateMetadata() {
425452
* Enable/disable redirection to STDOUT. If set to {@code true}, logs will be printed to STDOUT in
426453
* the Json format that can be parsed by the logging agent. If set to {@code false}, logs will be
427454
* ingested to Cloud Logging by calling Logging API.
455+
*
456+
* <p>This method is mutually exclusive with {@link #setLogTarget()}.
457+
*
458+
* @deprecated Use {@link #setLogTarget()}.
428459
*/
460+
@Deprecated
429461
public void setRedirectToStdout(boolean value) {
430-
this.redirectToStdout = value;
462+
this.logTarget = value ? LogTarget.STDOUT : LogTarget.CLOUD_LOGGING;
431463
}
432464

465+
/*
466+
* @deprecated Use {@link #getLogTarget()}.
467+
*/
468+
@Deprecated
433469
public Boolean getRedirectToStdout() {
434-
return redirectToStdout;
470+
return this.logTarget == LogTarget.STDOUT;
471+
}
472+
473+
/**
474+
* Configure the destination for ingested logs. If set to STDOUT or STDERR, logs will be printed
475+
* to the corresponding stream in the Json format that can be parsed by the logging agent. If set
476+
* to CLOUD_LOGGING, logs will be sent directly to the Google Cloud Logging API.
477+
*
478+
* <p>This method is mutually exclusive with {@link #setRedirectToStdout()}.
479+
*/
480+
public void setLogTarget(LogTarget value) {
481+
this.logTarget = value;
482+
}
483+
484+
public LogTarget getLogTarget() {
485+
return logTarget;
435486
}
436487

437488
/**

0 commit comments

Comments
 (0)