diff --git a/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/README.md b/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/README.md
index ba815c51741e..183a8d62a4c0 100644
--- a/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/README.md
+++ b/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/README.md
@@ -50,6 +50,8 @@ See [Logback filters](https://logback.qos.ch/manual/filters.html#thresholdFilter
INFO
application.log
+
+ /path/to/credentials.json
com.example.enhancers.TestLoggingEnhancer
com.example.enhancers.AnotherEnhancer
WARN
@@ -88,6 +90,7 @@ Authentication
See the [Authentication](https://github.com/googleapis/google-cloud-java#authentication)
section in the base directory's README.
+You can also specify custom credentials by setting the optional property credentialsFile in your configuration file.
Limitations
-----------
diff --git a/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/main/java/com/google/cloud/logging/logback/LoggingAppender.java b/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/main/java/com/google/cloud/logging/logback/LoggingAppender.java
index 23d5e4183fdf..2e49e8d51e9a 100644
--- a/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/main/java/com/google/cloud/logging/logback/LoggingAppender.java
+++ b/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/main/java/com/google/cloud/logging/logback/LoggingAppender.java
@@ -23,6 +23,7 @@
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.util.Loader;
import com.google.api.core.InternalApi;
+import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.MonitoredResource;
import com.google.cloud.logging.LogEntry;
import com.google.cloud.logging.Logging;
@@ -32,6 +33,9 @@
import com.google.cloud.logging.MonitoredResourceUtil;
import com.google.cloud.logging.Payload;
import com.google.cloud.logging.Severity;
+import com.google.common.base.Strings;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -55,6 +59,8 @@
* Standard, GCE and GKE, defaults to "global". See supported resource
* types
+ *
<credentialsFile>/path/to/credentials/file</credentialsFile> (Optional,
+ * defaults to the default credentials of the environment)
* (Optional) add custom labels to log entries using {@link LoggingEnhancer} classes.
* <enhancer>com.example.enhancer1</enhancer>
* <enhancer>com.example.enhancer2</enhancer>
@@ -67,6 +73,7 @@ public class LoggingAppender extends UnsynchronizedAppenderBase {
private static final String LEVEL_VALUE_KEY = "levelValue";
private volatile Logging logging;
+ private LoggingOptions loggingOptions;
private List loggingEnhancers;
private List loggingEventEnhancers;
private WriteOption[] defaultWriteOptions;
@@ -74,6 +81,7 @@ public class LoggingAppender extends UnsynchronizedAppenderBase {
private Level flushLevel;
private String log;
private String resourceType;
+ private String credentialsFile;
private Set enhancerClassNames = new HashSet<>();
private Set loggingEventEnhancerClassNames = new HashSet<>();
@@ -111,6 +119,17 @@ public void setResourceType(String resourceType) {
this.resourceType = resourceType;
}
+ /**
+ * Sets the credentials file to use to create the {@link LoggingOptions}. The credentials returned
+ * by {@link GoogleCredentials#getApplicationDefault()} will be used if no custom credentials file
+ * has been set.
+ *
+ * @param credentialsFile The credentials file to use.
+ */
+ public void setCredentialsFile(String credentialsFile) {
+ this.credentialsFile = credentialsFile;
+ }
+
/** Add extra labels using classes that implement {@link LoggingEnhancer}. */
public void addEnhancer(String enhancerClassName) {
this.enhancerClassNames.add(enhancerClassName);
@@ -186,7 +205,7 @@ public synchronized void start() {
}
String getProjectId() {
- return LoggingOptions.getDefaultInstance().getProjectId();
+ return getLoggingOptions().getProjectId();
}
@Override
@@ -212,13 +231,37 @@ Logging getLogging() {
if (logging == null) {
synchronized (this) {
if (logging == null) {
- logging = LoggingOptions.getDefaultInstance().getService();
+ logging = getLoggingOptions().getService();
}
}
}
return logging;
}
+ /** Gets the {@link LoggingOptions} to use for this {@link LoggingAppender}. */
+ LoggingOptions getLoggingOptions() {
+ if (loggingOptions == null) {
+ if (Strings.isNullOrEmpty(credentialsFile)) {
+ loggingOptions = LoggingOptions.getDefaultInstance();
+ } else {
+ try {
+ loggingOptions =
+ LoggingOptions.newBuilder()
+ .setCredentials(
+ GoogleCredentials.fromStream(new FileInputStream(credentialsFile)))
+ .build();
+ } catch (IOException e) {
+ throw new RuntimeException(
+ String.format(
+ "Could not read credentials file %s. Please verify that the file exists and is a valid Google credentials file.",
+ credentialsFile),
+ e);
+ }
+ }
+ }
+ return loggingOptions;
+ }
+
private LogEntry logEntryFor(ILoggingEvent e) {
StringBuilder payload = new StringBuilder(e.getFormattedMessage()).append('\n');
writeStack(e.getThrowableProxy(), "", payload);
diff --git a/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java b/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java
index 5855c80177d7..9b66d88dc501 100644
--- a/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java
+++ b/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java
@@ -22,6 +22,7 @@
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.fail;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.filter.ThresholdFilter;
@@ -31,6 +32,7 @@
import com.google.cloud.logging.LogEntry;
import com.google.cloud.logging.Logging;
import com.google.cloud.logging.Logging.WriteOption;
+import com.google.cloud.logging.LoggingOptions;
import com.google.cloud.logging.Payload.StringPayload;
import com.google.cloud.logging.Severity;
import com.google.common.collect.ImmutableMap;
@@ -211,6 +213,32 @@ public void testMdcValuesAreConvertedToLabels() {
assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(logEntry);
}
+ @Test
+ public void testCreateLoggingOptions() {
+ // Try to build LoggingOptions with custom credentials.
+ final String nonExistentFile = "/path/to/non/existent/file";
+ LoggingAppender appender = new LoggingAppender();
+ appender.setCredentialsFile(nonExistentFile);
+ try {
+ appender.getLoggingOptions();
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e.getMessage().contains(nonExistentFile));
+ }
+ // Try to build LoggingOptions with default credentials.
+ LoggingOptions defaultOptions = null;
+ try {
+ defaultOptions = LoggingOptions.getDefaultInstance();
+ } catch (Exception e) {
+ // Could not build a default LoggingOptions instance.
+ }
+ if (defaultOptions != null) {
+ appender = new LoggingAppender();
+ LoggingOptions options = appender.getLoggingOptions();
+ assertThat(options).isEqualTo(defaultOptions);
+ }
+ }
+
private LoggingEvent createLoggingEvent(Level level, long timestamp) {
LoggingEvent loggingEvent = new LoggingEvent();
loggingEvent.setMessage("this is a test");