Skip to content

Commit

Permalink
Merge pull request #280 from solarwinds/cc/NH-93486
Browse files Browse the repository at this point in the history
NH-93486: export traces via otlp
  • Loading branch information
cleverchuk authored Nov 22, 2024
2 parents 74163b3 + 336c3ac commit f7f878f
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 74 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ on:
schedule:
- cron: '0 7 * * 1'

permissions:
packages: read
contents: read
id-token: write

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_USERNAME: ${{ github.actor }}

jobs:
analyze:
name: Analyze
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* © SolarWinds Worldwide, LLC. All rights reserved.
*
* 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 com.solarwinds.opentelemetry.extensions;

import com.google.auto.service.AutoService;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;

@AutoService(ResourceProvider.class)
public class ApmResourceProvider implements ResourceProvider {
public static final AttributeKey<String> moduleKey = AttributeKey.stringKey("sw.data.module");

public static final AttributeKey<String> versionKey = AttributeKey.stringKey("sw.apm.version");

@Override
public Resource createResource(ConfigProperties configProperties) {
Attributes resourceAttributes =
Attributes.of(moduleKey, "apm", versionKey, BuildConfig.SOLARWINDS_AGENT_VERSION);
return Resource.create(resourceAttributes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@

package com.solarwinds.opentelemetry.extensions;

import static com.solarwinds.opentelemetry.extensions.SharedNames.LAYER_NAME_PLACEHOLDER;
import static com.solarwinds.opentelemetry.extensions.SharedNames.TRANSACTION_NAME_KEY;

import com.solarwinds.joboe.logging.Logger;
import com.solarwinds.joboe.logging.LoggerFactory;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.LongHistogram;
Expand Down Expand Up @@ -116,7 +118,13 @@ public void onEnding(ReadWriteSpan span) {
final SpanContext parentSpanContext = spanData.getParentSpanContext();
if (!parentSpanContext.isValid() || parentSpanContext.isRemote()) {
span.setAttribute(TRANSACTION_NAME_KEY, TransactionNameManager.getTransactionName(spanData));
span.setAttribute(
AttributeKey.stringKey("TransactionName"),
TransactionNameManager.getTransactionName(spanData));
}

span.setAttribute(
"Layer", String.format(LAYER_NAME_PLACEHOLDER, span.getKind(), span.getName().trim()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ public class ResourceCustomizer implements BiFunction<Resource, ConfigProperties

@Override
public Resource apply(Resource resource, ConfigProperties configProperties) {
ResourceBuilder resourceBuilder =
resource.toBuilder()
.put("sw.data.module", "apm")
.put("sw.apm.version", BuildConfig.SOLARWINDS_AGENT_VERSION);
ResourceBuilder resourceBuilder = resource.toBuilder();
String resourceAttribute = resource.getAttribute(ResourceAttributes.PROCESS_COMMAND_LINE);
List<String> processArgs = resource.getAttribute(ResourceAttributes.PROCESS_COMMAND_ARGS);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* © SolarWinds Worldwide, LLC. All rights reserved.
*
* 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 com.solarwinds.opentelemetry.extensions;

import static com.solarwinds.opentelemetry.extensions.ApmResourceProvider.moduleKey;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.resources.Resource;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class ApmResourceProviderTest {
@InjectMocks private ApmResourceProvider apmResourceProvider;

@Test
void testCreateResource() {
Resource resource =
apmResourceProvider.createResource(DefaultConfigProperties.create(Collections.emptyMap()));
String module = resource.getAttribute(moduleKey);
String version = resource.getAttribute(moduleKey);

assertNotNull(module);
assertNotNull(version);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

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

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.resources.Resource;
Expand Down Expand Up @@ -100,19 +99,4 @@ void verifyThatProcessCommandLineIsNotModifiedWhenServiceKeyIsNotPresentProcessC
Arrays.asList("-Duser.country=US", "-Duser.language=en"),
actual.getAttribute(ResourceAttributes.PROCESS_COMMAND_ARGS));
}

@Test
void verifyAgentVersionIsAddedToResource() {
Resource resource =
Resource.create(
Attributes.builder()
.put(
ResourceAttributes.PROCESS_COMMAND_ARGS,
Arrays.asList("-Duser.country=US", "-Duser.language=en"))
.build());
Resource actual =
tested.apply(resource, DefaultConfigProperties.create(Collections.emptyMap()));

assertNotNull(actual.getAttribute(AttributeKey.stringKey("sw.apm.version")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* © SolarWinds Worldwide, LLC. All rights reserved.
*
* 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.
*/

/*
* © SolarWinds Worldwide, LLC. All rights reserved.
*
* 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.
*/

/*
* © SolarWinds Worldwide, LLC. All rights reserved.
*
* 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 com.solarwinds.opentelemetry.extensions;

import com.solarwinds.joboe.core.HostId;
import com.solarwinds.joboe.core.util.ServerHostInfoReader;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.ResourceAttributes;

public class HostIdResourceProvider implements ResourceProvider {

@Override
public Resource createResource(ConfigProperties configProperties) {
AttributesBuilder builder = Attributes.builder();

HostId hostId = ServerHostInfoReader.INSTANCE.getHostId();
builder.put(ResourceAttributes.CONTAINER_ID, hostId.getDockerContainerId());
builder.put(ResourceAttributes.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(ResourceAttributes.HOST_ID, hostId.getHerokuDynoId());
builder.put(AttributeKey.stringKey("sw.uams.client.id"), hostId.getUamsClientId());
builder.put(AttributeKey.stringKey("uuid"), hostId.getUuid());

HostId.K8sMetadata k8sMetadata = hostId.getK8sMetadata();
if (k8sMetadata != null) {
builder.put(ResourceAttributes.K8S_POD_UID, k8sMetadata.getPodUid());
builder.put(ResourceAttributes.K8S_NAMESPACE_NAME, k8sMetadata.getNamespace());
builder.put(ResourceAttributes.K8S_POD_NAME, k8sMetadata.getPodName());
}

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

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

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

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

builder.put(ResourceAttributes.CLOUD_ACCOUNT_ID, azureVmMetadata.getCloudAccountId());
builder.put(ResourceAttributes.CLOUD_PLATFORM, azureVmMetadata.getCloudPlatform());
builder.put(ResourceAttributes.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(ResourceAttributes.HOST_NAME, hostId.getHostname());
builder.put(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, hostId.getEc2AvailabilityZone());
builder.put(ResourceAttributes.HOST_ID, hostId.getEc2InstanceId());
return Resource.create(builder.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public class SolarwindsPropertiesSupplier implements Supplier<Map<String, String

static {
if (isAgentEnabled()) {
PROPERTIES.put("otel.traces.exporter", COMPONENT_NAME);
PROPERTIES.put("otel.metrics.exporter", "none");
PROPERTIES.put("otel.logs.exporter", "none");
PROPERTIES.put("otel.propagators", String.format("tracecontext,baggage,%s", COMPONENT_NAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,33 +84,10 @@ public CompletableResultCode export(@Nonnull Collection<SpanData> collection) {
entryEvent = new EventImpl(null, w3cContext, false);
}

if (!spanData.getParentSpanContext().isValid()
|| spanData.getParentSpanContext().isRemote()) { // then a root span of this service
String transactionName =
spanData
.getAttributes()
.get(
AttributeKey.stringKey(
"TransactionName")); // check if there's transaction name set as
// attribute
if (transactionName == null) {
transactionName = TransactionNameManager.getTransactionName(spanData);
if (transactionName != null) {
entryEvent.addInfo(
"TransactionName",
transactionName); // only do this if we are generating a transaction name here.
// If it's already in attributes, it will be inserted by
// addInfo(getTags...)
}
}
}

InstrumentationScopeInfo scopeInfo = spanData.getInstrumentationScopeInfo();
entryEvent.addInfo(
"Label",
"entry",
"Layer",
spanName,
"sw.span_kind",
spanData.getKind().toString(),
"otel.scope.name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.solarwinds.opentelemetry.extensions.initialize;

import static com.solarwinds.opentelemetry.extensions.SharedNames.COMPONENT_NAME;

import com.solarwinds.joboe.config.ConfigContainer;
import com.solarwinds.joboe.config.ConfigGroup;
import com.solarwinds.joboe.config.ConfigManager;
Expand Down Expand Up @@ -270,6 +272,42 @@ static void configureOtelMetricExport(ConfigContainer container) {
}
}

static void configureOtelTraceExport(ConfigContainer container) {
String serviceKey = (String) container.get(ConfigProperty.AGENT_SERVICE_KEY);
String apiKey = ServiceKeyUtils.getApiKey(serviceKey);

String dataCell = "na-01";
String env = "cloud";
String collectorEndpoint = (String) container.get(ConfigProperty.AGENT_COLLECTOR);

if (collectorEndpoint != null) {
if (collectorEndpoint.contains("appoptics.com")) {
System.setProperty("otel.traces.exporter", COMPONENT_NAME);
return;
}

collectorEndpoint = collectorEndpoint.split(":")[0];
String[] fragments = collectorEndpoint.split("\\.");
if (fragments.length > 2) {
// This is based on knowledge of the SWO url format where the third name from the left in
// the domain is the data-cell name and assumes this format will stay stable.
dataCell = fragments[2];
}

if (fragments.length > 3) {
env = fragments[3];
}
}

System.setProperty("otel.exporter.otlp.traces.protocol", "grpc");
System.setProperty(
"otel.exporter.otlp.traces.headers", String.format("authorization=Bearer %s", apiKey));

System.setProperty(
"otel.exporter.otlp.traces.endpoint",
String.format("https://otel.collector.%s.%s.solarwinds.com", dataCell, env));
}

static Map<String, String> mergeEnvWithSysProperties(Map<String, String> env, Properties props) {
Map<String, String> res = new HashMap<>(env);

Expand Down Expand Up @@ -323,6 +361,7 @@ private static void loadConfigurations() throws InvalidConfigException {
processConfigs(configs);
configureOtelLogExport(configs);
configureOtelMetricExport(configs);
configureOtelTraceExport(configs);
} catch (InvalidConfigException e) {
// if there was a config read exception then processConfigs might throw exception due to
// incomplete config container.
Expand Down
Loading

0 comments on commit f7f878f

Please sign in to comment.