loggingEventEnhancerClassNames = new HashSet<>();
/**
- * Batched logging requests get immediately flushed for logs at or above this level.
+ * Sets a threshold for log severity level to flush all log entries that were batched so far.
*
- * Defaults to Error if not set.
+ *
Defaults to Error.
*
* @param flushLevel Logback log level
*/
@@ -117,49 +131,86 @@ public void setFlushLevel(Level flushLevel) {
}
/**
- * Sets the log filename.
+ * Sets the LOG_ID part of the https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#FIELDS.log_name
+ * for which the logs are ingested.
*
- * @param log filename
+ * @param log LOG_ID part of the name
*/
public void setLog(String log) {
this.log = log;
}
/**
- * Sets the name of the monitored resource (Optional).
+ * Sets the name of the monitored resource (Optional). If not define the appender will try to
+ * identify the resource type automatically. Currently support resource types include "gae_app",
+ * "gce_instance", "k8s_container", "cloud_run_revision" and "cloud_function". If the appender
+ * fails to identify the resource type it will be set to "global".
*
- *
Must be a supported resource type.
- * gae_app, gce_instance and container are auto-detected.
+ *
Must be a one of the supported resource types.
*
- *
Defaults to "global"
- *
- * @param resourceType name of the monitored resource
+ * @param resourceType the name of the monitored resource.
*/
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.
+ * Sets the path to the https://cloud.google.com/iam/docs/creating-managing-service-account-keys. If not set
+ * the appender will use {@link GoogleCredentials#getApplicationDefault()} to authenticate.
*
- * @param credentialsFile The credentials file to use.
+ * @param credentialsFile the path to the credentials file.
*/
public void setCredentialsFile(String credentialsFile) {
this.credentialsFile = credentialsFile;
}
/**
- * Define synchronization mode for writing log entries.
+ * Sets project ID to be used to customize log destination name for written log entries.
+ *
+ * @param projectId The project ID to be used to construct the resource destination name for log
+ * entries.
+ */
+ public void setLogDestinationProjectId(String projectId) {
+ this.logDestinationProjectId = projectId;
+ }
+
+ /**
+ * Sets the log ingestion mode. It can be one of the {@link Synchronicity} values.
*
- * @param flag to set {@code Synchronicity} value.
+ *
Default to {@code Synchronicity.ASYNC}
+ *
+ * @param flag the new ingestion mode.
*/
public void setWriteSynchronicity(Synchronicity flag) {
this.writeSyncFlag = flag;
}
+ /**
+ * Sets the automatic population of metadata fields for ingested logs.
+ *
+ *
Default to {@code true}.
+ *
+ * @param flag the metadata auto-population flag.
+ */
+ public void setAutoPopulateMetadata(boolean flag) {
+ autoPopulateMetadata = flag;
+ }
+
+ /**
+ * Sets the redirect of the appender's output to STDOUT instead of ingesting logs to Cloud Logging
+ * using Logging API.
+ *
+ *
Default to {@code false}.
+ *
+ * @param flag the redirect flag.
+ */
+ public void setRedirectToStdout(boolean flag) {
+ redirectToStdout = flag;
+ }
+
/** Add extra labels using classes that implement {@link LoggingEnhancer}. */
public void addEnhancer(String enhancerClassName) {
this.enhancerClassNames.add(enhancerClassName);
@@ -169,57 +220,68 @@ public void addLoggingEventEnhancer(String enhancerClassName) {
this.loggingEventEnhancerClassNames.add(enhancerClassName);
}
- Level getFlushLevel() {
- return (flushLevel != null) ? flushLevel : Level.ERROR;
+ /**
+ * Returns the current value of the ingestion mode.
+ *
+ *
The method is deprecated. Use appender configuration to setup the ingestion
+ *
+ * @return a {@link Synchronicity} value of the ingestion module.
+ */
+ @Deprecated
+ public Synchronicity getWriteSynchronicity() {
+ return (this.writeSyncFlag != null) ? this.writeSyncFlag : Synchronicity.ASYNC;
}
- String getLogName() {
- return (log != null) ? log : "java.log";
+ private void setupMonitoredResource() {
+ if (monitoredResource == null && autoPopulateMetadata) {
+ monitoredResource = MonitoredResourceUtil.getResource(getProjectId(), resourceType);
+ }
}
- public Synchronicity getWriteSynchronicity() {
- return (this.writeSyncFlag != null) ? this.writeSyncFlag : Synchronicity.ASYNC;
+ @InternalApi("Visible for testing")
+ void setupMonitoredResource(MonitoredResource monitoredResource) {
+ this.monitoredResource = monitoredResource;
}
- MonitoredResource getMonitoredResource(String projectId) {
- return MonitoredResourceUtil.getResource(projectId, resourceType);
+ private Level getFlushLevel() {
+ return (flushLevel != null) ? flushLevel : Level.ERROR;
}
- List getLoggingEnhancers() {
- return getEnhancers(enhancerClassNames);
+ private String getLogName() {
+ return (log != null) ? log : "java.log";
}
- List getLoggingEventEnhancers() {
+ private List getLoggingEnhancers() {
+ return getEnhancers(enhancerClassNames, LoggingEnhancer.class);
+ }
+
+ private List getLoggingEventEnhancers() {
if (loggingEventEnhancerClassNames.isEmpty()) {
return DEFAULT_LOGGING_EVENT_ENHANCERS;
} else {
- return getEnhancers(loggingEventEnhancerClassNames);
+ return getEnhancers(loggingEventEnhancerClassNames, LoggingEventEnhancer.class);
}
}
- List getEnhancers(Set classNames) {
- List loggingEnhancers = new ArrayList<>();
+ private List getEnhancers(Set classNames, Class classOfT) {
+ List enhancers = new ArrayList<>();
if (classNames != null) {
- for (String enhancerClassName : classNames) {
- if (enhancerClassName != null) {
- T enhancer = getEnhancer(enhancerClassName);
- if (enhancer != null) {
- loggingEnhancers.add(enhancer);
+ for (String className : classNames) {
+ if (className != null) {
+ try {
+ T enhancer =
+ Loader.loadClass(className.trim())
+ .asSubclass(classOfT)
+ .getDeclaredConstructor()
+ .newInstance();
+ enhancers.add(enhancer);
+ } catch (Exception ex) {
+ // invalid className: ignore
}
}
}
}
- return loggingEnhancers;
- }
-
- private T getEnhancer(String enhancerClassName) {
- try {
- Class clz = (Class) Loader.loadClass(enhancerClassName.trim());
- return clz.getDeclaredConstructor().newInstance();
- } catch (Exception ex) {
- // If we cannot create the enhancer we fallback to null
- }
- return null;
+ return enhancers;
}
/** Initialize and configure the cloud logging service. */
@@ -228,9 +290,13 @@ public synchronized void start() {
if (isStarted()) {
return;
}
- MonitoredResource resource = getMonitoredResource(getProjectId());
+
+ setupMonitoredResource();
+
defaultWriteOptions =
- new WriteOption[] {WriteOption.logName(getLogName()), WriteOption.resource(resource)};
+ new WriteOption[] {
+ WriteOption.logName(getLogName()), WriteOption.resource(monitoredResource)
+ };
Level flushLevel = getFlushLevel();
if (flushLevel != Level.OFF) {
getLogging().setFlushSeverity(severityFor(flushLevel));
@@ -251,8 +317,26 @@ String getProjectId() {
@Override
protected void append(ILoggingEvent e) {
- LogEntry logEntry = logEntryFor(e);
- getLogging().write(Collections.singleton(logEntry), defaultWriteOptions);
+ Iterable entries = Collections.singleton(logEntryFor(e));
+ if (autoPopulateMetadata) {
+ entries =
+ getLogging()
+ .populateMetadata(
+ entries,
+ monitoredResource,
+ "com.google.cloud.logging",
+ "jdk",
+ "sun",
+ "java",
+ "ch.qos.logback");
+ }
+ if (redirectToStdout) {
+ for (LogEntry entry : entries) {
+ System.out.println(entry.toStructuredJsonString());
+ }
+ } else {
+ getLogging().write(entries, defaultWriteOptions);
+ }
}
@Override
@@ -281,6 +365,7 @@ Logging getLogging() {
}
/** Flushes any pending asynchronous logging writes. */
+ @Deprecated
public void flush() {
if (!isStarted()) {
return;
@@ -293,15 +378,12 @@ public void flush() {
/** Gets the {@link LoggingOptions} to use for this {@link LoggingAppender}. */
protected LoggingOptions getLoggingOptions() {
if (loggingOptions == null) {
- if (Strings.isNullOrEmpty(credentialsFile)) {
- loggingOptions = LoggingOptions.getDefaultInstance();
- } else {
+ LoggingOptions.Builder builder = LoggingOptions.newBuilder();
+ builder.setProjectId(logDestinationProjectId);
+ if (!Strings.isNullOrEmpty(credentialsFile)) {
try {
- loggingOptions =
- LoggingOptions.newBuilder()
- .setCredentials(
- GoogleCredentials.fromStream(new FileInputStream(credentialsFile)))
- .build();
+ builder.setCredentials(
+ GoogleCredentials.fromStream(new FileInputStream(credentialsFile)));
} catch (IOException e) {
throw new RuntimeException(
String.format(
@@ -310,6 +392,9 @@ protected LoggingOptions getLoggingOptions() {
e);
}
}
+ // opt-out metadata auto-population to control it in the appender code
+ builder.setAutoPopulateMetadata(false);
+ loggingOptions = builder.build();
}
return loggingOptions;
}
@@ -328,7 +413,7 @@ private LogEntry logEntryFor(ILoggingEvent e) {
}
LogEntry.Builder builder =
LogEntry.newBuilder(Payload.JsonPayload.of(jsonContent))
- .setTimestamp(e.getTimeStamp())
+ .setTimestamp(Instant.ofEpochMilli(e.getTimeStamp()))
.setSeverity(severity);
builder
.addLabel(LEVEL_NAME_KEY, level.toString())
diff --git a/src/test/java/com/example/enhancers/AnotherTestLoggingEnhancer.java b/src/test/java/com/example/enhancers/AnotherTestLoggingEnhancer.java
deleted file mode 100644
index d9da84471..000000000
--- a/src/test/java/com/example/enhancers/AnotherTestLoggingEnhancer.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2017 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.enhancers;
-
-import com.google.cloud.logging.LogEntry;
-import com.google.cloud.logging.LoggingEnhancer;
-
-public class AnotherTestLoggingEnhancer implements LoggingEnhancer {
-
- @Override
- public void enhanceLogEntry(LogEntry.Builder logEntry) {
- logEntry.addLabel("test-label-2", "test-value-2");
- }
-}
diff --git a/src/test/java/com/example/enhancers/TestLoggingEnhancer.java b/src/test/java/com/example/enhancers/TestLoggingEnhancer.java
deleted file mode 100644
index f982b3f06..000000000
--- a/src/test/java/com/example/enhancers/TestLoggingEnhancer.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2017 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.enhancers;
-
-import com.google.cloud.logging.LogEntry;
-import com.google.cloud.logging.LoggingEnhancer;
-
-public class TestLoggingEnhancer implements LoggingEnhancer {
-
- @Override
- public void enhanceLogEntry(LogEntry.Builder logEntry) {
- logEntry.addLabel("test-label-1", "test-value-1");
- }
-}
diff --git a/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java b/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java
index ab6cd8346..b71747af3 100644
--- a/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java
+++ b/src/test/java/com/google/cloud/logging/logback/LoggingAppenderTest.java
@@ -22,7 +22,6 @@
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;
@@ -33,11 +32,15 @@
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.JsonPayload;
+import com.google.cloud.logging.LoggingEnhancer;
+import com.google.cloud.logging.Payload;
import com.google.cloud.logging.Severity;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import java.util.HashMap;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.time.Instant;
import java.util.Map;
import org.easymock.Capture;
import org.easymock.EasyMock;
@@ -49,14 +52,83 @@
@RunWith(EasyMockRunner.class)
public class LoggingAppenderTest {
- private final String projectId = "test-project";
+ private static final String PROJECT_ID = "test-project";
+ private static final String CRED_FILE_PROJECT_ID = "project-12345";
+ private static final String OVERRIDED_PROJECT_ID = "some-project-id";
+ private static final String DUMMY_CRED_FILE_PATH =
+ "src/test/java/com/google/cloud/logging/logback/dummy-credentials.json";
+ private static Payload.JsonPayload JSON_PAYLOAD =
+ Payload.JsonPayload.of(ImmutableMap.of("message", "this is a test"));
+ private static Payload.JsonPayload JSON_ERROR_PAYLOAD =
+ Payload.JsonPayload.of(
+ ImmutableMap.of(
+ "message",
+ "this is a test",
+ "@type",
+ "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent"));
+ private static final MonitoredResource DEFAULT_RESOURCE =
+ MonitoredResource.of("global", ImmutableMap.of("project_id", PROJECT_ID));
+ private static final LogEntry WARN_ENTRY =
+ LogEntry.newBuilder(JSON_PAYLOAD)
+ .setTimestamp(Instant.ofEpochMilli(100000L))
+ .setSeverity(Severity.WARNING)
+ .setLabels(
+ new ImmutableMap.Builder()
+ .put("levelName", "WARN")
+ .put("levelValue", String.valueOf(30000L))
+ .put("loggerName", LoggingAppenderTest.class.getName())
+ // .put("test-label-1", "test-value-1")
+ // .put("test-label-2", "test-value-2")
+ .build())
+ .build();
+ private static final LogEntry ERROR_ENTRY =
+ LogEntry.newBuilder(JSON_ERROR_PAYLOAD)
+ .setTimestamp(Instant.ofEpochMilli(100000L))
+ .setSeverity(Severity.ERROR)
+ .setLabels(
+ new ImmutableMap.Builder()
+ .put("levelName", "ERROR")
+ .put("levelValue", String.valueOf(40000L))
+ .put("loggerName", LoggingAppenderTest.class.getName())
+ .build())
+ .build();
+ private static final LogEntry INFO_ENTRY =
+ LogEntry.newBuilder(JSON_PAYLOAD)
+ .setTimestamp(Instant.ofEpochMilli(100000L))
+ .setSeverity(Severity.INFO)
+ .setLabels(
+ new ImmutableMap.Builder()
+ .put("levelName", "INFO")
+ .put("levelValue", String.valueOf(20000L))
+ .put("loggerName", LoggingAppenderTest.class.getName())
+ .put("mdc1", "value1")
+ .put("mdc2", "value2")
+ .build())
+ .build();
+
private Logging logging;
private LoggingAppender loggingAppender;
+ static class CustomLoggingEventEnhancer implements LoggingEventEnhancer {
+
+ @Override
+ public void enhanceLogEntry(LogEntry.Builder builder, ILoggingEvent e) {
+ builder.addLabel("foo", "bar");
+ }
+ }
+
+ static class CustomLoggingEnhancer implements LoggingEnhancer {
+
+ @Override
+ public void enhanceLogEntry(LogEntry.Builder builder) {
+ builder.addLabel("foo", "bar");
+ }
+ }
+
class TestLoggingAppender extends LoggingAppender {
@Override
String getProjectId() {
- return projectId;
+ return PROJECT_ID;
}
@Override
@@ -69,6 +141,7 @@ Logging getLogging() {
public void setUp() {
logging = EasyMock.createStrictMock(Logging.class);
loggingAppender = new TestLoggingAppender();
+ loggingAppender.setAutoPopulateMetadata(false);
}
private final WriteOption[] defaultWriteOptions =
@@ -77,29 +150,18 @@ public void setUp() {
WriteOption.resource(
MonitoredResource.newBuilder("global")
.setLabels(
- new ImmutableMap.Builder().put("project_id", projectId).build())
+ new ImmutableMap.Builder()
+ .put("project_id", PROJECT_ID)
+ .build())
.build())
};
@Test
public void testFlushLevelConfigUpdatesLoggingFlushSeverity() {
- Map jsonContent = new HashMap<>();
- jsonContent.put("message", "this is a test");
- JsonPayload payload = JsonPayload.of(jsonContent);
- LogEntry logEntry =
- LogEntry.newBuilder(payload)
- .setTimestamp(100000L)
- .setSeverity(Severity.WARNING)
- .setLabels(
- new ImmutableMap.Builder()
- .put("levelName", "WARN")
- .put("levelValue", String.valueOf(30000L))
- .put("loggerName", this.getClass().getName())
- .build())
- .build();
logging.setFlushSeverity(Severity.WARNING);
Capture> capturedArgument = Capture.newInstance();
- logging.write(capture(capturedArgument), (WriteOption) anyObject(), (WriteOption) anyObject());
+ logging.write(
+ capture(capturedArgument), anyObject(WriteOption.class), anyObject(WriteOption.class));
replay(logging);
Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
LoggingEvent loggingEvent = createLoggingEvent(Level.WARN, timestamp.getSeconds());
@@ -109,7 +171,7 @@ public void testFlushLevelConfigUpdatesLoggingFlushSeverity() {
loggingAppender.doAppend(loggingEvent);
verify(logging);
assertThat(capturedArgument.getValue().iterator().hasNext()).isTrue();
- assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(logEntry);
+ assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(WARN_ENTRY);
}
@Test
@@ -122,26 +184,10 @@ public void testFlushLevelConfigSupportsFlushLevelOff() {
@Test
public void testFilterLogsOnlyLogsAtOrAboveLogLevel() {
- Map jsonContent = new HashMap<>();
- jsonContent.put("message", "this is a test");
- jsonContent.put(
- "@type",
- "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent");
- JsonPayload payload = JsonPayload.of(jsonContent);
- LogEntry logEntry =
- LogEntry.newBuilder(payload)
- .setTimestamp(100000L)
- .setSeverity(Severity.ERROR)
- .setLabels(
- new ImmutableMap.Builder()
- .put("levelName", "ERROR")
- .put("levelValue", String.valueOf(40000L))
- .put("loggerName", this.getClass().getName())
- .build())
- .build();
logging.setFlushSeverity(Severity.ERROR);
Capture> capturedArgument = Capture.newInstance();
- logging.write(capture(capturedArgument), (WriteOption) anyObject(), (WriteOption) anyObject());
+ logging.write(
+ capture(capturedArgument), anyObject(WriteOption.class), anyObject(WriteOption.class));
expectLastCall().once();
replay(logging);
Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
@@ -158,41 +204,7 @@ public void testFilterLogsOnlyLogsAtOrAboveLogLevel() {
loggingAppender.doAppend(loggingEvent2);
verify(logging);
assertThat(capturedArgument.getValue().iterator().hasNext()).isTrue();
- assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(logEntry);
- }
-
- @Test
- public void testEnhancersAddCorrectLabelsToLogEntries() {
- Map jsonContent = new HashMap<>();
- jsonContent.put("message", "this is a test");
- JsonPayload payload = JsonPayload.of(jsonContent);
- LogEntry logEntry =
- LogEntry.newBuilder(payload)
- .setTimestamp(100000L)
- .setSeverity(Severity.WARNING)
- .setLabels(
- new ImmutableMap.Builder()
- .put("levelName", "WARN")
- .put("levelValue", String.valueOf(30000L))
- .put("loggerName", this.getClass().getName())
- .put("test-label-1", "test-value-1")
- .put("test-label-2", "test-value-2")
- .build())
- .build();
- logging.setFlushSeverity(Severity.ERROR);
- Capture> capturedArgument = Capture.newInstance();
- logging.write(capture(capturedArgument), (WriteOption) anyObject(), (WriteOption) anyObject());
- expectLastCall().once();
- replay(logging);
- loggingAppender.addEnhancer("com.example.enhancers.TestLoggingEnhancer");
- loggingAppender.addEnhancer("com.example.enhancers.AnotherTestLoggingEnhancer");
- loggingAppender.start();
- Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
- LoggingEvent loggingEvent = createLoggingEvent(Level.WARN, timestamp.getSeconds());
- loggingAppender.doAppend(loggingEvent);
- verify(logging);
- assertThat(capturedArgument.getValue().iterator().hasNext()).isTrue();
- assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(logEntry);
+ assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(ERROR_ENTRY);
}
@Test
@@ -200,7 +212,8 @@ public void testDefaultWriteOptionsHasExpectedDefaults() {
logging.setFlushSeverity(Severity.ERROR);
Capture logNameArg = Capture.newInstance();
Capture resourceArg = Capture.newInstance();
- logging.write((Iterable) anyObject(), capture(logNameArg), capture(resourceArg));
+ logging.write(
+ EasyMock.>anyObject(), capture(logNameArg), capture(resourceArg));
expectLastCall().once();
replay(logging);
loggingAppender.start();
@@ -215,25 +228,10 @@ public void testDefaultWriteOptionsHasExpectedDefaults() {
@Test
public void testMdcValuesAreConvertedToLabels() {
- Map jsonContent = new HashMap<>();
- jsonContent.put("message", "this is a test");
- JsonPayload payload = JsonPayload.of(jsonContent);
- LogEntry logEntry =
- LogEntry.newBuilder(payload)
- .setTimestamp(100000L)
- .setSeverity(Severity.INFO)
- .setLabels(
- new ImmutableMap.Builder()
- .put("levelName", "INFO")
- .put("levelValue", String.valueOf(20000L))
- .put("loggerName", this.getClass().getName())
- .put("mdc1", "value1")
- .put("mdc2", "value2")
- .build())
- .build();
logging.setFlushSeverity(Severity.ERROR);
Capture> capturedArgument = Capture.newInstance();
- logging.write(capture(capturedArgument), (WriteOption) anyObject(), (WriteOption) anyObject());
+ logging.write(
+ capture(capturedArgument), anyObject(WriteOption.class), anyObject(WriteOption.class));
expectLastCall().once();
replay(logging);
Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
@@ -244,33 +242,32 @@ public void testMdcValuesAreConvertedToLabels() {
loggingAppender.doAppend(loggingEvent);
verify(logging);
assertThat(capturedArgument.getValue().iterator().hasNext()).isTrue();
- assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(logEntry);
+ assertThat(capturedArgument.getValue().iterator().next()).isEqualTo(INFO_ENTRY);
}
- @Test
- public void testCreateLoggingOptions() {
- // Try to build LoggingOptions with custom credentials.
+ @Test(expected = RuntimeException.class)
+ public void testCreateLoggingOptionsWithInvalidCredentials() {
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);
- }
+ appender.getLoggingOptions();
+ }
+
+ @Test
+ public void testCreateLoggingOptionsWithCredentials() {
+ // Try to build LoggingOptions with file based credentials.
+ LoggingAppender appender = new LoggingAppender();
+ appender.setCredentialsFile(DUMMY_CRED_FILE_PATH);
+ assertThat(appender.getLoggingOptions().getProjectId()).isEqualTo(CRED_FILE_PROJECT_ID);
+ }
+
+ @Test
+ public void testCreateLoggingOptionsWithDestination() {
+ // Try to build LoggingOptions with file based credentials.
+ LoggingAppender appender = new LoggingAppender();
+ appender.setCredentialsFile(DUMMY_CRED_FILE_PATH);
+ appender.setLogDestinationProjectId(OVERRIDED_PROJECT_ID);
+ assertThat(appender.getLoggingOptions().getProjectId()).isEqualTo(OVERRIDED_PROJECT_ID);
}
private LoggingEvent createLoggingEvent(Level level, long timestamp) {
@@ -289,7 +286,8 @@ public void testMdcValuesAreConvertedToLabelsWithPassingNullValues() {
MDC.put("mdc3", "value3");
logging.setFlushSeverity(Severity.ERROR);
Capture> capturedArgument = Capture.newInstance();
- logging.write(capture(capturedArgument), (WriteOption) anyObject(), (WriteOption) anyObject());
+ logging.write(
+ capture(capturedArgument), anyObject(WriteOption.class), anyObject(WriteOption.class));
expectLastCall().once();
replay(logging);
Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
@@ -311,13 +309,13 @@ public void testAddCustomLoggingEventEnhancers() {
MDC.put("mdc1", "value1");
logging.setFlushSeverity(Severity.ERROR);
Capture> capturedArgument = Capture.newInstance();
- logging.write(capture(capturedArgument), (WriteOption) anyObject(), (WriteOption) anyObject());
+ logging.write(
+ capture(capturedArgument), anyObject(WriteOption.class), anyObject(WriteOption.class));
expectLastCall().once();
replay(logging);
Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
LoggingEvent loggingEvent = createLoggingEvent(Level.INFO, timestamp.getSeconds());
- loggingAppender.addLoggingEventEnhancer(CustomLoggingEventEnhancer1.class.getName());
- loggingAppender.addLoggingEventEnhancer(CustomLoggingEventEnhancer2.class.getName());
+ loggingAppender.addLoggingEventEnhancer(CustomLoggingEventEnhancer.class.getName());
loggingAppender.start();
loggingAppender.doAppend(loggingEvent);
verify(logging);
@@ -325,15 +323,36 @@ public void testAddCustomLoggingEventEnhancers() {
Map capturedArgumentMap =
capturedArgument.getValue().iterator().next().getLabels();
assertThat(capturedArgumentMap.get("mdc1")).isNull();
- assertThat(capturedArgumentMap.get("foo")).isEqualTo("foo");
- assertThat(capturedArgumentMap.get("bar")).isEqualTo("bar");
+ assertThat(capturedArgumentMap.get("foo")).isEqualTo("bar");
}
@Test
- public void testFlush() {
+ public void testAddCustomLoggingEnhancer() {
logging.setFlushSeverity(Severity.ERROR);
Capture> capturedArgument = Capture.newInstance();
- logging.write(capture(capturedArgument), (WriteOption) anyObject(), (WriteOption) anyObject());
+ logging.write(
+ capture(capturedArgument), anyObject(WriteOption.class), anyObject(WriteOption.class));
+ expectLastCall().once();
+ replay(logging);
+ loggingAppender.addEnhancer(CustomLoggingEnhancer.class.getName());
+ loggingAppender.start();
+ Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
+ LoggingEvent loggingEvent = createLoggingEvent(Level.WARN, timestamp.getSeconds());
+ loggingAppender.doAppend(loggingEvent);
+ verify(logging);
+ Map capturedArgumentMap =
+ capturedArgument.getValue().iterator().next().getLabels();
+ assertThat(capturedArgumentMap.get("foo")).isEqualTo("bar");
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void testFlush() {
+ logging.setFlushSeverity(Severity.ERROR);
+ logging.write(
+ EasyMock.>anyObject(),
+ anyObject(WriteOption.class),
+ anyObject(WriteOption.class));
expectLastCall().times(2);
logging.flush();
replay(logging);
@@ -347,19 +366,84 @@ public void testFlush() {
verify(logging);
}
- static class CustomLoggingEventEnhancer1 implements LoggingEventEnhancer {
+ @Test
+ public void testAutoPopulationEnabled() {
+ logging.setFlushSeverity(Severity.ERROR);
+ Capture> capturedLogEntries = Capture.newInstance();
+ EasyMock.expect(
+ logging.populateMetadata(
+ capture(capturedLogEntries),
+ EasyMock.eq(DEFAULT_RESOURCE),
+ EasyMock.eq("com.google.cloud.logging"),
+ EasyMock.eq("jdk"),
+ EasyMock.eq("sun"),
+ EasyMock.eq("java"),
+ EasyMock.eq("ch.qos.logback")))
+ .andReturn(ImmutableList.of(INFO_ENTRY))
+ .once();
+ // it is impossible to define expectation for varargs using a single anyObject() matcher
+ // see the EasyMock bug https://github.com/easymock/easymock/issues/130.
+ // the following mock uses the known fact that the method pass two WriteOption arguments
+ // the arguments should be replaced with a single anyObject() matchers when the bug is fixed
+ logging.write(
+ EasyMock.>anyObject(),
+ anyObject(WriteOption.class),
+ anyObject(WriteOption.class));
+ expectLastCall().once();
+ replay(logging);
- @Override
- public void enhanceLogEntry(LogEntry.Builder builder, ILoggingEvent e) {
- builder.addLabel("foo", "foo");
- }
+ loggingAppender.setupMonitoredResource(DEFAULT_RESOURCE);
+ loggingAppender.setAutoPopulateMetadata(true);
+ loggingAppender.start();
+ Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
+ LoggingEvent loggingEvent = createLoggingEvent(Level.INFO, timestamp.getSeconds());
+ loggingEvent.setMDCPropertyMap(ImmutableMap.of("mdc1", "value1", "mdc2", "value2"));
+ loggingAppender.doAppend(loggingEvent);
+ verify(logging);
+ LogEntry testLogEntry = capturedLogEntries.getValue().iterator().next();
+ assertThat(testLogEntry).isEqualTo(INFO_ENTRY);
}
- static class CustomLoggingEventEnhancer2 implements LoggingEventEnhancer {
+ @Test
+ public void testRedirectToStdoutEnabled() {
+ logging.setFlushSeverity(Severity.ERROR);
+ EasyMock.expect(
+ logging.populateMetadata(
+ EasyMock.>anyObject(),
+ EasyMock.anyObject(MonitoredResource.class),
+ EasyMock.anyString(),
+ EasyMock.anyString(),
+ EasyMock.anyString(),
+ EasyMock.anyString(),
+ EasyMock.anyString()))
+ .andReturn(ImmutableList.of(INFO_ENTRY))
+ .once();
+ replay(logging);
- @Override
- public void enhanceLogEntry(LogEntry.Builder builder, ILoggingEvent e) {
- builder.addLabel("bar", "bar");
- }
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(bout);
+ System.setOut(out);
+ loggingAppender.setupMonitoredResource(DEFAULT_RESOURCE);
+ loggingAppender.setAutoPopulateMetadata(true);
+ loggingAppender.setRedirectToStdout(true);
+ loggingAppender.start();
+ Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(100000, 0);
+ LoggingEvent loggingEvent = createLoggingEvent(Level.INFO, timestamp.getSeconds());
+ loggingAppender.doAppend(loggingEvent);
+ verify(logging);
+ assertThat(Strings.isNullOrEmpty(bout.toString())).isFalse();
+ System.setOut(null);
+ }
+
+ @Test
+ public void testRedirectToStdoutDisabled() {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(bout);
+ System.setOut(out);
+
+ testAutoPopulationEnabled();
+
+ assertThat(Strings.isNullOrEmpty(bout.toString())).isTrue();
+ System.setOut(null);
}
}
diff --git a/src/test/java/com/google/cloud/logging/logback/dummy-credentials.json b/src/test/java/com/google/cloud/logging/logback/dummy-credentials.json
new file mode 100644
index 000000000..c99e8764e
--- /dev/null
+++ b/src/test/java/com/google/cloud/logging/logback/dummy-credentials.json
@@ -0,0 +1,12 @@
+{
+ "type": "service_account",
+ "project_id": "project-12345",
+ "private_key_id": "12345",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKhPSTDs4cpKfnMc\np86fCkpnuER7bGc+mGkhkw6bE+BnROfrDCFBSjrENLS5JcsenANQ1kYGt9iVW2fd\nZAWUdDoj+t7g6+fDpzY1BzPSUls421Dmu7joDPY8jSdMzFCeg7Lyj0I36bJJ7ooD\nVPW6Q0XQcb8FfBiFPAKuY4elj/YDAgMBAAECgYBo2GMWmCmbM0aL/KjH/KiTawMN\nnfkMY6DbtK9/5LjADHSPKAt5V8ueygSvI7rYSiwToLKqEptJztiO3gnls/GmFzj1\nV/QEvFs6Ux3b0hD2SGpGy1m6NWWoAFlMISRkNiAxo+AMdCi4I1hpk4+bHr9VO2Bv\nV0zKFxmgn1R8qAR+4QJBANqKxJ/qJ5+lyPuDYf5s+gkZWjCLTC7hPxIJQByDLICw\niEnqcn0n9Gslk5ngJIGQcKBXIp5i0jWSdKN/hLxwgHECQQDFKGmo8niLzEJ5sa1r\nspww8Hc2aJM0pBwceshT8ZgVPnpgmITU1ENsKpJ+y1RTjZD6N0aj9gS9UB/UXdTr\nHBezAkEAqkDRTYOtusH9AXQpM3zSjaQijw72Gs9/wx1RxOSsFtVwV6U97CLkV1S+\n2HG1/vn3w/IeFiYGfZXLKFR/pA5BAQJAbFeu6IaGM9yFUzaOZDZ8mnAqMp349t6Q\nDB5045xJxLLWsSpfJE2Y12H1qvO1XUzYNIgXq5ZQOHBFbYA6txBy/QJBAKDRQN47\n6YClq9652X+1lYIY/h8MxKiXpVZVncXRgY6pbj4pmWEAM88jra9Wq6R77ocyECzi\nXCqi18A/sl6ymWc=\n-----END PRIVATE KEY-----\n",
+ "client_email": "project-12345@appspot.gserviceaccount.com",
+ "client_id": "123456789012345678901",
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+ "token_uri": "https://oauth2.googleapis.com/token",
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/project-12345%40appspot.gserviceaccount.com"
+ }
diff --git a/versions.txt b/versions.txt
index ec0d91f01..2f47cd598 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-logging-logback:0.122.9-alpha:0.122.9-alpha
+google-cloud-logging-logback:0.123.0-alpha:0.123.0-alpha