1 /* 2 * Copyright 2018, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.contrib.logcorrelation.log4j2; 18 19 import io.opencensus.common.ExperimentalApi; 20 import java.util.List; 21 import javax.annotation.Nullable; 22 import org.apache.logging.log4j.core.ContextDataInjector; 23 import org.apache.logging.log4j.core.Layout; 24 import org.apache.logging.log4j.core.LogEvent; 25 import org.apache.logging.log4j.core.config.Property; 26 import org.apache.logging.log4j.util.ReadOnlyStringMap; 27 import org.apache.logging.log4j.util.StringMap; 28 29 /** 30 * A Log4j {@link ContextDataInjector} that adds OpenCensus tracing data to log events. 31 * 32 * <p>This class adds the following key-value pairs: 33 * 34 * <ul> 35 * <li>{@value #TRACE_ID_CONTEXT_KEY} - the lowercase base16 encoding of the current trace ID 36 * <li>{@value #SPAN_ID_CONTEXT_KEY} - the lowercase base16 encoding of the current span ID 37 * <li>{@value #TRACE_SAMPLED_CONTEXT_KEY} - the sampling decision of the current span ({@code 38 * "true"} or {@code "false"}) 39 * </ul> 40 * 41 * <p>The tracing data can be accessed with {@link LogEvent#getContextData} or included in a {@link 42 * Layout}. For example, the following patterns could be used to include the tracing data with a <a 43 * href="https://logging.apache.org/log4j/2.x/manual/layouts.html#Pattern_Layout">Pattern 44 * Layout</a>: 45 * 46 * <ul> 47 * <li><code>%X{opencensusTraceId}</code> 48 * <li><code>%X{opencensusSpanId}</code> 49 * <li><code>%X{opencensusTraceSampled}</code> 50 * </ul> 51 * 52 * <p>This feature is currently experimental. 53 * 54 * @since 0.16 55 * @see <a 56 * href="https://logging.apache.org/log4j/2.x/log4j-core/apidocs/org/apache/logging/log4j/core/ContextDataInjector.html">org.apache.logging.log4j.core.ContextDataInjector</a> 57 */ 58 @ExperimentalApi 59 public final class OpenCensusTraceContextDataInjector implements ContextDataInjector { 60 private static final SpanSelection DEFAULT_SPAN_SELECTION = SpanSelection.ALL_SPANS; 61 62 /** 63 * Context key for the current trace ID. The name is {@value}. 64 * 65 * @since 0.16 66 */ 67 public static final String TRACE_ID_CONTEXT_KEY = "opencensusTraceId"; 68 69 /** 70 * Context key for the current span ID. The name is {@value}. 71 * 72 * @since 0.16 73 */ 74 public static final String SPAN_ID_CONTEXT_KEY = "opencensusSpanId"; 75 76 /** 77 * Context key for the sampling decision of the current span. The name is {@value}. 78 * 79 * @since 0.16 80 */ 81 public static final String TRACE_SAMPLED_CONTEXT_KEY = "opencensusTraceSampled"; 82 83 /** 84 * Name of the property that defines the {@link SpanSelection}. The name is {@value}. 85 * 86 * @since 0.16 87 */ 88 public static final String SPAN_SELECTION_PROPERTY_NAME = 89 "io.opencensus.contrib.logcorrelation.log4j2." 90 + "OpenCensusTraceContextDataInjector.spanSelection"; 91 92 private final SpanSelection spanSelection; 93 94 /** 95 * How to decide whether to add tracing data from the current span to a log entry. 96 * 97 * @since 0.16 98 */ 99 public enum SpanSelection { 100 101 /** 102 * Never add tracing data to log entries. This constant disables the log correlation feature. 103 * 104 * @since 0.16 105 */ 106 NO_SPANS, 107 108 /** 109 * Add tracing data to a log entry iff the current span is sampled. 110 * 111 * @since 0.16 112 */ 113 SAMPLED_SPANS, 114 115 /** 116 * Always add tracing data to log entries, even when the current span is not sampled. This is 117 * the default. 118 * 119 * @since 0.16 120 */ 121 ALL_SPANS 122 } 123 124 /** 125 * Returns the {@code SpanSelection} setting for this instance. 126 * 127 * @return the {@code SpanSelection} setting for this instance. 128 * @since 0.16 129 */ getSpanSelection()130 public SpanSelection getSpanSelection() { 131 return spanSelection; 132 } 133 134 /** 135 * Constructor to be called by Log4j. 136 * 137 * <p>This constructor looks up the {@link SpanSelection} using the system property {@link 138 * #SPAN_SELECTION_PROPERTY_NAME}. 139 * 140 * @since 0.16 141 */ OpenCensusTraceContextDataInjector()142 public OpenCensusTraceContextDataInjector() { 143 this(lookUpSpanSelectionProperty()); 144 } 145 146 // visible for testing OpenCensusTraceContextDataInjector(SpanSelection spanSelection)147 OpenCensusTraceContextDataInjector(SpanSelection spanSelection) { 148 this.spanSelection = spanSelection; 149 } 150 lookUpSpanSelectionProperty()151 private static SpanSelection lookUpSpanSelectionProperty() { 152 String spanSelectionProperty = System.getProperty(SPAN_SELECTION_PROPERTY_NAME); 153 return spanSelectionProperty == null || spanSelectionProperty.isEmpty() 154 ? DEFAULT_SPAN_SELECTION 155 : parseSpanSelection(spanSelectionProperty); 156 } 157 parseSpanSelection(String spanSelection)158 private static SpanSelection parseSpanSelection(String spanSelection) { 159 try { 160 return SpanSelection.valueOf(spanSelection); 161 } catch (IllegalArgumentException e) { 162 return DEFAULT_SPAN_SELECTION; 163 } 164 } 165 166 // Note that this method must return an object that can be passed to another thread. 167 @Override injectContextData(@ullable List<Property> properties, StringMap reusable)168 public StringMap injectContextData(@Nullable List<Property> properties, StringMap reusable) { 169 return ContextDataUtils.injectContextData(spanSelection, properties, reusable); 170 } 171 172 // Note that this method does not need to return an object that can be passed to another thread. 173 @Override rawContextData()174 public ReadOnlyStringMap rawContextData() { 175 return ContextDataUtils.nonShareableRawContextData(spanSelection); 176 } 177 } 178