From ebb319a02dc418054c3b265965e8a7ecf8fe4acf Mon Sep 17 00:00:00 2001 From: Maksym Ochenashko Date: Sat, 10 Aug 2024 12:19:54 +0300 Subject: [PATCH] sdk-common: add `ProcessRuntimeDetector` --- docs/sdk/configuration.md | 2 +- .../otel4s/sdk/OpenTelemetrySdk.scala | 2 + .../typelevel/otel4s/sdk/resource/OS.scala | 5 ++ .../otel4s/sdk/resource/Process.scala | 38 +++++++++++++ .../ProcessRuntimeDetectorPlatform.scala | 44 +++++++++++++++ .../ProcessRuntimeDetectorPlatform.scala | 56 +++++++++++++++++++ .../ProcessRuntimeDetectorPlatform.scala | 45 +++++++++++++++ .../TelemetryResourceAutoConfigure.scala | 7 ++- .../sdk/resource/ProcessRuntimeDetector.scala | 43 ++++++++++++++ .../resource/TelemetryResourceDetector.scala | 3 +- .../TelemetryResourceAutoConfigureSuite.scala | 12 +++- .../TelemetryResourceDetectorSuite.scala | 23 +++++++- .../otel4s/sdk/metrics/SdkMetrics.scala | 2 + .../otel4s/sdk/trace/SdkTraces.scala | 2 + 14 files changed, 276 insertions(+), 8 deletions(-) create mode 100644 sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/Process.scala create mode 100644 sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala create mode 100644 sdk/common/jvm/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala create mode 100644 sdk/common/native/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala create mode 100644 sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetector.scala diff --git a/docs/sdk/configuration.md b/docs/sdk/configuration.md index 406646709..e21618b1d 100644 --- a/docs/sdk/configuration.md +++ b/docs/sdk/configuration.md @@ -50,7 +50,7 @@ If not specified, SDK defaults the service name to `unknown_service:scala`. | otel.resource.attributes | OTEL\\_RESOURCE\\_ATTRIBUTES | Specify resource attributes in the following format: `key1=val1,key2=val2,key3=val3`. | | otel.service.name | OTEL\\_SERVICE\\_NAME | Specify logical service name. Takes precedence over `service.name` defined with `otel.resource.attributes`. | | otel.experimental.resource.disabled-keys | OTEL\\_EXPERIMENTAL\\_RESOURCE\\_DISABLED\\_KEYS | Specify resource attribute keys that are filtered. | -| otel.otel4s.resource.detectors | OTEL\\_OTEL4S\\_RESOURCE\\_DETECTORS | Specify resource detectors to use. Defaults to `host,os`. | +| otel.otel4s.resource.detectors | OTEL\\_OTEL4S\\_RESOURCE\\_DETECTORS | Specify resource detectors to use. Defaults to `host,os,process_runtime`. | ## Metrics diff --git a/sdk/all/src/main/scala/org/typelevel/otel4s/sdk/OpenTelemetrySdk.scala b/sdk/all/src/main/scala/org/typelevel/otel4s/sdk/OpenTelemetrySdk.scala index a373e82ae..2ad049c44 100644 --- a/sdk/all/src/main/scala/org/typelevel/otel4s/sdk/OpenTelemetrySdk.scala +++ b/sdk/all/src/main/scala/org/typelevel/otel4s/sdk/OpenTelemetrySdk.scala @@ -198,6 +198,8 @@ object OpenTelemetrySdk { * By default, the following detectors are enabled: * - host: `host.arch`, `host.name` * - os: `os.type`, `os.description` + * - process_runtime: `process.runtime.name`, + * `process.runtime.version`, `process.runtime.description` * * @param detector * the detector to add diff --git a/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/OS.scala b/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/OS.scala index 88167a160..746c326a0 100644 --- a/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/OS.scala +++ b/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/OS.scala @@ -19,6 +19,11 @@ package org.typelevel.otel4s.sdk.resource import scala.scalajs.js import scala.scalajs.js.annotation.JSImport +/** A mapping of the Node.js OS API. + * + * @see + * [[https://nodejs.org/api/os.html]] + */ private object OS { @js.native diff --git a/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/Process.scala b/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/Process.scala new file mode 100644 index 000000000..c958e602a --- /dev/null +++ b/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/Process.scala @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Typelevel + * + * 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 org.typelevel.otel4s.sdk.resource + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSImport + +/** A mapping of the Node.js process API. + * + * @see + * [[https://nodejs.org/api/process.html]] + */ +private object Process { + + @js.native + @JSImport("process", "versions") + def versions: Versions = js.native + + @js.native + trait Versions extends js.Object { + def node: String = js.native + } + +} diff --git a/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala b/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala new file mode 100644 index 000000000..3268d405b --- /dev/null +++ b/sdk/common/js/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Typelevel + * + * 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 org.typelevel.otel4s.sdk.resource + +import cats.effect.Sync +import org.typelevel.otel4s.Attributes +import org.typelevel.otel4s.sdk.TelemetryResource +import org.typelevel.otel4s.semconv.SchemaUrls + +private[resource] trait ProcessRuntimeDetectorPlatform { + self: ProcessRuntimeDetector.type => + + def apply[F[_]: Sync]: TelemetryResourceDetector[F] = + new Detector[F] + + private class Detector[F[_]: Sync] extends TelemetryResourceDetector[F] { + def name: String = Const.Name + + def detect: F[Option[TelemetryResource]] = Sync[F].delay { + val attributes = Attributes( + Keys.Name("nodejs"), + Keys.Version(Process.versions.node), + Keys.Description("Node.js") + ) + + Some(TelemetryResource(attributes, Some(SchemaUrls.Current))) + } + } + +} diff --git a/sdk/common/jvm/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala b/sdk/common/jvm/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala new file mode 100644 index 000000000..384feeaba --- /dev/null +++ b/sdk/common/jvm/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Typelevel + * + * 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 org.typelevel.otel4s.sdk.resource + +import cats.effect.Sync +import cats.syntax.apply._ +import cats.syntax.flatMap._ +import cats.syntax.functor._ +import org.typelevel.otel4s.Attributes +import org.typelevel.otel4s.sdk.TelemetryResource +import org.typelevel.otel4s.semconv.SchemaUrls + +private[resource] trait ProcessRuntimeDetectorPlatform { + self: ProcessRuntimeDetector.type => + + def apply[F[_]: Sync]: TelemetryResourceDetector[F] = + new Detector[F] + + private class Detector[F[_]: Sync] extends TelemetryResourceDetector[F] { + def name: String = Const.Name + + def detect: F[Option[TelemetryResource]] = + for { + runtimeName <- Sync[F].delay(sys.props.get("java.runtime.name")) + runtimeVersion <- Sync[F].delay(sys.props.get("java.runtime.version")) + vmVendor <- Sync[F].delay(sys.props.get("java.vm.vendor")) + vmName <- Sync[F].delay(sys.props.get("java.vm.name")) + vmVersion <- Sync[F].delay(sys.props.get("java.vm.version")) + } yield { + val attributes = Attributes.newBuilder + + runtimeName.foreach(name => attributes.addOne(Keys.Name(name))) + runtimeVersion.foreach(name => attributes.addOne(Keys.Version(name))) + (vmVendor, vmName, vmVersion).mapN { (vendor, name, version) => + attributes.addOne(Keys.Description(s"$vendor $name $version")) + } + + Some(TelemetryResource(attributes.result(), Some(SchemaUrls.Current))) + } + } + +} diff --git a/sdk/common/native/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala b/sdk/common/native/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala new file mode 100644 index 000000000..0b05d5eb4 --- /dev/null +++ b/sdk/common/native/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetectorPlatform.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2023 Typelevel + * + * 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 org.typelevel.otel4s.sdk.resource + +import cats.effect.Sync +import org.typelevel.otel4s.Attributes +import org.typelevel.otel4s.sdk.TelemetryResource +import org.typelevel.otel4s.semconv.SchemaUrls + +private[resource] trait ProcessRuntimeDetectorPlatform { + self: ProcessRuntimeDetector.type => + + def apply[F[_]: Sync]: TelemetryResourceDetector[F] = + new Detector[F] + + private class Detector[F[_]: Sync] extends TelemetryResourceDetector[F] { + def name: String = Const.Name + + def detect: F[Option[TelemetryResource]] = + Sync[F].delay { + val attributes = Attributes( + Keys.Name("scalanative"), + // Keys.Version(""), not available yet + Keys.Description("Scala Native") + ) + + Some(TelemetryResource(attributes, Some(SchemaUrls.Current))) + } + } + +} diff --git a/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigure.scala b/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigure.scala index 6ed11c444..76a6e221b 100644 --- a/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigure.scala +++ b/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigure.scala @@ -40,7 +40,7 @@ import java.nio.charset.StandardCharsets * | otel.resource.attributes | OTEL_RESOURCE_ATTRIBUTES | Specify resource attributes in the following format: key1=val1,key2=val2,key3=val3 | * | otel.service.name | OTEL_SERVICE_NAME | Specify logical service name. Takes precedence over `service.name` defined with `otel.resource.attributes` | * | otel.experimental.resource.disabled-keys | OTEL_EXPERIMENTAL_RESOURCE_DISABLED_KEYS | Specify resource attribute keys that are filtered. | - * | otel.otel4s.resource.detectors | OTEL_OTEL4S_RESOURCE_DETECTORS | Specify resource detectors to use. Defaults to `host,os`. | + * | otel.otel4s.resource.detectors | OTEL_OTEL4S_RESOURCE_DETECTORS | Specify resource detectors to use. Defaults to `host,os,process_runtime`. | * }}} * * @see @@ -205,7 +205,8 @@ private[sdk] object TelemetryResourceAutoConfigure { private object Defaults { val Detectors: Set[String] = Set( HostDetector.Const.Name, - OSDetector.Const.Name + OSDetector.Const.Name, + ProcessRuntimeDetector.Const.Name ) } @@ -218,7 +219,7 @@ private[sdk] object TelemetryResourceAutoConfigure { * | otel.resource.attributes | OTEL_RESOURCE_ATTRIBUTES | Specify resource attributes in the following format: key1=val1,key2=val2,key3=val3 | * | otel.service.name | OTEL_SERVICE_NAME | Specify logical service name. Takes precedence over `service.name` defined with `otel.resource.attributes` | * | otel.experimental.resource.disabled-keys | OTEL_EXPERIMENTAL_RESOURCE_DISABLED_KEYS | Specify resource attribute keys that are filtered. | - * | otel.otel4s.resource.detectors | OTEL_OTEL4S_RESOURCE_DETECTORS | Specify resource detectors to use. Defaults to `host,os`. | + * | otel.otel4s.resource.detectors | OTEL_OTEL4S_RESOURCE_DETECTORS | Specify resource detectors to use. Defaults to `host,os,process_runtime`. | * }}} * * @see diff --git a/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetector.scala b/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetector.scala new file mode 100644 index 000000000..d38aeefc9 --- /dev/null +++ b/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/ProcessRuntimeDetector.scala @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Typelevel + * + * 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 org.typelevel.otel4s.sdk.resource + +import org.typelevel.otel4s.AttributeKey + +/** Detects runtime details such as name, version, and description. + * + * @see + * https://opentelemetry.io/docs/specs/semconv/resource/process/#process-runtimes + */ +object ProcessRuntimeDetector extends ProcessRuntimeDetectorPlatform { + + private[sdk] object Const { + val Name = "process_runtime" + } + + private[resource] object Keys { + val Name: AttributeKey[String] = + AttributeKey("process.runtime.name") + + val Version: AttributeKey[String] = + AttributeKey("process.runtime.version") + + val Description: AttributeKey[String] = + AttributeKey("process.runtime.description") + } + +} diff --git a/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetector.scala b/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetector.scala index 917a8467c..15f97555a 100644 --- a/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetector.scala +++ b/sdk/common/shared/src/main/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetector.scala @@ -54,11 +54,12 @@ object TelemetryResourceDetector { * Includes: * - host detector * - os detector + * - process runtime detector * * @tparam F * the higher-kinded type of a polymorphic effect */ def default[F[_]: Sync]: Set[TelemetryResourceDetector[F]] = - Set(HostDetector[F], OSDetector[F]) + Set(HostDetector[F], OSDetector[F], ProcessRuntimeDetector[F]) } diff --git a/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigureSuite.scala b/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigureSuite.scala index cd6d719d0..3c5fffe54 100644 --- a/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigureSuite.scala +++ b/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/autoconfigure/TelemetryResourceAutoConfigureSuite.scala @@ -20,6 +20,7 @@ package autoconfigure import cats.effect.IO import munit.CatsEffectSuite +import munit.internal.PlatformCompat import org.typelevel.otel4s.sdk.resource.TelemetryResourceDetector class TelemetryResourceAutoConfigureSuite extends CatsEffectSuite { @@ -70,6 +71,15 @@ class TelemetryResourceAutoConfigureSuite extends CatsEffectSuite { val host = Set("host.arch", "host.name") val os = Set("os.type", "os.description") + val runtime = { + val name = "process.runtime.name" + val version = "process.runtime.version" + val description = "process.runtime.description" + + if (PlatformCompat.isNative) Set(name, description) + else Set(name, version, description) + } + val telemetry = Set( "telemetry.sdk.language", "telemetry.sdk.name", @@ -77,7 +87,7 @@ class TelemetryResourceAutoConfigureSuite extends CatsEffectSuite { ) val all = - host ++ os ++ service ++ telemetry + host ++ os ++ runtime ++ service ++ telemetry IO(assertEquals(resource.attributes.map(_.key.name).toSet, all)) } diff --git a/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetectorSuite.scala b/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetectorSuite.scala index 7af3062f5..a8b252b69 100644 --- a/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetectorSuite.scala +++ b/sdk/common/shared/src/test/scala/org/typelevel/otel4s/sdk/resource/TelemetryResourceDetectorSuite.scala @@ -18,6 +18,7 @@ package org.typelevel.otel4s.sdk.resource import cats.effect.IO import munit.CatsEffectSuite +import munit.internal.PlatformCompat import org.typelevel.otel4s.semconv.SchemaUrls class TelemetryResourceDetectorSuite extends CatsEffectSuite { @@ -44,9 +45,27 @@ class TelemetryResourceDetectorSuite extends CatsEffectSuite { } } - test("default - contain host, os detectors") { + test("ProcessRuntimeDetector - detect name, version, and description") { + val keys = { + val name = "process.runtime.name" + val version = "process.runtime.version" + val description = "process.runtime.description" + + if (PlatformCompat.isNative) Set(name, description) + else Set(name, version, description) + } + + for { + resource <- ProcessRuntimeDetector[IO].detect + } yield { + assertEquals(resource.map(_.attributes.map(_.key.name).toSet), Some(keys)) + assertEquals(resource.flatMap(_.schemaUrl), Some(SchemaUrls.Current)) + } + } + + test("default - contain host, os, process_runtime detectors") { val detectors = TelemetryResourceDetector.default[IO].map(_.name) - val expected = Set("host", "os") + val expected = Set("host", "os", "process_runtime") assertEquals(detectors.map(_.name), expected) } diff --git a/sdk/metrics/src/main/scala/org/typelevel/otel4s/sdk/metrics/SdkMetrics.scala b/sdk/metrics/src/main/scala/org/typelevel/otel4s/sdk/metrics/SdkMetrics.scala index 21bd0155f..26678c788 100644 --- a/sdk/metrics/src/main/scala/org/typelevel/otel4s/sdk/metrics/SdkMetrics.scala +++ b/sdk/metrics/src/main/scala/org/typelevel/otel4s/sdk/metrics/SdkMetrics.scala @@ -143,6 +143,8 @@ object SdkMetrics { * By default, the following detectors are enabled: * - host: `host.arch`, `host.name` * - os: `os.type`, `os.description` + * - process_runtime: `process.runtime.name`, + * `process.runtime.version`, `process.runtime.description` * * @param detector * the detector to add diff --git a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraces.scala b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraces.scala index ae90c9287..7d26117da 100644 --- a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraces.scala +++ b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraces.scala @@ -164,6 +164,8 @@ object SdkTraces { * By default, the following detectors are enabled: * - host: `host.arch`, `host.name` * - os: `os.type`, `os.description` + * - process_runtime: `process.runtime.name`, + * `process.runtime.version`, `process.runtime.description` * * @param detector * the detector to add