Skip to content

Commit c668050

Browse files
committed
NH-119445: export profiles as span event implementation
1 parent 9c2897a commit c668050

File tree

7 files changed

+538
-5
lines changed

7 files changed

+538
-5
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
plugins{
17+
plugins {
1818
id("io.github.gradle-nexus.publish-plugin") version "2.0.0"
1919
}
2020

21-
val swoAgentVersion = "3.0.3"
21+
val swoAgentVersion = "3.0.3-profile"
2222
extra["swoAgentVersion"] = swoAgentVersion
2323
group = "com.solarwinds"
2424
version = if (System.getenv("SNAPSHOT_BUILD").toBoolean()) "$swoAgentVersion-SNAPSHOT" else swoAgentVersion

custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/InboundMeasurementMetricsGenerator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ public void onEnd(ReadableSpan span) {
100100
responseTimeAttr.put(errorKey, hasError);
101101
responseTime.record(
102102
duration, responseTimeAttr.put(TRANSACTION_NAME_KEY, transactionName).build());
103+
logger.debug(
104+
String.format(
105+
"Inbound measurement metrics generated with context: %s", Context.current()));
103106
}
104107
}
105108

custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsProfilingSpanProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.solarwinds.joboe.sampling.Metadata;
2828
import com.solarwinds.joboe.shaded.javax.annotation.Nonnull;
2929
import com.solarwinds.opentelemetry.core.Util;
30+
import com.solarwinds.opentelemetry.extensions.profile.SampleEmitter;
3031
import io.opentelemetry.api.trace.Span;
3132
import io.opentelemetry.api.trace.SpanContext;
3233
import io.opentelemetry.context.Context;
@@ -49,7 +50,10 @@ public void onStart(@Nonnull Context parentContext, ReadWriteSpan span) {
4950
Metadata metadata = Util.buildMetadata(spanContext);
5051
if (metadata.isValid()) {
5152
Profiler.addProfiledThread(
52-
Thread.currentThread(), metadata, Metadata.bytesToHex(metadata.getTaskID()));
53+
Thread.currentThread(),
54+
metadata,
55+
Metadata.bytesToHex(metadata.getTaskID()),
56+
new SampleEmitter(span));
5357
}
5458
} else {
5559
span.setAttribute(SW_KEY_PREFIX + "profile.spans", -1); // profiler disabled
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* © SolarWinds Worldwide, LLC. All rights reserved.
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 com.solarwinds.opentelemetry.extensions.profile;
18+
19+
import com.solarwinds.joboe.core.profiler.ProfileSampleEmitter;
20+
import com.solarwinds.joboe.logging.LoggerFactory;
21+
import com.solarwinds.joboe.shaded.google.gson.Gson;
22+
import com.solarwinds.joboe.shaded.google.gson.GsonBuilder;
23+
import io.opentelemetry.api.common.AttributeKey;
24+
import io.opentelemetry.api.common.Attributes;
25+
import io.opentelemetry.api.common.AttributesBuilder;
26+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
27+
import java.util.List;
28+
import java.util.Map;
29+
30+
public class SampleEmitter implements ProfileSampleEmitter {
31+
private static final Gson gson = new GsonBuilder().create();
32+
33+
private AttributesBuilder entryAttributesBuilder;
34+
35+
private AttributesBuilder exitAttributesBuilder;
36+
37+
private AttributesBuilder sampleAttributesBuilder;
38+
39+
private ReadWriteSpan readWriteSpan;
40+
41+
public SampleEmitter(ReadWriteSpan readWriteSpan) {
42+
this.readWriteSpan = readWriteSpan;
43+
}
44+
45+
@Override
46+
public <T> void addAttribute(String key, T value) {
47+
if (value instanceof String) {
48+
if (entryAttributesBuilder != null) {
49+
entryAttributesBuilder.put(AttributeKey.stringKey(key), (String) value);
50+
}
51+
52+
if (sampleAttributesBuilder != null) {
53+
sampleAttributesBuilder.put(AttributeKey.stringKey(key), (String) value);
54+
}
55+
56+
if (exitAttributesBuilder != null) {
57+
exitAttributesBuilder.put(AttributeKey.stringKey(key), (String) value);
58+
}
59+
}
60+
61+
if (value instanceof Long) {
62+
if (entryAttributesBuilder != null) {
63+
entryAttributesBuilder.put(AttributeKey.longKey(key), (Long) value);
64+
}
65+
66+
if (sampleAttributesBuilder != null) {
67+
sampleAttributesBuilder.put(AttributeKey.longKey(key), (Long) value);
68+
}
69+
70+
if (exitAttributesBuilder != null) {
71+
exitAttributesBuilder.put(AttributeKey.longKey(key), (Long) value);
72+
}
73+
}
74+
75+
if (value instanceof List) {
76+
boolean match = ((List<?>) value).stream().anyMatch(item -> item instanceof Long);
77+
Long[] longs = null;
78+
if (match) {
79+
longs = ((List<?>) value).stream().map(Long.class::cast).toArray(Long[]::new);
80+
}
81+
82+
if (entryAttributesBuilder != null && match) {
83+
entryAttributesBuilder.put(AttributeKey.longArrayKey(key), longs);
84+
}
85+
86+
if (sampleAttributesBuilder != null && match) {
87+
sampleAttributesBuilder.put(AttributeKey.longArrayKey(key), longs);
88+
}
89+
90+
if (exitAttributesBuilder != null && match) {
91+
exitAttributesBuilder.put(AttributeKey.longArrayKey(key), longs);
92+
}
93+
94+
match = ((List<?>) value).stream().anyMatch(item -> item instanceof Map);
95+
String[] strings = null;
96+
if (match) {
97+
strings = ((List<?>) value).stream().map(gson::toJson).toArray(String[]::new);
98+
}
99+
100+
if (entryAttributesBuilder != null && match) {
101+
entryAttributesBuilder.put(AttributeKey.stringArrayKey(key), strings);
102+
}
103+
104+
if (sampleAttributesBuilder != null && match) {
105+
sampleAttributesBuilder.put(AttributeKey.stringArrayKey(key), strings);
106+
}
107+
108+
if (exitAttributesBuilder != null && match) {
109+
exitAttributesBuilder.put(AttributeKey.stringArrayKey(key), strings);
110+
}
111+
}
112+
}
113+
114+
@Override
115+
public void beginEntryEmit() {
116+
entryAttributesBuilder = Attributes.builder();
117+
}
118+
119+
@Override
120+
public void beginExitEmit() {
121+
exitAttributesBuilder = Attributes.builder();
122+
}
123+
124+
@Override
125+
public void beginSampleEmit() {
126+
sampleAttributesBuilder = Attributes.builder();
127+
}
128+
129+
@Override
130+
public boolean endEntryEmit() {
131+
readWriteSpan.addEvent("sw.profile", entryAttributesBuilder.build());
132+
entryAttributesBuilder = null;
133+
LoggerFactory.getLogger().debug("Entry emit has been ended: span = " + readWriteSpan);
134+
return true;
135+
}
136+
137+
@Override
138+
public boolean endExitEmit() {
139+
readWriteSpan.addEvent("sw.profile", exitAttributesBuilder.build());
140+
LoggerFactory.getLogger().debug("Exit emit has been ended: span = " + readWriteSpan);
141+
exitAttributesBuilder = null;
142+
readWriteSpan = null;
143+
return true;
144+
}
145+
146+
@Override
147+
public boolean endSampleEmit() {
148+
readWriteSpan.addEvent("sw.profile", sampleAttributesBuilder.build());
149+
LoggerFactory.getLogger().debug("Sample emit has been ended: span = " + readWriteSpan);
150+
sampleAttributesBuilder = null;
151+
return true;
152+
}
153+
}

custom/src/test/java/com/solarwinds/opentelemetry/extensions/SolarwindsProfilingSpanProcessorTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ void onStartWhenProfilingIsEnabledOnRootSpanWithValidMetadataShouldAddProfiledTh
161161

162162
processor.onStart(mockParentContext, mockSpan);
163163
profilerMock.verify(
164-
() -> Profiler.addProfiledThread(eq(Thread.currentThread()), any(), eq(traceId)), times(1));
164+
() -> Profiler.addProfiledThread(eq(Thread.currentThread()), any(), eq(traceId), any()),
165+
times(1));
165166
verify(mockSpan, never()).setAttribute(anyString(), anyInt());
166167
}
167168

0 commit comments

Comments
 (0)