Skip to content

Commit afb77f5

Browse files
committed
Set exception logged only when it really is emitted
1 parent b4345a3 commit afb77f5

File tree

5 files changed

+209
-177
lines changed

5 files changed

+209
-177
lines changed

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/exporter/AgentLogExporter.java

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,16 @@
1010
import com.azure.monitor.opentelemetry.autoconfigure.implementation.logging.OperationLogger;
1111
import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.TelemetryItem;
1212
import com.azure.monitor.opentelemetry.autoconfigure.implementation.quickpulse.QuickPulse;
13-
import com.microsoft.applicationinsights.agent.internal.configuration.Configuration.SamplingOverride;
14-
import com.microsoft.applicationinsights.agent.internal.sampling.AiFixedPercentageSampler;
15-
import com.microsoft.applicationinsights.agent.internal.sampling.SamplingOverrides;
1613
import com.microsoft.applicationinsights.agent.internal.telemetry.BatchItemProcessor;
1714
import com.microsoft.applicationinsights.agent.internal.telemetry.TelemetryClient;
1815
import com.microsoft.applicationinsights.agent.internal.telemetry.TelemetryObservers;
1916
import io.opentelemetry.api.logs.LoggerProvider;
20-
import io.opentelemetry.api.trace.SpanContext;
2117
import io.opentelemetry.javaagent.bootstrap.CallDepth;
2218
import io.opentelemetry.sdk.common.CompletableResultCode;
2319
import io.opentelemetry.sdk.logs.data.LogRecordData;
2420
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
25-
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
26-
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
2721
import io.opentelemetry.semconv.ExceptionAttributes;
2822
import java.util.Collection;
29-
import java.util.List;
3023
import java.util.function.Consumer;
3124
import javax.annotation.Nullable;
3225
import org.slf4j.Logger;
@@ -39,24 +32,13 @@ public class AgentLogExporter implements LogRecordExporter {
3932
private static final OperationLogger exportingLogLogger =
4033
new OperationLogger(AgentLogExporter.class, "Exporting log");
4134

42-
// TODO (trask) could implement this in a filtering LogExporter instead
43-
private volatile int severityThreshold;
44-
45-
private final SamplingOverrides logSamplingOverrides;
46-
private final SamplingOverrides exceptionSamplingOverrides;
4735
private final LogDataMapper mapper;
4836
private final Consumer<TelemetryItem> telemetryItemConsumer;
4937

5038
public AgentLogExporter(
51-
int severityThreshold,
52-
List<SamplingOverride> logSamplingOverrides,
53-
List<SamplingOverride> exceptionSamplingOverrides,
5439
LogDataMapper mapper,
5540
@Nullable QuickPulse quickPulse,
5641
BatchItemProcessor batchItemProcessor) {
57-
this.severityThreshold = severityThreshold;
58-
this.logSamplingOverrides = new SamplingOverrides(logSamplingOverrides);
59-
this.exceptionSamplingOverrides = new SamplingOverrides(exceptionSamplingOverrides);
6042
this.mapper = mapper;
6143
telemetryItemConsumer =
6244
telemetryItem -> {
@@ -70,10 +52,6 @@ public AgentLogExporter(
7052
};
7153
}
7254

73-
public void setSeverityThreshold(int severityThreshold) {
74-
this.severityThreshold = severityThreshold;
75-
}
76-
7755
@Override
7856
public CompletableResultCode export(Collection<LogRecordData> logs) {
7957
// incrementing CallDepth for LoggerProvider causes the OpenTelemetry Java agent logging
@@ -107,47 +85,12 @@ private CompletableResultCode internalExport(Collection<LogRecordData> logs) {
10785

10886
private void internalExport(LogRecordData log) {
10987
try {
110-
int severityNumber = log.getSeverity().getSeverityNumber();
111-
if (severityNumber < severityThreshold) {
112-
return;
113-
}
88+
logger.debug("exporting log: {}", log);
11489

11590
String stack = log.getAttributes().get(ExceptionAttributes.EXCEPTION_STACKTRACE);
91+
Double sampleRate = log.getAttributes().get(AiSemanticAttributes.SAMPLE_RATE);
11692

117-
SamplingOverrides samplingOverrides =
118-
stack != null ? exceptionSamplingOverrides : logSamplingOverrides;
119-
120-
SpanContext spanContext = log.getSpanContext();
121-
Double parentSpanSampleRate = log.getAttributes().get(AiSemanticAttributes.SAMPLE_RATE);
122-
123-
AiFixedPercentageSampler sampler = samplingOverrides.getOverride(log.getAttributes());
124-
125-
boolean hasSamplingOverride = sampler != null;
126-
127-
if (!hasSamplingOverride
128-
&& spanContext.isValid()
129-
&& !spanContext.getTraceFlags().isSampled()) {
130-
// if there is no sampling override, and the log is part of an unsampled trace,
131-
// then don't capture it
132-
return;
133-
}
134-
135-
Double sampleRate = null;
136-
if (hasSamplingOverride) {
137-
SamplingResult samplingResult = sampler.shouldSampleLog(spanContext, parentSpanSampleRate);
138-
if (samplingResult.getDecision() != SamplingDecision.RECORD_AND_SAMPLE) {
139-
return;
140-
}
141-
sampleRate = samplingResult.getAttributes().get(AiSemanticAttributes.SAMPLE_RATE);
142-
}
143-
144-
if (sampleRate == null) {
145-
sampleRate = parentSpanSampleRate;
146-
}
147-
148-
logger.debug("exporting log: {}", log);
149-
150-
// TODO (trask) no longer need to check AiSemanticAttributes.SAMPLE_RATE in map() method
93+
// TODO (trask) get stack and sampleRate inside map() method instead of passing into
15194
TelemetryItem telemetryItem = mapper.map(log, stack, sampleRate);
15295
telemetryItemConsumer.accept(telemetryItem);
15396

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package com.microsoft.applicationinsights.agent.internal.init;
2+
3+
import com.azure.core.util.logging.ClientLogger;
4+
import com.azure.monitor.opentelemetry.autoconfigure.implementation.AiSemanticAttributes;
5+
import com.microsoft.applicationinsights.agent.internal.configuration.Configuration;
6+
import com.microsoft.applicationinsights.agent.internal.sampling.AiFixedPercentageSampler;
7+
import com.microsoft.applicationinsights.agent.internal.sampling.SamplingOverrides;
8+
import io.opentelemetry.api.trace.Span;
9+
import io.opentelemetry.api.trace.SpanContext;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
12+
import io.opentelemetry.sdk.internal.AttributesMap;
13+
import io.opentelemetry.sdk.logs.LogRecordProcessor;
14+
import io.opentelemetry.sdk.logs.ReadWriteLogRecord;
15+
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
16+
import io.opentelemetry.sdk.trace.ReadableSpan;
17+
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
18+
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
19+
import io.opentelemetry.semconv.ExceptionAttributes;
20+
import java.lang.reflect.Field;
21+
import java.util.List;
22+
import javax.annotation.Nullable;
23+
24+
public class AzureMonitorLogFilteringProcessor implements LogRecordProcessor {
25+
26+
private static final ClientLogger logger = new ClientLogger(AzureMonitorLogProcessor.class);
27+
private static final Field lockField;
28+
private static final Field attributesMapField;
29+
30+
static {
31+
Class<?> sdkReadWriteLogRecordClass = getSdkReadWriteLogRecordClass();
32+
lockField = getLockField(sdkReadWriteLogRecordClass);
33+
attributesMapField = getAttributesMapField(sdkReadWriteLogRecordClass);
34+
}
35+
36+
private final SamplingOverrides logSamplingOverrides;
37+
private final SamplingOverrides exceptionSamplingOverrides;
38+
private final BatchLogRecordProcessor batchLogRecordProcessor;
39+
40+
private volatile int severityThreshold;
41+
42+
public AzureMonitorLogFilteringProcessor(
43+
List<Configuration.SamplingOverride> logSamplingOverrides,
44+
List<Configuration.SamplingOverride> exceptionSamplingOverrides,
45+
BatchLogRecordProcessor batchLogRecordProcessor,
46+
int severityThreshold) {
47+
48+
this.severityThreshold = severityThreshold;
49+
this.logSamplingOverrides = new SamplingOverrides(logSamplingOverrides);
50+
this.exceptionSamplingOverrides = new SamplingOverrides(exceptionSamplingOverrides);
51+
this.batchLogRecordProcessor = batchLogRecordProcessor;
52+
this.severityThreshold = severityThreshold;
53+
}
54+
55+
public void setSeverityThreshold(int severityThreshold) {
56+
this.severityThreshold = severityThreshold;
57+
}
58+
59+
@Override
60+
public void onEmit(Context context, ReadWriteLogRecord logRecord) {
61+
62+
int severityNumber = logRecord.getSeverity().getSeverityNumber();
63+
if (severityNumber < severityThreshold) {
64+
// quick return
65+
return;
66+
}
67+
68+
Double parentSpanSampleRate = null;
69+
Span currentSpan = Span.fromContext(context);
70+
if (currentSpan instanceof ReadableSpan) {
71+
ReadableSpan readableSpan = (ReadableSpan) currentSpan;
72+
parentSpanSampleRate = readableSpan.getAttribute(AiSemanticAttributes.SAMPLE_RATE);
73+
}
74+
75+
// deal with sampling synchronously so that we only call setAttributeExceptionLogged()
76+
// when we know we are emitting the exception (span sampling happens synchronously as well)
77+
78+
String stack = logRecord.getAttribute(ExceptionAttributes.EXCEPTION_STACKTRACE);
79+
80+
SamplingOverrides samplingOverrides =
81+
stack != null ? exceptionSamplingOverrides : logSamplingOverrides;
82+
83+
SpanContext spanContext = logRecord.getSpanContext();
84+
85+
AiFixedPercentageSampler sampler = samplingOverrides.getOverride(logRecord.getAttributes());
86+
87+
boolean hasSamplingOverride = sampler != null;
88+
89+
if (!hasSamplingOverride && spanContext.isValid() && !spanContext.getTraceFlags().isSampled()) {
90+
// if there is no sampling override, and the log is part of an unsampled trace,
91+
// then don't capture it
92+
return;
93+
}
94+
95+
Double sampleRate = null;
96+
if (hasSamplingOverride) {
97+
SamplingResult samplingResult = sampler.shouldSampleLog(spanContext, parentSpanSampleRate);
98+
if (samplingResult.getDecision() != SamplingDecision.RECORD_AND_SAMPLE) {
99+
return;
100+
}
101+
sampleRate = samplingResult.getAttributes().get(AiSemanticAttributes.SAMPLE_RATE);
102+
}
103+
104+
if (sampleRate == null) {
105+
sampleRate = parentSpanSampleRate;
106+
}
107+
108+
if (sampleRate != null) {
109+
logRecord.setAttribute(AiSemanticAttributes.SAMPLE_RATE, sampleRate);
110+
}
111+
112+
setAttributeExceptionLogged(LocalRootSpan.fromContext(context), logRecord);
113+
114+
batchLogRecordProcessor.onEmit(context, logRecord);
115+
}
116+
117+
@Nullable
118+
private static Class<?> getSdkReadWriteLogRecordClass() {
119+
try {
120+
return Class.forName("io.opentelemetry.sdk.logs.SdkReadWriteLogRecord");
121+
} catch (ClassNotFoundException e) {
122+
return null;
123+
}
124+
}
125+
126+
@Nullable
127+
private static Field getLockField(Class<?> sdkReadWriteLogRecordClass) {
128+
if (sdkReadWriteLogRecordClass == null) {
129+
return null;
130+
}
131+
try {
132+
Field lockField = sdkReadWriteLogRecordClass.getDeclaredField("lock");
133+
lockField.setAccessible(true);
134+
return lockField;
135+
} catch (NoSuchFieldException e) {
136+
return null;
137+
}
138+
}
139+
140+
@Nullable
141+
private static Field getAttributesMapField(Class<?> sdkReadWriteLogRecordClass) {
142+
if (sdkReadWriteLogRecordClass == null) {
143+
return null;
144+
}
145+
try {
146+
Field attributesMapField = sdkReadWriteLogRecordClass.getDeclaredField("attributes");
147+
attributesMapField.setAccessible(true);
148+
return attributesMapField;
149+
} catch (NoSuchFieldException e) {
150+
return null;
151+
}
152+
}
153+
154+
private static void setAttributeExceptionLogged(Span span, ReadWriteLogRecord logRecord) {
155+
if (lockField == null || attributesMapField == null) {
156+
return;
157+
}
158+
String stacktrace = null;
159+
try {
160+
synchronized (lockField) {
161+
// TODO add `getAttribute()` to `ReadWriteLogRecord` upstream
162+
stacktrace =
163+
((AttributesMap) attributesMapField.get(logRecord))
164+
.get(ExceptionAttributes.EXCEPTION_STACKTRACE);
165+
}
166+
} catch (Exception e) {
167+
logger.error(e.getMessage(), e);
168+
}
169+
if (stacktrace != null) {
170+
span.setAttribute(AiSemanticAttributes.LOGGED_EXCEPTION, stacktrace);
171+
}
172+
}
173+
}

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AzureMonitorLogProcessor.java

Lines changed: 4 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -3,103 +3,23 @@
33

44
package com.microsoft.applicationinsights.agent.internal.init;
55

6-
import com.azure.core.util.logging.ClientLogger;
76
import com.azure.monitor.opentelemetry.autoconfigure.implementation.AiSemanticAttributes;
87
import com.azure.monitor.opentelemetry.autoconfigure.implementation.OperationNames;
98
import io.opentelemetry.api.trace.Span;
109
import io.opentelemetry.context.Context;
11-
import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
12-
import io.opentelemetry.sdk.internal.AttributesMap;
1310
import io.opentelemetry.sdk.logs.LogRecordProcessor;
1411
import io.opentelemetry.sdk.logs.ReadWriteLogRecord;
1512
import io.opentelemetry.sdk.trace.ReadableSpan;
16-
import io.opentelemetry.semconv.ExceptionAttributes;
17-
import java.lang.reflect.Field;
18-
import javax.annotation.Nullable;
1913

2014
public class AzureMonitorLogProcessor implements LogRecordProcessor {
2115

22-
private static final ClientLogger logger = new ClientLogger(AzureMonitorLogProcessor.class);
23-
private static final Field lockField;
24-
private static final Field attributesMapField;
25-
26-
static {
27-
Class<?> sdkReadWriteLogRecordClass = getSdkReadWriteLogRecordClass();
28-
lockField = getLockField(sdkReadWriteLogRecordClass);
29-
attributesMapField = getAttributesMapField(sdkReadWriteLogRecordClass);
30-
}
31-
3216
@Override
3317
public void onEmit(Context context, ReadWriteLogRecord logRecord) {
3418
Span currentSpan = Span.fromContext(context);
35-
if (!(currentSpan instanceof ReadableSpan)) {
36-
return;
37-
}
38-
setAttributeExceptionLogged(LocalRootSpan.fromContext(context), logRecord);
39-
40-
ReadableSpan readableSpan = (ReadableSpan) currentSpan;
41-
logRecord.setAttribute(
42-
AiSemanticAttributes.OPERATION_NAME, OperationNames.getOperationName(readableSpan));
43-
Double sampleRate = readableSpan.getAttribute(AiSemanticAttributes.SAMPLE_RATE);
44-
if (sampleRate != null) {
45-
logRecord.setAttribute(AiSemanticAttributes.SAMPLE_RATE, sampleRate);
46-
}
47-
}
48-
49-
@Nullable
50-
private static Class<?> getSdkReadWriteLogRecordClass() {
51-
try {
52-
return Class.forName("io.opentelemetry.sdk.logs.SdkReadWriteLogRecord");
53-
} catch (ClassNotFoundException e) {
54-
return null;
55-
}
56-
}
57-
58-
@Nullable
59-
private static Field getLockField(Class<?> sdkReadWriteLogRecordClass) {
60-
if (sdkReadWriteLogRecordClass == null) {
61-
return null;
62-
}
63-
try {
64-
Field lockField = sdkReadWriteLogRecordClass.getDeclaredField("lock");
65-
lockField.setAccessible(true);
66-
return lockField;
67-
} catch (NoSuchFieldException e) {
68-
return null;
69-
}
70-
}
71-
72-
@Nullable
73-
private static Field getAttributesMapField(Class<?> sdkReadWriteLogRecordClass) {
74-
if (sdkReadWriteLogRecordClass == null) {
75-
return null;
76-
}
77-
try {
78-
Field attributesMapField = sdkReadWriteLogRecordClass.getDeclaredField("attributes");
79-
attributesMapField.setAccessible(true);
80-
return attributesMapField;
81-
} catch (NoSuchFieldException e) {
82-
return null;
83-
}
84-
}
85-
86-
private static void setAttributeExceptionLogged(Span span, ReadWriteLogRecord logRecord) {
87-
if (lockField == null || attributesMapField == null) {
88-
return;
89-
}
90-
String stacktrace = null;
91-
try {
92-
synchronized (lockField) {
93-
// TODO add `getAttribute()` to `ReadWriteLogRecord` upstream
94-
stacktrace =
95-
((AttributesMap) attributesMapField.get(logRecord))
96-
.get(ExceptionAttributes.EXCEPTION_STACKTRACE);
97-
}
98-
} catch (Exception e) {
99-
logger.error(e.getMessage(), e);
100-
}
101-
if (stacktrace != null) {
102-
span.setAttribute(AiSemanticAttributes.LOGGED_EXCEPTION, stacktrace);
19+
if (currentSpan instanceof ReadableSpan) {
20+
ReadableSpan readableSpan = (ReadableSpan) currentSpan;
21+
logRecord.setAttribute(
22+
AiSemanticAttributes.OPERATION_NAME, OperationNames.getOperationName(readableSpan));
10323
}
10424
}
10525
}

0 commit comments

Comments
 (0)