This repository has been archived by the owner on Dec 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Exporter/OCAgent: Add OcAgentNodeUtils. (#1471)
* Exporter/OCAgent: Add OcAgentNodeUtils. Add utilities for detecting and creating Node. Equivalent to https://github.com/census-ecosystem/opencensus-go-exporter-ocagent/blob/master/nodeinfo.go. * Fix nullness checker. * Use a local VERSION string in OC-Agent Exporter.
- Loading branch information
Showing
7 changed files
with
314 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
184 changes: 184 additions & 0 deletions
184
...rs/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/* | ||
* Copyright 2018, OpenCensus 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 io.opencensus.exporter.trace.ocagent; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import io.opencensus.common.OpenCensusLibraryInformation; | ||
import io.opencensus.common.Timestamp; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResource; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResource.AwsEc2InstanceMonitoredResource; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGceInstanceMonitoredResource; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGkeContainerMonitoredResource; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResourceUtils; | ||
import io.opencensus.proto.agent.common.v1.LibraryInfo; | ||
import io.opencensus.proto.agent.common.v1.LibraryInfo.Language; | ||
import io.opencensus.proto.agent.common.v1.Node; | ||
import io.opencensus.proto.agent.common.v1.ProcessIdentifier; | ||
import io.opencensus.proto.agent.common.v1.ServiceInfo; | ||
import java.lang.management.ManagementFactory; | ||
import java.net.InetAddress; | ||
import java.net.UnknownHostException; | ||
import java.security.SecureRandom; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import javax.annotation.Nullable; | ||
|
||
/** Utilities for detecting and creating {@link Node}. */ | ||
final class OcAgentNodeUtils { | ||
|
||
// The current version of the OpenCensus OC-Agent Exporter. | ||
@VisibleForTesting | ||
static final String OC_AGENT_EXPORTER_VERSION = "0.17.0-SNAPSHOT"; // CURRENT_OPENCENSUS_VERSION | ||
|
||
@VisibleForTesting static final String RESOURCE_TYPE_ATTRIBUTE_KEY = "OPENCENSUS_SOURCE_TYPE"; | ||
@VisibleForTesting static final String RESOURCE_LABEL_ATTRIBUTE_KEY = "OPENCENSUS_SOURCE_LABELS"; | ||
|
||
@Nullable | ||
private static final MonitoredResource RESOURCE = MonitoredResourceUtils.getDefaultResource(); | ||
|
||
// Creates a Node with information from the OpenCensus library and environment variables. | ||
static Node getNodeInfo(String serviceName) { | ||
String jvmName = ManagementFactory.getRuntimeMXBean().getName(); | ||
Timestamp censusTimestamp = Timestamp.fromMillis(System.currentTimeMillis()); | ||
return Node.newBuilder() | ||
.setIdentifier(getProcessIdentifier(jvmName, censusTimestamp)) | ||
.setLibraryInfo(getLibraryInfo(OpenCensusLibraryInformation.VERSION)) | ||
.setServiceInfo(getServiceInfo(serviceName)) | ||
.putAllAttributes(getAttributeMap(RESOURCE)) | ||
.build(); | ||
} | ||
|
||
// Creates process identifier with the given JVM name and start time. | ||
@VisibleForTesting | ||
static ProcessIdentifier getProcessIdentifier(String jvmName, Timestamp censusTimestamp) { | ||
String hostname; | ||
int pid; | ||
// jvmName should be something like '<pid>@<hostname>', at least in Oracle and OpenJdk JVMs | ||
int delimiterIndex = jvmName.indexOf('@'); | ||
if (delimiterIndex < 1) { | ||
// Not the expected format, generate a random number. | ||
try { | ||
hostname = InetAddress.getLocalHost().getHostName(); | ||
} catch (UnknownHostException e) { | ||
hostname = "localhost"; | ||
} | ||
// Generate a random number as the PID. | ||
pid = new SecureRandom().nextInt(); | ||
} else { | ||
hostname = jvmName.substring(delimiterIndex + 1, jvmName.length()); | ||
try { | ||
pid = Integer.parseInt(jvmName.substring(0, delimiterIndex)); | ||
} catch (NumberFormatException e) { | ||
// Generate a random number as the PID if format is unexpected. | ||
pid = new SecureRandom().nextInt(); | ||
} | ||
} | ||
|
||
return ProcessIdentifier.newBuilder() | ||
.setHostName(hostname) | ||
.setPid(pid) | ||
.setStartTimestamp(TraceProtoUtils.toTimestampProto(censusTimestamp)) | ||
.build(); | ||
} | ||
|
||
// Creates library info with the given OpenCensus Java version. | ||
@VisibleForTesting | ||
static LibraryInfo getLibraryInfo(String currentOcJavaVersion) { | ||
return LibraryInfo.newBuilder() | ||
.setLanguage(Language.JAVA) | ||
.setCoreLibraryVersion(currentOcJavaVersion) | ||
.setExporterVersion(OC_AGENT_EXPORTER_VERSION) | ||
.build(); | ||
} | ||
|
||
// Creates service info with the given service name. | ||
@VisibleForTesting | ||
static ServiceInfo getServiceInfo(String serviceName) { | ||
return ServiceInfo.newBuilder().setName(serviceName).build(); | ||
} | ||
|
||
/* | ||
* Creates an attribute map with the given MonitoredResource. | ||
* If the given resource is not null, the attribute map contains exactly two entries: | ||
* | ||
* OPENCENSUS_SOURCE_TYPE: | ||
* A string that describes the type of the resource prefixed by a domain namespace, | ||
* e.g. “kubernetes.io/container”. | ||
* OPENCENSUS_SOURCE_LABELS: | ||
* A comma-separated list of labels describing the source in more detail, | ||
* e.g. “key1=val1,key2=val2”. The allowed character set is appropriately constrained. | ||
*/ | ||
// TODO: update the resource attributes once we have an agreement on the resource specs: | ||
// https://github.com/census-instrumentation/opencensus-specs/pull/162. | ||
@VisibleForTesting | ||
static Map<String, String> getAttributeMap(@Nullable MonitoredResource resource) { | ||
if (resource == null) { | ||
return Collections.emptyMap(); | ||
} else { | ||
Map<String, String> resourceAttributes = new HashMap<String, String>(); | ||
resourceAttributes.put(RESOURCE_TYPE_ATTRIBUTE_KEY, resource.getResourceType().name()); | ||
resourceAttributes.put(RESOURCE_LABEL_ATTRIBUTE_KEY, getConcatenatedResourceLabels(resource)); | ||
return resourceAttributes; | ||
} | ||
} | ||
|
||
// Encodes the attributes of MonitoredResource into a comma-separated list of labels. | ||
// For example "aws_account=account1,instance_id=instance1,region=us-east-2". | ||
private static String getConcatenatedResourceLabels(MonitoredResource resource) { | ||
StringBuilder resourceLabels = new StringBuilder(); | ||
if (resource instanceof AwsEc2InstanceMonitoredResource) { | ||
AwsEc2InstanceMonitoredResource awsEc2Resource = (AwsEc2InstanceMonitoredResource) resource; | ||
putIntoBuilderIfHasValue(resourceLabels, "aws_account", awsEc2Resource.getAccount()); | ||
putIntoBuilderIfHasValue(resourceLabels, "instance_id", awsEc2Resource.getInstanceId()); | ||
putIntoBuilderIfHasValue(resourceLabels, "region", awsEc2Resource.getRegion()); | ||
} else if (resource instanceof GcpGceInstanceMonitoredResource) { | ||
GcpGceInstanceMonitoredResource gceResource = (GcpGceInstanceMonitoredResource) resource; | ||
putIntoBuilderIfHasValue(resourceLabels, "gcp_account", gceResource.getAccount()); | ||
putIntoBuilderIfHasValue(resourceLabels, "instance_id", gceResource.getInstanceId()); | ||
putIntoBuilderIfHasValue(resourceLabels, "zone", gceResource.getZone()); | ||
} else if (resource instanceof GcpGkeContainerMonitoredResource) { | ||
GcpGkeContainerMonitoredResource gkeResource = (GcpGkeContainerMonitoredResource) resource; | ||
putIntoBuilderIfHasValue(resourceLabels, "gcp_account", gkeResource.getAccount()); | ||
putIntoBuilderIfHasValue(resourceLabels, "instance_id", gkeResource.getInstanceId()); | ||
putIntoBuilderIfHasValue(resourceLabels, "location", gkeResource.getZone()); | ||
putIntoBuilderIfHasValue(resourceLabels, "namespace_name", gkeResource.getNamespaceId()); | ||
putIntoBuilderIfHasValue(resourceLabels, "cluster_name", gkeResource.getClusterName()); | ||
putIntoBuilderIfHasValue(resourceLabels, "container_name", gkeResource.getContainerName()); | ||
putIntoBuilderIfHasValue(resourceLabels, "pod_name", gkeResource.getPodId()); | ||
} | ||
return resourceLabels.toString(); | ||
} | ||
|
||
// If the given resourceValue is not empty, encodes resourceKey and resourceValue as | ||
// "resourceKey:resourceValue" and puts it into the given StringBuilder. Otherwise skip the value. | ||
private static void putIntoBuilderIfHasValue( | ||
StringBuilder builder, String resourceKey, String resourceValue) { | ||
if (resourceValue.isEmpty()) { | ||
return; | ||
} | ||
if (!(builder.length() == 0)) { | ||
// Appends the comma separator to the front, if the StringBuilder already has entries. | ||
builder.append(','); | ||
} | ||
builder.append(resourceKey); | ||
builder.append('='); | ||
builder.append(resourceValue); | ||
} | ||
|
||
private OcAgentNodeUtils() {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
...race/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtilsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
* Copyright 2018, OpenCensus 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 io.opencensus.exporter.trace.ocagent; | ||
|
||
import static com.google.common.truth.Truth.assertThat; | ||
import static io.opencensus.exporter.trace.ocagent.OcAgentNodeUtils.OC_AGENT_EXPORTER_VERSION; | ||
import static io.opencensus.exporter.trace.ocagent.OcAgentNodeUtils.RESOURCE_LABEL_ATTRIBUTE_KEY; | ||
import static io.opencensus.exporter.trace.ocagent.OcAgentNodeUtils.RESOURCE_TYPE_ATTRIBUTE_KEY; | ||
|
||
import io.opencensus.common.Timestamp; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResource.AwsEc2InstanceMonitoredResource; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGceInstanceMonitoredResource; | ||
import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGkeContainerMonitoredResource; | ||
import io.opencensus.proto.agent.common.v1.LibraryInfo; | ||
import io.opencensus.proto.agent.common.v1.LibraryInfo.Language; | ||
import io.opencensus.proto.agent.common.v1.ProcessIdentifier; | ||
import io.opencensus.proto.agent.common.v1.ServiceInfo; | ||
import java.util.Map; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.JUnit4; | ||
|
||
/** Tests for {@link OcAgentNodeUtils}. */ | ||
@RunWith(JUnit4.class) | ||
public class OcAgentNodeUtilsTest { | ||
|
||
private static final AwsEc2InstanceMonitoredResource AWS_RESOURCE = | ||
AwsEc2InstanceMonitoredResource.create("account1", "instance1", "us-east-2"); | ||
private static final GcpGceInstanceMonitoredResource GCE_RESOURCE = | ||
GcpGceInstanceMonitoredResource.create("account2", "instance2", "us-west2"); | ||
private static final GcpGkeContainerMonitoredResource GKE_RESOURCE = | ||
GcpGkeContainerMonitoredResource.create( | ||
"account3", "cluster", "container", "", "instance3", "", "us-west4"); | ||
|
||
@Test | ||
public void testConstants() { | ||
assertThat(OC_AGENT_EXPORTER_VERSION).isEqualTo("0.17.0-SNAPSHOT"); | ||
assertThat(RESOURCE_TYPE_ATTRIBUTE_KEY).isEqualTo("OPENCENSUS_SOURCE_TYPE"); | ||
assertThat(RESOURCE_LABEL_ATTRIBUTE_KEY).isEqualTo("OPENCENSUS_SOURCE_LABELS"); | ||
} | ||
|
||
@Test | ||
public void getProcessIdentifier() { | ||
String jvmName = "[email protected]"; | ||
Timestamp timestamp = Timestamp.create(10, 20); | ||
ProcessIdentifier processIdentifier = OcAgentNodeUtils.getProcessIdentifier(jvmName, timestamp); | ||
assertThat(processIdentifier.getHostName()).isEqualTo("my.org"); | ||
assertThat(processIdentifier.getPid()).isEqualTo(54321); | ||
assertThat(processIdentifier.getStartTimestamp()) | ||
.isEqualTo(com.google.protobuf.Timestamp.newBuilder().setSeconds(10).setNanos(20).build()); | ||
} | ||
|
||
@Test | ||
public void getLibraryInfo() { | ||
String currentOcJavaVersion = "0.16.0"; | ||
LibraryInfo libraryInfo = OcAgentNodeUtils.getLibraryInfo(currentOcJavaVersion); | ||
assertThat(libraryInfo.getLanguage()).isEqualTo(Language.JAVA); | ||
assertThat(libraryInfo.getCoreLibraryVersion()).isEqualTo(currentOcJavaVersion); | ||
assertThat(libraryInfo.getExporterVersion()).isEqualTo(OC_AGENT_EXPORTER_VERSION); | ||
} | ||
|
||
@Test | ||
public void getServiceInfo() { | ||
String serviceName = "my-service"; | ||
ServiceInfo serviceInfo = OcAgentNodeUtils.getServiceInfo(serviceName); | ||
assertThat(serviceInfo.getName()).isEqualTo(serviceName); | ||
} | ||
|
||
@Test | ||
public void getAttributeMap_Null() { | ||
Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(null); | ||
assertThat(attributeMap).isEmpty(); | ||
} | ||
|
||
@Test | ||
public void getAttributeMap_AwsEc2Resource() { | ||
Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(AWS_RESOURCE); | ||
assertThat(attributeMap) | ||
.containsExactly( | ||
RESOURCE_TYPE_ATTRIBUTE_KEY, | ||
"AWS_EC2_INSTANCE", | ||
RESOURCE_LABEL_ATTRIBUTE_KEY, | ||
"aws_account=account1,instance_id=instance1,region=us-east-2"); | ||
} | ||
|
||
@Test | ||
public void getAttributeMap_GceResource() { | ||
Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(GCE_RESOURCE); | ||
assertThat(attributeMap) | ||
.containsExactly( | ||
RESOURCE_TYPE_ATTRIBUTE_KEY, | ||
"GCP_GCE_INSTANCE", | ||
RESOURCE_LABEL_ATTRIBUTE_KEY, | ||
"gcp_account=account2,instance_id=instance2,zone=us-west2"); | ||
} | ||
|
||
@Test | ||
public void getAttributeMap_GkeResource() { | ||
Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(GKE_RESOURCE); | ||
assertThat(attributeMap) | ||
.containsExactly( | ||
RESOURCE_TYPE_ATTRIBUTE_KEY, | ||
"GCP_GKE_CONTAINER", | ||
RESOURCE_LABEL_ATTRIBUTE_KEY, | ||
"gcp_account=account3,instance_id=instance3,location=us-west4," | ||
+ "cluster_name=cluster,container_name=container"); | ||
} | ||
} |