Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright The Hypertrace 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 org.hypertrace.agent.otel.extensions;

import io.opentelemetry.api.trace.Span;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class SpanUtils {

static final String AGENT_VERSION_ATTRIBUTE_KEY = "otel.extension.library.version";
private static final Logger logger = LoggerFactory.getLogger(SpanUtils.class.getName());

private static final ConcurrentMap<String, String> ATTRIBUTE_MAP = new ConcurrentHashMap<>();

private static Supplier<String> agentVersionSupplier = () -> getAgentVersion();

private static boolean errorObtainingAgentVersion;

/**
* Obtains the current version of the Hypertrace agent. This is used to populate the
* "otel.extension.library.version" that is added to the Span.
*
* @return
*/
private static boolean setAgentVersion() {
String agentVersion = ATTRIBUTE_MAP.get(AGENT_VERSION_ATTRIBUTE_KEY);

if (agentVersion == null && !errorObtainingAgentVersion) {
agentVersion = agentVersionSupplier.get();

if (agentVersion != null) {
ATTRIBUTE_MAP.put(AGENT_VERSION_ATTRIBUTE_KEY, agentVersion);
} else {
errorObtainingAgentVersion = true;
}
}
return !errorObtainingAgentVersion;
}

/**
* Adds the hypertrace-specific attributes to the Span.
*
* @param span
*/
public static void setSpanAttributes(Span span) {
if (span == null) {
return;
}

collect();

for (Map.Entry<String, String> nextAttribute : ATTRIBUTE_MAP.entrySet()) {
span.setAttribute(nextAttribute.getKey(), nextAttribute.getValue());
}
}

/**
* Collects the data that is used for the attribute values.
*
* @return
*/
private static boolean collect() {
if (!setAgentVersion()) {
return false;
}

return true;
}

/**
* Obtain the agent version by obtaining the implementation version of the HypertraceAgent class.
*
* @return
*/
static String getAgentVersion() {
try {
Class<?> clazz = Class.forName("org.hypertrace.agent.instrument.HypertraceAgent", true, null);
return clazz.getPackage().getImplementationVersion();
} catch (ClassNotFoundException e) {
logger.warn("Could not load HypertraceAgent class");
return null;
}
}

/**
* For unit tests, replace the default agentVersionSupplier.
*
* @param _agentVersionSupplier
*/
static void setVersionSupplier(Supplier<String> _agentVersionSupplier) {
agentVersionSupplier = _agentVersionSupplier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.hypertrace.agent.otel.extensions.CgroupsReader;
import org.hypertrace.agent.otel.extensions.SpanUtils;

public class AddTagsSpanProcessor implements SpanProcessor {

Expand All @@ -40,6 +41,8 @@ public void onStart(Context parentContext, ReadWriteSpan span) {
if (containerId != null && !containerId.isEmpty()) {
span.setAttribute(ResourceAttributes.CONTAINER_ID, containerId);
}

SpanUtils.setSpanAttributes(span);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright The Hypertrace 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 org.hypertrace.agent.otel.extensions;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.StatusCode;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/** Unit tests for SpanUtils. */
public class SpanUtilsTest {

/**
* We can't seem to create mocks using Mockito in this environment, so we create a concrete
* implementation of the Span interface, to serve as a mock.
*/
private static class MockSpan implements Span {

private final Map<AttributeKey<String>, String> attributeMap = new HashMap<>();

@Override
public <T> Span setAttribute(AttributeKey<T> key, T value) {
this.attributeMap.put((AttributeKey<String>) key, (String) value);
return this;
}

@Override
public Span addEvent(String name, Attributes attributes) {
return null;
}

@Override
public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) {
return null;
}

@Override
public Span setStatus(StatusCode statusCode, String description) {
return null;
}

@Override
public Span recordException(Throwable exception, Attributes additionalAttributes) {
return null;
}

@Override
public Span updateName(String name) {
return null;
}

@Override
public void end() {}

@Override
public void end(long timestamp, TimeUnit unit) {}

@Override
public SpanContext getSpanContext() {
return null;
}

@Override
public boolean isRecording() {
return false;
}

String getAttribute(String attributeKey) {
AttributeKey key = AttributeKey.stringKey(attributeKey);
return attributeMap.get(key);
}
}

private static final String AGENT_VERSION = "1.2.3";

private MockSpan mockSpan;

public SpanUtilsTest() {
super();
}

@BeforeEach
public void setup() {
mockSpan = new MockSpan();
}

@Test
public void testSetSpanAttributes() {
SpanUtils.setVersionSupplier(() -> AGENT_VERSION);

SpanUtils.setSpanAttributes(mockSpan);

String attrValue = mockSpan.getAttribute(SpanUtils.AGENT_VERSION_ATTRIBUTE_KEY);

Assertions.assertEquals(AGENT_VERSION, attrValue);
}
}