Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
.gradle/
.DS_Store
.idea/
buildSrc/.kotlin/
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ plugins{
id("io.github.gradle-nexus.publish-plugin") version "2.0.0"
}

val swoAgentVersion = "3.2.0"
val swoAgentVersion = "3.3.0"
extra["swoAgentVersion"] = swoAgentVersion
group = "com.solarwinds"
version = if (System.getenv("SNAPSHOT_BUILD").toBoolean()) "$swoAgentVersion-SNAPSHOT" else swoAgentVersion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ public class HostIdResourceProvider implements ResourceProvider {
public Resource createResource(ConfigProperties configProperties) {
return Resource.create(HostIdResourceUtil.createAttribute());
}

@Override
public int order() {
return Integer.MIN_VALUE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class SolarwindsPropertiesSupplier implements Supplier<Map<String, String
"base2_exponential_bucket_histogram");
PROPERTIES.put(
"otel.instrumentation.runtime-telemetry.emit-experimental-jfr-metrics", "true");
PROPERTIES.put("otel.resource.providers.azure.enabled", "true");
PROPERTIES.put("otel.resource.providers.aws.enabled", "true");
} else {
PROPERTIES.put("otel.sdk.disabled", "true");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,32 +165,6 @@ private static void maybeFollowOtelConfigProperties(ConfigContainer configs) {
logger.info("failed to follow Otel's log file config." + e.getMessage());
}
}

String serviceName = System.getProperty("otel.service.name");
if (serviceName == null) {
serviceName = System.getenv("OTEL_SERVICE_NAME");
}

String serviceKey = (String) configs.get(ConfigProperty.AGENT_SERVICE_KEY);
if (serviceName == null) {
if (serviceKey != null) {
String name = ServiceKeyUtils.getServiceName(serviceKey);
if (name != null) {
System.setProperty("otel.service.name", name);
}
}

} else {
if (serviceKey != null) {
try {
String key = String.format("%s:%s", ServiceKeyUtils.getApiKey(serviceKey), serviceName);
configs.put(ConfigProperty.AGENT_SERVICE_KEY, key, true);
} catch (InvalidConfigException e) {
LoggerFactory.getLogger()
.warn(String.format("Unable to update service name to %s", serviceName));
}
}
}
}

static void configureOtel(ConfigContainer container) throws InvalidConfigException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,80 +21,28 @@
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.semconv.ContainerAttributes;
import io.opentelemetry.semconv.K8sAttributes;
import io.opentelemetry.semconv.ServiceAttributes;
import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes;
import io.opentelemetry.semconv.incubating.HostIncubatingAttributes;
import io.opentelemetry.semconv.incubating.ProcessIncubatingAttributes;

