diff options
author | Kristen Kozak <sebright@google.com> | 2018-05-25 01:02:27 -0700 |
---|---|---|
committer | Kristen Kozak <sebright@google.com> | 2018-06-12 15:38:56 -0700 |
commit | d34e4b2fb33eb26ea8cf88889682839579880bfb (patch) | |
tree | c7f970830254014bbe345f8486b6332102cff620 /contrib/log_correlation | |
parent | a6e0485dd24b22779c0ca2d7b03b9c4a9da7a3ba (diff) | |
download | opencensus-java-d34e4b2fb33eb26ea8cf88889682839579880bfb.tar.gz |
Start adding log correlation for Stackdriver Logging.
Diffstat (limited to 'contrib/log_correlation')
8 files changed, 343 insertions, 0 deletions
diff --git a/contrib/log_correlation/stackdriver/README.md b/contrib/log_correlation/stackdriver/README.md new file mode 100644 index 00000000..0b43d8c2 --- /dev/null +++ b/contrib/log_correlation/stackdriver/README.md @@ -0,0 +1,6 @@ +# OpenCensus Stackdriver Log Correlation + +This subproject is currently experimental. It provides a Stackdriver Logging LoggingEnhancer that +automatically adds tracing data to log entries. The LoggingEnhancer adds the trace ID, which allows +Stackdriver to display log entries associated with each trace or filter logs based on trace ID. It +currently also adds the span ID and sampling decision. diff --git a/contrib/log_correlation/stackdriver/build.gradle b/contrib/log_correlation/stackdriver/build.gradle new file mode 100644 index 00000000..1b149ab7 --- /dev/null +++ b/contrib/log_correlation/stackdriver/build.gradle @@ -0,0 +1,10 @@ +description = 'OpenCensus Stackdriver Log Correlation' + +apply plugin: 'java' + +dependencies { + compile project(':opencensus-api'), + libraries.google_cloud_logging + + signature "org.codehaus.mojo.signature:java16:+@signature" +} diff --git a/contrib/log_correlation/stackdriver/src/main/java/io/opencensus/contrib/logcorrelation/stackdriver/OpenCensusTraceLoggingEnhancer.java b/contrib/log_correlation/stackdriver/src/main/java/io/opencensus/contrib/logcorrelation/stackdriver/OpenCensusTraceLoggingEnhancer.java new file mode 100644 index 00000000..a2ce3110 --- /dev/null +++ b/contrib/log_correlation/stackdriver/src/main/java/io/opencensus/contrib/logcorrelation/stackdriver/OpenCensusTraceLoggingEnhancer.java @@ -0,0 +1,54 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.logcorrelation.stackdriver; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.logging.LogEntry; +import com.google.cloud.logging.LoggingEnhancer; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.TraceId; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.Tracing; + +/** Stackdriver {@link LoggingEnhancer} that adds OpenCensus tracing data to log entries. */ +@ExperimentalApi +public final class OpenCensusTraceLoggingEnhancer implements LoggingEnhancer { + private static final String SPAN_ID_KEY = "span_id"; + private static final String SAMPLED_KEY = "sampled"; + + private static final Tracer tracer = Tracing.getTracer(); + + /** Constructor to be called by Stackdriver logging. */ + public OpenCensusTraceLoggingEnhancer() {} + + @Override + public void enhanceLogEntry(LogEntry.Builder builder) { + SpanContext span = tracer.getCurrentSpan().getContext(); + builder.setTrace(formatTraceId(span.getTraceId())); + + // TODO(sebright): Find the correct way to add span ID and sampling decision. + builder.addLabel(SPAN_ID_KEY, span.getSpanId().toLowerBase16()); + builder.addLabel(SAMPLED_KEY, Boolean.toString(span.getTraceOptions().isSampled())); + } + + private static String formatTraceId(TraceId traceId) { + // TODO(sebright): Should we cache the project ID, for efficiency? + String projectId = ServiceOptions.getDefaultProjectId(); + return "projects/" + projectId + "/traces/" + traceId.toLowerBase16(); + } +} diff --git a/contrib/log_correlation/stackdriver/src/test/java/io/opencensus/contrib/logcorrelation/stackdriver/OpenCensusTraceLoggingEnhancerTest.java b/contrib/log_correlation/stackdriver/src/test/java/io/opencensus/contrib/logcorrelation/stackdriver/OpenCensusTraceLoggingEnhancerTest.java new file mode 100644 index 00000000..7f399bb1 --- /dev/null +++ b/contrib/log_correlation/stackdriver/src/test/java/io/opencensus/contrib/logcorrelation/stackdriver/OpenCensusTraceLoggingEnhancerTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.logcorrelation.stackdriver; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.logging.LogEntry; +import io.opencensus.common.Scope; +import io.opencensus.trace.Annotation; +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.BlankSpan; +import io.opencensus.trace.EndSpanOptions; +import io.opencensus.trace.Link; +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.SpanId; +import io.opencensus.trace.TraceId; +import io.opencensus.trace.TraceOptions; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.Tracing; +import java.util.EnumSet; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link OpenCensusTraceLoggingEnhancer}. */ +// TODO(sebright): Find a way to test that OpenCensusTraceLoggingEnhancer is called from Stackdriver +// logging. See +// https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/TESTING.md#testing-code-that-uses-logging. +@RunWith(JUnit4.class) +public class OpenCensusTraceLoggingEnhancerTest { + private static final String GOOGLE_CLOUD_PROJECT = "GOOGLE_CLOUD_PROJECT"; + + private static Tracer tracer = Tracing.getTracer(); + + @Test + public void enhanceLogEntry_Sampled() { + String projectId = "my-test-project-1"; + String traceId = "4c9874d0b41224cce77ff74ee10f5ee6"; + String spanId = "592ae363e92cb3dd"; + boolean isSampled = true; + testLoggingEnhancer(projectId, traceId, spanId, isSampled); + } + + @Test + public void enhanceLogEntry_NotSampled() { + String projectId = "my-test-project-2"; + String traceId = "7f4703d9bb02f4f2e67fb840103cdd34"; + String spanId = "2d7d95a555557434"; + boolean isSampled = false; + testLoggingEnhancer(projectId, traceId, spanId, isSampled); + } + + private static void testLoggingEnhancer( + String projectId, String traceId, String spanId, boolean isSampled) { + System.setProperty(GOOGLE_CLOUD_PROJECT, projectId); + try { + Scope scope = + tracer.withSpan( + new TestSpan( + SpanContext.create( + TraceId.fromLowerBase16(traceId), + SpanId.fromLowerBase16(spanId), + TraceOptions.builder().setIsSampled(isSampled).build()))); + try { + LogEntry.Builder builder = LogEntry.newBuilder(null); + new OpenCensusTraceLoggingEnhancer().enhanceLogEntry(builder); + LogEntry logEntry = builder.build(); + assertThat(logEntry.getLabels().get("span_id")).isEqualTo(spanId); + assertThat(logEntry.getLabels().get("sampled")).isEqualTo(isSampled ? "true" : "false"); + assertThat(logEntry.getTrace()).isEqualTo("projects/" + projectId + "/traces/" + traceId); + } finally { + scope.close(); + } + } finally { + System.clearProperty(GOOGLE_CLOUD_PROJECT); + } + } + + // TODO(sebright): Should the OpenCensusTraceLoggingEnhancer avoid adding tracing data when the + // span is blank? + @Test + public void enhanceLogEntry_BlankSpan() { + System.setProperty(GOOGLE_CLOUD_PROJECT, "my-test-project-3"); + try { + Scope scope = tracer.withSpan(BlankSpan.INSTANCE); + try { + LogEntry.Builder builder = LogEntry.newBuilder(null); + new OpenCensusTraceLoggingEnhancer().enhanceLogEntry(builder); + LogEntry logEntry = builder.build(); + assertThat(logEntry.getLabels().get("span_id")).isEqualTo("0000000000000000"); + assertThat(logEntry.getLabels().get("sampled")).isEqualTo("false"); + assertThat(logEntry.getTrace()) + .isEqualTo("projects/my-test-project-3/traces/00000000000000000000000000000000"); + } finally { + scope.close(); + } + } finally { + System.clearProperty(GOOGLE_CLOUD_PROJECT); + } + } + + private static final class TestSpan extends Span { + TestSpan(SpanContext context) { + super(context, EnumSet.of(Options.RECORD_EVENTS)); + } + + @Override + public void end(EndSpanOptions options) {} + + @Override + public void addLink(Link link) {} + + @Override + public void addAnnotation(Annotation annotation) {} + + @Override + public void addAnnotation(String description, Map<String, AttributeValue> attributes) {} + } +} diff --git a/contrib/log_correlation/stackdriver_demo/README.md b/contrib/log_correlation/stackdriver_demo/README.md new file mode 100644 index 00000000..ba922e0c --- /dev/null +++ b/contrib/log_correlation/stackdriver_demo/README.md @@ -0,0 +1,9 @@ +# OpenCensus Stackdriver Log Correlation Demo + +An application that demonstrates log correlation in Stackdriver, using +`opencensus-contrib-log-correlation-stackdriver`. The application contains SLF4J log statements and +OpenCensus tracing instrumentation. It configures logging with a Logback XML configuration and +exports logs using the +[Stackdriver Logging Logback appender](https://cloud.google.com/logging/docs/setup/java#logback_appender). +It also exports traces using `opencensus-exporter-trace-stackdriver`, so that Stackdriver can show +the log entries associated with each trace. diff --git a/contrib/log_correlation/stackdriver_demo/build.gradle b/contrib/log_correlation/stackdriver_demo/build.gradle new file mode 100644 index 00000000..ae6c9279 --- /dev/null +++ b/contrib/log_correlation/stackdriver_demo/build.gradle @@ -0,0 +1,36 @@ +description = 'OpenCensus Stackdriver Log Correlation Demo' + +apply plugin: 'java' + +[compileJava, compileTestJava].each() { + it.sourceCompatibility = 1.7 + it.targetCompatibility = 1.7 +} + +dependencies { + compile project(':opencensus-api'), + project(':opencensus-contrib-log-correlation-stackdriver'), + project(':opencensus-exporter-trace-stackdriver'), + libraries.slf4j, + libraries.google_cloud_logging_logback + + runtime project(':opencensus-impl-lite') + + signature "org.codehaus.mojo.signature:java18:+@signature" +} + +apply plugin: 'application' + +startScripts.enabled = false + +task openCensusLogCorrelationStackdriverDemo(type: CreateStartScripts) { + mainClassName = 'io.opencensus.contrib.logcorrelation.stackdriver.demo.OpenCensusTraceLoggingEnhancerDemo' + applicationName = 'OpenCensusTraceLoggingEnhancerDemo' + outputDir = new File(project.buildDir, 'tmp') + classpath = jar.outputs.files + project.configurations.runtime +} + +applicationDistribution.into('bin') { + from(openCensusLogCorrelationStackdriverDemo) + fileMode = 0755 +} diff --git a/contrib/log_correlation/stackdriver_demo/src/main/java/io/opencensus/contrib/logcorrelation/stackdriver/demo/OpenCensusTraceLoggingEnhancerDemo.java b/contrib/log_correlation/stackdriver_demo/src/main/java/io/opencensus/contrib/logcorrelation/stackdriver/demo/OpenCensusTraceLoggingEnhancerDemo.java new file mode 100644 index 00000000..3b7bef5f --- /dev/null +++ b/contrib/log_correlation/stackdriver_demo/src/main/java/io/opencensus/contrib/logcorrelation/stackdriver/demo/OpenCensusTraceLoggingEnhancerDemo.java @@ -0,0 +1,77 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.logcorrelation.stackdriver.demo; + +import io.opencensus.common.Scope; +import io.opencensus.contrib.logcorrelation.stackdriver.OpenCensusTraceLoggingEnhancer; +import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration; +import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter; +import io.opencensus.trace.Sampler; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.Tracing; +import io.opencensus.trace.samplers.Samplers; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Demo for {@link OpenCensusTraceLoggingEnhancer}. */ +public final class OpenCensusTraceLoggingEnhancerDemo { + + // Use a high sampling probability to demonstrate tracing. + private static final Sampler SAMPLER = Samplers.probabilitySampler(0.7); + + private static final Logger logger = + LoggerFactory.getLogger(OpenCensusTraceLoggingEnhancerDemo.class.getName()); + + private static final Tracer tracer = Tracing.getTracer(); + + private OpenCensusTraceLoggingEnhancerDemo() {} + + /** Runs the demo. */ + public static void main(String[] args) throws IOException { + StackdriverTraceExporter.createAndRegister(StackdriverTraceConfiguration.builder().build()); + try (Scope scope = tracer.spanBuilder("Demo.Main").setSampler(SAMPLER).startScopedSpan()) { + pause(); + logger.warn("parent span log message 1"); + pause(); + doWork(); + pause(); + logger.info("parent span log message 2"); + pause(); + } + Tracing.getExportComponent().shutdown(); + } + + private static void doWork() { + try (Scope scope = tracer.spanBuilder("Demo.DoWork").startScopedSpan()) { + pause(); + logger.info("child span log message 1"); + pause(); + logger.error("child span log message 2"); + pause(); + } + } + + /** Sleeps for 500 ms to spread out the events on the trace. */ + private static void pause() { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + } +} diff --git a/contrib/log_correlation/stackdriver_demo/src/main/resources/logback.xml b/contrib/log_correlation/stackdriver_demo/src/main/resources/logback.xml new file mode 100644 index 00000000..3c16ed8a --- /dev/null +++ b/contrib/log_correlation/stackdriver_demo/src/main/resources/logback.xml @@ -0,0 +1,16 @@ +<!-- This configuration file is based on the example at + https://github.com/GoogleCloudPlatform/google-cloud-java/tree/master/google-cloud-clients/google-cloud-contrib/google-cloud-logging-logback#usage --> +<configuration> + <appender name="CLOUD" class="com.google.cloud.logging.logback.LoggingAppender"> + <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> + <level>INFO</level> + </filter> + <log>application.log</log> + <enhancer>io.opencensus.contrib.logcorrelation.stackdriver.OpenCensusTraceLoggingEnhancer</enhancer> + <flushLevel>WARN</flushLevel> + </appender> + + <root level="info"> + <appender-ref ref="CLOUD" /> + </root> +</configuration> |