Skip to content

Commit

Permalink
NH-93486: export traces using otlp
Browse files Browse the repository at this point in the history
  • Loading branch information
cleverchuk committed Nov 7, 2024
1 parent c85404d commit 48ac1ba
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

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;
Expand Down Expand Up @@ -117,6 +118,12 @@ public void onEnding(ReadWriteSpan span) {
if (!parentSpanContext.isValid() || parentSpanContext.isRemote()) {
span.setAttribute(TRANSACTION_NAME_KEY, 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
@@ -0,0 +1,153 @@
/*
* © 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.google.auto.service.AutoService;
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;
import java.util.function.BiConsumer;

@AutoService(ResourceProvider.class)
public class HostIdResourceProvider implements ResourceProvider {

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

HostId hostId = ServerHostInfoReader.INSTANCE.getHostId();
setIfNotNull(builder::put, ResourceAttributes.HOST_NAME, hostId.getHostname());
setIfNotNull(
builder::put, ResourceAttributes.CLOUD_AVAILABILITY_ZONE, hostId.getEc2AvailabilityZone());
setIfNotNull(builder::put, ResourceAttributes.HOST_ID, hostId.getEc2InstanceId());

setIfNotNull(builder::put, ResourceAttributes.CONTAINER_ID, hostId.getDockerContainerId());
setIfNotNull(builder::put, ResourceAttributes.PROCESS_PID, (long) hostId.getPid());
setIfNotNull(
builder::put, AttributeKey.stringArrayKey("mac.addresses"), hostId.getMacAddresses());

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

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

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

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

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

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

setIfNotNull(
builder::put, ResourceAttributes.CLOUD_ACCOUNT_ID, azureVmMetadata.getCloudAccountId());
setIfNotNull(
builder::put, ResourceAttributes.CLOUD_PLATFORM, azureVmMetadata.getCloudPlatform());
setIfNotNull(builder::put, ResourceAttributes.CLOUD_REGION, azureVmMetadata.getCloudRegion());

setIfNotNull(
builder::put, AttributeKey.stringKey("azure.vm.name"), azureVmMetadata.getAzureVmName());
setIfNotNull(
builder::put, AttributeKey.stringKey("azure.vm.size"), azureVmMetadata.getAzureVmSize());
setIfNotNull(
builder::put,
AttributeKey.stringKey("azure.resource.group.name"),
azureVmMetadata.getAzureResourceGroupName());

setIfNotNull(
builder::put,
AttributeKey.stringKey("azure.vm.scale.set.name"),
azureVmMetadata.getAzureVmScaleSetName());
}

return Resource.create(builder.build());
}

public static <V> void setIfNotNull(
BiConsumer<AttributeKey<V>, V> setter, AttributeKey<V> key, V value) {
if (value != null) {
setter.accept(key, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ protected MetricsMonitor buildMetricsMonitor() {
*/
private void reportInit() {
try {
if (ConfigurationLoader.shouldUseOtlpForTraces()) {
return;
}
reportLayerInit();
} catch (Exception e) {
logger.warn("Failed to post init message: " + (e.getMessage() != null ? e.getMessage() : e));
Expand Down
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 @@ -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,41 @@ 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 +360,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 Expand Up @@ -598,4 +636,9 @@ public static boolean shouldUseOtlpForMetrics() {
return (enabled == null || enabled)
&& (collectorEndpoint == null || !collectorEndpoint.contains("appoptics.com"));
}

public static boolean shouldUseOtlpForTraces() {
String collectorEndpoint = (String) ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR);
return (collectorEndpoint == null || !collectorEndpoint.contains("appoptics.com"));
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ systemProp.org.gradle.internal.repository.initial.backoff=500
# Project properties provides a central place for shared property among subprojects
otel.agent.version=2.9.0
otel.sdk.version=1.43.0
swo.agent.version=2.9.0
swo.agent.version=2.9.1
Loading

0 comments on commit 48ac1ba

Please sign in to comment.