public final class HostIdResourceUtil {
private HostIdResourceUtil() {}

public static Attributes createAttribute() {
AttributesBuilder builder = Attributes.builder();

HostId hostId = ServerHostInfoReader.INSTANCE.getHostId();
builder.put(ContainerAttributes.CONTAINER_ID, hostId.getDockerContainerId());
builder.put(ProcessIncubatingAttributes.PROCESS_PID, (long) hostId.getPid());
builder.put(AttributeKey.stringArrayKey("mac.addresses"), hostId.getMacAddresses());

builder.put(
AttributeKey.stringKey("azure.app.service.instance.id"),
hostId.getAzureAppServiceInstanceId());
builder.put(HostIncubatingAttributes.HOST_ID, hostId.getHerokuDynoId());
builder.put(AttributeKey.stringKey("sw.uams.client.id"), hostId.getUamsClientId());
builder.put(AttributeKey.stringKey("uuid"), hostId.getUuid());
builder.put(ServiceAttributes.SERVICE_INSTANCE_ID, hostId.getUuid());

HostId.K8sMetadata k8sMetadata = hostId.getK8sMetadata();

if (k8sMetadata != null) {
builder.put(K8sAttributes.K8S_POD_UID, k8sMetadata.getPodUid());
builder.put(K8sAttributes.K8S_NAMESPACE_NAME, k8sMetadata.getNamespace());
builder.put(K8sAttributes.K8S_POD_NAME, k8sMetadata.getPodName());
}

HostId.AwsMetadata awsMetadata = hostId.getAwsMetadata();
if (awsMetadata != null) {
builder.put(HostIncubatingAttributes.HOST_ID, awsMetadata.getHostId());
builder.put(HostIncubatingAttributes.HOST_NAME, awsMetadata.getHostName());
builder.put(CloudIncubatingAttributes.CLOUD_PROVIDER, awsMetadata.getCloudProvider());

builder.put(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, awsMetadata.getCloudAccountId());
builder.put(CloudIncubatingAttributes.CLOUD_PLATFORM, awsMetadata.getCloudPlatform());
builder.put(
CloudIncubatingAttributes.CLOUD_AVAILABILITY_ZONE,
awsMetadata.getCloudAvailabilityZone());

builder.put(CloudIncubatingAttributes.CLOUD_REGION, awsMetadata.getCloudRegion());
builder.put(HostIncubatingAttributes.HOST_IMAGE_ID, awsMetadata.getHostImageId());
builder.put(HostIncubatingAttributes.HOST_TYPE, awsMetadata.getHostType());
}

HostId.AzureVmMetadata azureVmMetadata = hostId.getAzureVmMetadata();
if (azureVmMetadata != null) {
builder.put(HostIncubatingAttributes.HOST_ID, azureVmMetadata.getHostId());
builder.put(HostIncubatingAttributes.HOST_NAME, azureVmMetadata.getHostName());
builder.put(CloudIncubatingAttributes.CLOUD_PROVIDER, azureVmMetadata.getCloudProvider());

builder.put(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, azureVmMetadata.getCloudAccountId());
builder.put(CloudIncubatingAttributes.CLOUD_PLATFORM, azureVmMetadata.getCloudPlatform());
builder.put(CloudIncubatingAttributes.CLOUD_REGION, azureVmMetadata.getCloudRegion());

builder.put(AttributeKey.stringKey("azure.vm.name"), azureVmMetadata.getAzureVmName());
builder.put(AttributeKey.stringKey("azure.vm.size"), azureVmMetadata.getAzureVmSize());
builder.put(
AttributeKey.stringKey("azure.resourcegroup.name"),
azureVmMetadata.getAzureResourceGroupName());

builder.put(
AttributeKey.stringKey("azure.vm.scaleset.name"),
azureVmMetadata.getAzureVmScaleSetName());
}

builder.put(HostIncubatingAttributes.HOST_NAME, hostId.getHostname());
builder.put(CloudIncubatingAttributes.CLOUD_AVAILABILITY_ZONE, hostId.getEc2AvailabilityZone());
builder.put(HostIncubatingAttributes.HOST_ID, hostId.getEc2InstanceId());
return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ public Settings getSettings() {

String tokenHeader = String.format("Bearer %s", apiToken);
Settings fetchedSettings = delegate.fetchSettings(settingsUrl, tokenHeader);
logger.debug(String.format("Got settings from http: %s", fetchedSettings));
logger.debug(
String.format(
"Got settings from http: %s, serviceName: %s, hostname: %s",
fetchedSettings, serviceName, hostname));

return fetchedSettings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ private void addResourceDetector(ResourceModel resourceModel) {
detectors = new ArrayList<>();
}

List<ExperimentalResourceDetectorModel> newDetectors = new ArrayList<>(detectors);
// Ordering matters: SWO detectors are added first, then upstream detectors (e.g. aws/azure)
// are appended so they run last. With last-writer-wins merge semantics this lets upstream
// cloud attributes override our host-id attributes. Do not reorder without accounting for
// which detector's attributes should survive the merge.
List<ExperimentalResourceDetectorModel> newDetectors = new ArrayList<>();
newDetectors.add(
new ExperimentalResourceDetectorModel()
.withAdditionalProperty(
Expand All @@ -79,6 +83,8 @@ private void addResourceDetector(ResourceModel resourceModel) {
.withAdditionalProperty(
HostIdResourceComponentProvider.COMPONENT_NAME,
new ExperimentalResourceDetectorPropertyModel()));

newDetectors.addAll(detectors);
detectionDevelopment.withDetectors(newDetectors);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ public class ResourceComponentProvider implements ComponentProvider {

public static final String COMPONENT_NAME = "swo/resource";

@Getter @Setter private static Resource resource = null;
// Accumulated statically: create() merges into this field in place, and
// HostIdResourceComponentProvider reads it via getResource().merge(...). Assumes create() is
// invoked once; merging identical attributes is idempotent, but any new writer of this field
// must be aware the result is order-dependent.
@Getter @Setter private static Resource resource = Resource.getDefault();

@Override
public Class<Resource> getType() {
Expand All @@ -50,7 +54,7 @@ public Resource create(DeclarativeConfigProperties declarativeConfigProperties)
Attributes resourceAttributes =
Attributes.of(moduleKey, "apm", versionKey, BuildConfig.SOLARWINDS_AGENT_VERSION);

resource = Resource.create(resourceAttributes);
resource = resource.merge(Resource.create(resourceAttributes));
return resource;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.solarwinds.joboe.config.ConfigContainer;
import com.solarwinds.joboe.config.ConfigManager;
import com.solarwinds.joboe.config.ConfigProperty;
import com.solarwinds.joboe.config.InvalidConfigException;
import com.solarwinds.joboe.config.ServiceKeyUtils;
import com.solarwinds.opentelemetry.extensions.DefaultNamingScheme;
import com.solarwinds.opentelemetry.extensions.NamingScheme;
import com.solarwinds.opentelemetry.extensions.SpanAttributeNamingScheme;
Expand Down Expand Up @@ -133,27 +131,6 @@ void testWindowsRootPath() {
assertNull(ConfigurationLoader.getRuntimeConfigFilename());
}

@Test
@ClearSystemProperty(key = "otel.service.name")
void verifyThatOtelServiceNameSystemPropertyIsWhenNotExplicitlySet()
throws InvalidConfigException {
ConfigurationLoader.load();
assertEquals(
"name" /*name is from custom/build.gradle test task*/,
System.getProperty("otel.service.name"));
}

@Test
@SetSystemProperty(key = "otel.service.name", value = "test")
void verifyThatServiceKeyIsUpdatedWithOtelServiceNameWhenSystemPropertyIsSet()
throws InvalidConfigException {
ConfigurationLoader.load();
String serviceKeyAfter = (String) ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY);

assertEquals("test", ServiceKeyUtils.getServiceName(serviceKeyAfter));
assertEquals("token:test", serviceKeyAfter);
}

@Test
@ClearSystemProperty(key = "otel.exporter.otlp.endpoint")
@ClearSystemProperty(key = "otel.exporter.otlp.headers")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@

import static org.junit.jupiter.api.Assertions.assertNotNull;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.semconv.incubating.HostIncubatingAttributes;
import io.opentelemetry.semconv.incubating.ProcessIncubatingAttributes;
import java.util.List;
import org.junit.jupiter.api.Test;

class HostIdResourceUtilTest {
@Test
void testCreateAttributes() {
Attributes attribute = HostIdResourceUtil.createAttribute();
Long pid = attribute.get(ProcessIncubatingAttributes.PROCESS_PID);
String hostname = attribute.get(HostIncubatingAttributes.HOST_NAME);

assertNotNull(pid);
assertNotNull(hostname);
List<String> macAddresses = attribute.get(AttributeKey.stringArrayKey("mac.addresses"));
assertNotNull(macAddresses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,32 +110,6 @@ private static void maybeFollowOtelConfigProperties(ConfigContainer configs) {
logger.info("failed to follow Otel's log file config." + e.getMessage());
}
}

String serviceName = System.getProperty("otel.service.name");
if (serviceName == null) {
serviceName = System.getenv("OTEL_SERVICE_NAME");
}

String serviceKey = (String) configs.get(ConfigProperty.AGENT_SERVICE_KEY);
if (serviceName == null) {
if (serviceKey != null) {
String name = ServiceKeyUtils.getServiceName(serviceKey);
if (name != null) {
System.setProperty("otel.service.name", name);
}
}

} else {
if (serviceKey != null) {
try {
String key = String.format("%s:%s", ServiceKeyUtils.getApiKey(serviceKey), serviceName);
configs.put(ConfigProperty.AGENT_SERVICE_KEY, key, true);
} catch (InvalidConfigException e) {
LoggerFactory.getLogger()
.warn(String.format("Unable to update service name to %s", serviceName));
}
}
}
}

static Map<String, String> mergeEnvWithSysProperties(Map<String, String> env, Properties props) {
Expand Down
2 changes: 2 additions & 0 deletions libs/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ dependencies {
implementation("io.opentelemetry.contrib:opentelemetry-cel-sampler") {
exclude(module = "opentelemetry-sdk", group = "io.opentelemetry") // the agent includes this
exclude(group = "com.google.code.findbugs", module = "annotations")
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.protobuf")
}

implementation("org.json:json")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package com.solarwinds.opentelemetry.extensions;

import com.solarwinds.joboe.config.ConfigManager;
import com.solarwinds.joboe.config.ConfigProperty;
import com.solarwinds.joboe.config.InvalidConfigException;
import com.solarwinds.joboe.config.ServiceKeyUtils;
import com.solarwinds.joboe.logging.LoggerFactory;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.resources.Resource;
Expand All @@ -25,9 +29,11 @@
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import lombok.Getter;

public class ResourceCustomizer implements BiFunction<Resource, ConfigProperties, Resource> {
private static Resource resource;

@Getter private static Resource resource;

@Override
public Resource apply(Resource resource, ConfigProperties configProperties) {
Expand All @@ -51,16 +57,38 @@ public Resource apply(Resource resource, ConfigProperties configProperties) {
resourceBuilder.put(ProcessIncubatingAttributes.PROCESS_COMMAND_ARGS, args);
}

String serviceName = getServiceName(resource);
updateServiceKey(serviceName);
resourceBuilder.put(ServiceAttributes.SERVICE_NAME, serviceName);

LoggerFactory.getLogger().info("Resolved service name: " + serviceName);
ResourceCustomizer.resource = resourceBuilder.build();
LoggerFactory.getLogger()
.debug(
String.format(
"This log line is used for validation only: service.name: %s",
resource.getAttribute(ServiceAttributes.SERVICE_NAME)));
return ResourceCustomizer.resource;
}

public static Resource getResource() {
return resource;
private String getServiceName(Resource resource) {
String serviceName = resource.getAttribute(ServiceAttributes.SERVICE_NAME);
if (serviceName == null || serviceName.startsWith("unknown")) {
String serviceKey = ConfigManager.getConfigOptional(ConfigProperty.AGENT_SERVICE_KEY, null);
if (serviceKey != null) {
serviceName = ServiceKeyUtils.getServiceName(serviceKey);
}
}
Comment thread
cleverchuk marked this conversation as resolved.

return serviceName;
}

private void updateServiceKey(String serviceName) {
if (serviceName != null) {
String serviceKey = ConfigManager.getConfigOptional(ConfigProperty.AGENT_SERVICE_KEY, ":");
String apiKey = ServiceKeyUtils.getApiKey(serviceKey);

try {
ConfigManager.setConfig(
ConfigProperty.AGENT_SERVICE_KEY, String.format("%s:%s", apiKey, serviceName));
} catch (InvalidConfigException ignore) {
LoggerFactory.getLogger().debug("Failed to update service key with name: " + serviceName);
}
}
}
Comment thread
cleverchuk marked this conversation as resolved.
}
Loading
Loading