diff --git a/gcp-observability/src/main/java/io/grpc/gcp/observability/GcpObservability.java b/gcp-observability/src/main/java/io/grpc/gcp/observability/GcpObservability.java index 4bb9ffcb0c1..1eb243a7940 100644 --- a/gcp-observability/src/main/java/io/grpc/gcp/observability/GcpObservability.java +++ b/gcp-observability/src/main/java/io/grpc/gcp/observability/GcpObservability.java @@ -66,10 +66,10 @@ public final class GcpObservability implements AutoCloseable { */ public static synchronized GcpObservability grpcInit() throws IOException { if (instance == null) { - GlobalLoggingTags globalLoggingTags = new GlobalLoggingTags(); + GlobalLocationTags globalLocationTags = new GlobalLocationTags(); ObservabilityConfigImpl observabilityConfig = ObservabilityConfigImpl.getInstance(); Sink sink = new GcpLogSink(observabilityConfig.getDestinationProjectId(), - globalLoggingTags.getLocationTags(), globalLoggingTags.getCustomTags(), + globalLocationTags.getLocationTags(), observabilityConfig.getCustomTags(), observabilityConfig.getFlushMessageCount()); // TODO(dnvindhya): Cleanup code for LoggingChannelProvider and LoggingServerProvider // once ChannelBuilder and ServerBuilder are used diff --git a/gcp-observability/src/main/java/io/grpc/gcp/observability/GlobalLoggingTags.java b/gcp-observability/src/main/java/io/grpc/gcp/observability/GlobalLocationTags.java similarity index 71% rename from gcp-observability/src/main/java/io/grpc/gcp/observability/GlobalLoggingTags.java rename to gcp-observability/src/main/java/io/grpc/gcp/observability/GlobalLocationTags.java index 208feb998d0..045671814f0 100644 --- a/gcp-observability/src/main/java/io/grpc/gcp/observability/GlobalLoggingTags.java +++ b/gcp-observability/src/main/java/io/grpc/gcp/observability/GlobalLocationTags.java @@ -16,8 +16,6 @@ package io.grpc.gcp.observability; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.util.Strings; @@ -35,20 +33,16 @@ import java.util.logging.Level; import java.util.logging.Logger; -/** A container of all global custom tags used for logging (for now). */ -final class GlobalLoggingTags { - private static final Logger logger = Logger.getLogger(GlobalLoggingTags.class.getName()); +/** A container of all global location tags used for observability. */ +final class GlobalLocationTags { + private static final Logger logger = Logger.getLogger(GlobalLocationTags.class.getName()); - private static final String ENV_KEY_PREFIX = "GRPC_OBSERVABILITY_"; private final Map locationTags; - private final Map customTags; - GlobalLoggingTags() { + GlobalLocationTags() { ImmutableMap.Builder locationTagsBuilder = ImmutableMap.builder(); - ImmutableMap.Builder customTagsBuilder = ImmutableMap.builder(); - populate(locationTagsBuilder, customTagsBuilder); + populate(locationTagsBuilder); locationTags = locationTagsBuilder.buildOrThrow(); - customTags = customTagsBuilder.buildOrThrow(); } private static String applyTrim(String value) { @@ -62,40 +56,36 @@ Map getLocationTags() { return locationTags; } - Map getCustomTags() { - return customTags; - } - @VisibleForTesting - static void populateFromMetadataServer(ImmutableMap.Builder customTags) { + static void populateFromMetadataServer(ImmutableMap.Builder locationTags) { MetadataConfig metadataConfig = new MetadataConfig(new DefaultHttpTransportFactory()); metadataConfig.init(); - customTags.putAll(metadataConfig.getAllValues()); + locationTags.putAll(metadataConfig.getAllValues()); } @VisibleForTesting - static void populateFromKubernetesValues(ImmutableMap.Builder customTags, + static void populateFromKubernetesValues(ImmutableMap.Builder locationTags, String namespaceFile, String hostnameFile, String cgroupFile) { // namespace name: contents of file /var/run/secrets/kubernetes.io/serviceaccount/namespace - populateFromFileContents(customTags, "namespace_name", - namespaceFile, GlobalLoggingTags::applyTrim); + populateFromFileContents(locationTags, "namespace_name", + namespaceFile, GlobalLocationTags::applyTrim); // pod_name: hostname i.e. contents of /etc/hostname - populateFromFileContents(customTags, "pod_name", hostnameFile, - GlobalLoggingTags::applyTrim); + populateFromFileContents(locationTags, "pod_name", hostnameFile, + GlobalLocationTags::applyTrim); // container_id: parsed from /proc/self/cgroup . Note: only works for Linux-based containers - populateFromFileContents(customTags, "container_id", cgroupFile, + populateFromFileContents(locationTags, "container_id", cgroupFile, (value) -> getContainerIdFromFileContents(value)); } @VisibleForTesting - static void populateFromFileContents(ImmutableMap.Builder customTags, String key, - String filePath, Function parser) { + static void populateFromFileContents(ImmutableMap.Builder locationTags, + String key, String filePath, Function parser) { String value = parser.apply(readFileContents(filePath)); if (value != null) { - customTags.put(key, value); + locationTags.put(key, value); } } @@ -139,25 +129,7 @@ private static String readFileContents(String file) { return null; } - private static void populateFromEnvironmentVars(ImmutableMap.Builder customTags) { - populateFromMap(System.getenv(), customTags); - } - - @VisibleForTesting - static void populateFromMap(Map map, - final ImmutableMap.Builder customTags) { - checkNotNull(map); - map.forEach((k, v) -> { - if (k.startsWith(ENV_KEY_PREFIX)) { - String customTagKey = k.substring(ENV_KEY_PREFIX.length()); - customTags.put(customTagKey, v); - } - }); - } - - static void populate(ImmutableMap.Builder locationTags, - ImmutableMap.Builder customTags) { - populateFromEnvironmentVars(customTags); + static void populate(ImmutableMap.Builder locationTags) { populateFromMetadataServer(locationTags); populateFromKubernetesValues(locationTags, "/var/run/secrets/kubernetes.io/serviceaccount/namespace", diff --git a/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfig.java b/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfig.java index a6abe589eed..48dd480973b 100644 --- a/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfig.java +++ b/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfig.java @@ -20,6 +20,7 @@ import io.grpc.observabilitylog.v1.GrpcLogRecord.EventType; import io.opencensus.trace.Sampler; import java.util.List; +import java.util.Map; @Internal public interface ObservabilityConfig { @@ -47,6 +48,9 @@ public interface ObservabilityConfig { /** Get sampler for TraceConfig - when Cloud Tracing is enabled. */ Sampler getSampler(); + /** Map of all custom tags used for logging, metrics and traces. */ + Map getCustomTags(); + /** * POJO for representing a filter used in configuration. */ diff --git a/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfigImpl.java b/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfigImpl.java index 2f547ec5af7..bcad0851562 100644 --- a/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfigImpl.java +++ b/gcp-observability/src/main/java/io/grpc/gcp/observability/ObservabilityConfigImpl.java @@ -20,6 +20,7 @@ import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import io.grpc.internal.JsonParser; import io.grpc.internal.JsonUtil; import io.grpc.observabilitylog.v1.GrpcLogRecord.EventType; @@ -48,6 +49,7 @@ final class ObservabilityConfigImpl implements ObservabilityConfig { private List logFilters; private List eventTypes; private Sampler sampler; + private Map customTags; static ObservabilityConfigImpl getInstance() throws IOException { ObservabilityConfigImpl config = new ObservabilityConfigImpl(); @@ -120,6 +122,17 @@ private void parseConfig(Map config) { this.sampler = Samplers.probabilitySampler(samplingRate); } } + Map rawCustomTags = JsonUtil.getObject(config, "custom_tags"); + if (rawCustomTags != null) { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + for (Map.Entry entry: rawCustomTags.entrySet()) { + checkArgument( + entry.getValue() instanceof String, + "'custom_tags' needs to be a map of "); + builder.put(entry.getKey(), (String) entry.getValue()); + } + customTags = builder.build(); + } } } @@ -191,4 +204,9 @@ public List getEventTypes() { public Sampler getSampler() { return sampler; } + + @Override + public Map getCustomTags() { + return customTags; + } } diff --git a/gcp-observability/src/test/java/io/grpc/gcp/observability/GlobalLoggingTagsTest.java b/gcp-observability/src/test/java/io/grpc/gcp/observability/GlobalLocationTagsTest.java similarity index 78% rename from gcp-observability/src/test/java/io/grpc/gcp/observability/GlobalLoggingTagsTest.java rename to gcp-observability/src/test/java/io/grpc/gcp/observability/GlobalLocationTagsTest.java index e2146d45027..86feb1aaec6 100644 --- a/gcp-observability/src/test/java/io/grpc/gcp/observability/GlobalLoggingTagsTest.java +++ b/gcp-observability/src/test/java/io/grpc/gcp/observability/GlobalLocationTagsTest.java @@ -30,7 +30,7 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) -public class GlobalLoggingTagsTest { +public class GlobalLocationTagsTest { private static String FILE_CONTENTS = "12:perf_event:/kubepods/burstable/podc43b6442-0725-4fb8-bb1c-d17f5122155c/" + "fe61ca6482b58f4a9831d08d6ea15db25f9fd19b4be19a54df8c6c0eab8742b7\n" @@ -51,24 +51,15 @@ public class GlobalLoggingTagsTest { @Rule public TemporaryFolder hostnameFolder = new TemporaryFolder(); @Rule public TemporaryFolder cgroupFolder = new TemporaryFolder(); - @Test - public void testPopulateFromMap() { - ImmutableMap.Builder customTags = ImmutableMap.builder(); - GlobalLoggingTags.populateFromMap( - ImmutableMap.of("GRPC_OBSERVABILITY_KEY1", "VALUE1", "ANOTHER_KEY2", "VALUE2", - "GRPC_OBSERVABILITY_KEY3", "VALUE3"), customTags); - assertThat(customTags.buildOrThrow()).containsExactly("KEY1", "VALUE1", "KEY3", "VALUE3"); - } - @Test public void testContainerIdParsing_lastLine() { - String containerId = GlobalLoggingTags.getContainerIdFromFileContents(FILE_CONTENTS_LAST_LINE); + String containerId = GlobalLocationTags.getContainerIdFromFileContents(FILE_CONTENTS_LAST_LINE); assertThat(containerId).isEqualTo("e19a54df"); } @Test public void testContainerIdParsing_fewerFields_notFound() { - String containerId = GlobalLoggingTags.getContainerIdFromFileContents( + String containerId = GlobalLocationTags.getContainerIdFromFileContents( "12:/kubepods/burstable/podc43b6442-0725-4fb8-bb1c-d17f5122155c/" + "fe61ca6482b58f4a9831d08d6ea15db25f9fd19b4be19a54df8c6c0eab8742b7\n"); assertThat(containerId).isNull(); @@ -76,7 +67,7 @@ public void testContainerIdParsing_fewerFields_notFound() { @Test public void testContainerIdParsing_fewerPaths_notFound() { - String containerId = GlobalLoggingTags.getContainerIdFromFileContents( + String containerId = GlobalLocationTags.getContainerIdFromFileContents( "12:xdf:/kubepods/podc43b6442-0725-4fb8-bb1c-d17f5122155c/" + "fe61ca6482b58f4a9831d08d6ea15db25f9fd19b4be19a54df8c6c0eab8742b7\n"); assertThat(containerId).isNull(); @@ -92,10 +83,10 @@ public void testPopulateKubernetesValues() throws IOException { Files.write("test-hostname2\n".getBytes(StandardCharsets.UTF_8), hostnameFile); Files.write(FILE_CONTENTS.getBytes(StandardCharsets.UTF_8), cgroupFile); - ImmutableMap.Builder customTags = ImmutableMap.builder(); - GlobalLoggingTags.populateFromKubernetesValues(customTags, namespaceFile.getAbsolutePath(), + ImmutableMap.Builder locationTags = ImmutableMap.builder(); + GlobalLocationTags.populateFromKubernetesValues(locationTags, namespaceFile.getAbsolutePath(), hostnameFile.getAbsolutePath(), cgroupFile.getAbsolutePath()); - assertThat(customTags.buildOrThrow()).containsExactly("container_id", + assertThat(locationTags.buildOrThrow()).containsExactly("container_id", "fe61ca6482b58f4a9831d08d6ea15db25f9fd19b4be19a54df8c6c0eab8742b7", "namespace_name", "test-namespace1", "pod_name", "test-hostname2"); } @@ -109,10 +100,10 @@ public void testNonKubernetesInstanceValues() throws IOException { Files.write("test-hostname2\n".getBytes(StandardCharsets.UTF_8), hostnameFile); Files.write(FILE_CONTENTS.getBytes(StandardCharsets.UTF_8), cgroupFile); - ImmutableMap.Builder customTags = ImmutableMap.builder(); - GlobalLoggingTags.populateFromKubernetesValues(customTags, + ImmutableMap.Builder locationTags = ImmutableMap.builder(); + GlobalLocationTags.populateFromKubernetesValues(locationTags, namespaceFilePath, hostnameFile.getAbsolutePath(), cgroupFile.getAbsolutePath()); - assertThat(customTags.buildOrThrow()).containsExactly("container_id", + assertThat(locationTags.buildOrThrow()).containsExactly("container_id", "fe61ca6482b58f4a9831d08d6ea15db25f9fd19b4be19a54df8c6c0eab8742b7", "pod_name", "test-hostname2"); } diff --git a/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java b/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java index f608dd5b25a..e065d7f29a4 100644 --- a/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java +++ b/gcp-observability/src/test/java/io/grpc/gcp/observability/ObservabilityConfigImplTest.java @@ -33,6 +33,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; +import java.util.Map; import org.junit.Rule; import org.junit.Test; @@ -106,6 +107,23 @@ public class ObservabilityConfigImplTest { + " \"global_trace_sampling_rate\": -0.75\n" + "}"; + private static final String CUSTOM_TAGS = "{\n" + + " \"enable_cloud_logging\": true,\n" + + " \"custom_tags\": {\n" + + " \"SOURCE_VERSION\" : \"J2e1Cf\",\n" + + " \"SERVICE_NAME\" : \"payment-service\",\n" + + " \"ENTRYPOINT_SCRIPT\" : \"entrypoint.sh\"\n" + + " }\n" + + "}"; + + private static final String BAD_CUSTOM_TAGS = "{\n" + + " \"enable_cloud_monitoring\": true,\n" + + " \"custom_tags\": {\n" + + " \"SOURCE_VERSION\" : \"J2e1Cf\",\n" + + " \"SERVICE_NAME\" : { \"SUB_SERVICE_NAME\" : \"payment-service\"},\n" + + " \"ENTRYPOINT_SCRIPT\" : \"entrypoint.sh\"\n" + + " }\n" + + "}"; ObservabilityConfigImpl observabilityConfig = new ObservabilityConfigImpl(); @@ -257,4 +275,26 @@ public void configFileLogFilters() throws Exception { assertThat(logFilters.get(1).headerBytes).isNull(); assertThat(logFilters.get(1).messageBytes).isNull(); } + + @Test + public void customTags() throws IOException { + observabilityConfig.parse(CUSTOM_TAGS); + assertTrue(observabilityConfig.isEnableCloudLogging()); + Map customTags = observabilityConfig.getCustomTags(); + assertThat(customTags).hasSize(3); + assertThat(customTags).containsEntry("SOURCE_VERSION", "J2e1Cf"); + assertThat(customTags).containsEntry("SERVICE_NAME", "payment-service"); + assertThat(customTags).containsEntry("ENTRYPOINT_SCRIPT", "entrypoint.sh"); + } + + @Test + public void badCustomTags() throws IOException { + try { + observabilityConfig.parse(BAD_CUSTOM_TAGS); + fail("exception expected!"); + } catch (IllegalArgumentException iae) { + assertThat(iae.getMessage()).isEqualTo( + "'custom_tags' needs to be a map of "); + } + } } \ No newline at end of file