diff options
author | Yang Song <songy23@users.noreply.github.com> | 2018-09-26 10:29:35 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-26 10:29:35 -0700 |
commit | 9580d5f56a6bd14751632a1d4af8a8b0d259ac0e (patch) | |
tree | ba37de3886b4716307c8bd48ae57476fde237abe | |
parent | 8ca454685adeede687b4857270c590f642d3c415 (diff) | |
download | opencensus-java-9580d5f56a6bd14751632a1d4af8a8b0d259ac0e.tar.gz |
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.
7 files changed, 314 insertions, 4 deletions
diff --git a/RELEASING.md b/RELEASING.md index e4315cd2..649ac81f 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -65,6 +65,7 @@ token](https://help.github.com/articles/creating-a-personal-access-token-for-the examples/build.gradle examples/pom.xml api/src/main/java/io/opencensus/common/OpenCensusLibraryInformation.java + exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java ) $ git checkout -b v$MAJOR.$MINOR.x master $ git push upstream v$MAJOR.$MINOR.x diff --git a/buildscripts/import-control.xml b/buildscripts/import-control.xml index b4c7410b..002820d8 100644 --- a/buildscripts/import-control.xml +++ b/buildscripts/import-control.xml @@ -170,6 +170,7 @@ General guidelines on imports: </subpackage> <subpackage name="ocagent"> <allow pkg="com.google.protobuf"/> + <allow pkg="io.opencensus.contrib.monitoredresource.util"/> <allow pkg="io.opencensus.contrib.opencensus.proto.util"/> <allow pkg="io.opencensus.exporter.trace.ocagent"/> <allow pkg="io.opencensus.proto"/> diff --git a/exporters/trace/ocagent/build.gradle b/exporters/trace/ocagent/build.gradle index 7214eb58..b9489864 100644 --- a/exporters/trace/ocagent/build.gradle +++ b/exporters/trace/ocagent/build.gradle @@ -15,5 +15,4 @@ dependencies { testCompile project(':opencensus-api') signature "org.codehaus.mojo.signature:java17:1.0@signature" - signature "net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature" } diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java new file mode 100644 index 00000000..65729803 --- /dev/null +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java @@ -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() {} +} diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterHandler.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterHandler.java index 27e56c66..cb4b06aa 100644 --- a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterHandler.java +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterHandler.java @@ -28,7 +28,7 @@ final class OcAgentTraceExporterHandler extends Handler { private static final String DEFAULT_SERVICE_NAME = "OpenCensus"; // private final String endPoint; - // private final String serviceName; + // private final Node node; // private final boolean useInsecure; OcAgentTraceExporterHandler() { @@ -38,7 +38,10 @@ final class OcAgentTraceExporterHandler extends Handler { OcAgentTraceExporterHandler( @Nullable String endPoint, @Nullable String serviceName, @Nullable Boolean useInsecure) { // this.endPoint = endPoint == null ? DEFAULT_END_POINT : endPoint; - // this.serviceName = serviceName == null ? DEFAULT_SERVICE_NAME : serviceName; + // if (serviceName == null) { + // serviceName = DEFAULT_SERVICE_NAME; + // } + // this.node = OcAgentNodeUtils.getNodeInfo(serviceName); // this.useInsecure = useInsecure == null ? false : useInsecure; } diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtils.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtils.java index 26bae0ac..df11ef58 100644 --- a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtils.java +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtils.java @@ -283,7 +283,7 @@ public final class TraceProtoUtils { return TruncatableString.newBuilder().setValue(string).setTruncatedByteCount(0).build(); } - private static com.google.protobuf.Timestamp toTimestampProto(Timestamp timestamp) { + static com.google.protobuf.Timestamp toTimestampProto(Timestamp timestamp) { return com.google.protobuf.Timestamp.newBuilder() .setSeconds(timestamp.getSeconds()) .setNanos(timestamp.getNanos()) diff --git a/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtilsTest.java b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtilsTest.java new file mode 100644 index 00000000..813066bc --- /dev/null +++ b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtilsTest.java @@ -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 = "54321@my.org"; + 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"); + } +} |