diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 282826fb2..e99c93fec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,11 +92,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target project/target + run: mkdir -p semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target project/target + run: tar cf targets.tar semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') diff --git a/build.sbt b/build.sbt index 8cbb10944..86457d1e4 100644 --- a/build.sbt +++ b/build.sbt @@ -118,6 +118,8 @@ lazy val root = tlCrossRootProject oteljava, `semconv-stable`, `semconv-experimental`, + `semconv-metrics-stable`, + `semconv-metrics-experimental`, benchmarks, examples, unidocs @@ -584,6 +586,10 @@ lazy val oteljava = project .settings(munitDependencies) .settings(scalafixSettings) +// +// Semantic conventions +// + lazy val `semconv-stable` = crossProject(JVMPlatform, JSPlatform, NativePlatform) .crossType(CrossType.Pure) @@ -621,6 +627,37 @@ lazy val `semconv-experimental` = .settings(munitDependencies) .settings(scalafixSettings) +lazy val `semconv-metrics-stable` = + crossProject(JVMPlatform, JSPlatform, NativePlatform) + .crossType(CrossType.Pure) + .in(file("semconv/metrics/stable")) + .dependsOn(`core-metrics`) + .settings( + name := "otel4s-semconv-metrics", + startYear := Some(2024), + description := "Stable semantic metrics.", + ) + .settings(munitDependencies) + .settings(scalafixSettings) + +lazy val `semconv-metrics-experimental` = + crossProject(JVMPlatform, JSPlatform, NativePlatform) + .crossType(CrossType.Pure) + .in(file("semconv/metrics/experimental")) + .dependsOn(`core-metrics`, `semconv-metrics-stable`) + .settings( + name := "otel4s-semconv-metrics-experimental", + startYear := Some(2024), + description := "Experimental (incubating) semantic metrics. Breaking changes expected. Library instrumentation SHOULD NOT depend on this.", + mimaPreviousArtifacts := Set.empty + ) + .settings(munitDependencies) + .settings(scalafixSettings) + +// +// +// + lazy val scalafix = tlScalafixProject .rulesSettings( name := "otel4s-scalafix", @@ -775,6 +812,8 @@ lazy val unidocs = project `oteljava-testkit`, oteljava, `semconv-stable`.jvm, - `semconv-experimental`.jvm + `semconv-experimental`.jvm, + `semconv-metrics-stable`.jvm, + `semconv-metrics-experimental`.jvm ) ) diff --git a/buildscripts/semantic-convention/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 b/buildscripts/semantic-convention/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 new file mode 100644 index 000000000..0fac3d7bb --- /dev/null +++ b/buildscripts/semantic-convention/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 @@ -0,0 +1,129 @@ +{%- macro to_scala_key_type(attribute) -%} + {{ attribute.type | instantiated_type | map_text("scala_key_type") }} +{%- endmacro %} + +{%- macro instrumentType(type) -%} +{%- if type == "gauge" -%}Gauge[F, Long] +{%- elif type == "counter" -%}Counter[F, Long] +{%- elif type == "updowncounter" -%}UpDownCounter[F, Long] +{%- elif type == "histogram" -%}Histogram[F, Double] +{%- else %}{{ type }} +{%- endif -%} +{%- endmacro -%} + +{%- macro instrumentFactory(type) -%} +{%- if type == "gauge" -%}gauge[Long] +{%- elif type == "counter" -%}counter[Long] +{%- elif type == "updowncounter" -%}upDownCounter[Long] +{%- elif type == "histogram" -%}histogram[Double] +{%- else %}{{ type }} +{%- endif -%} +{%- endmacro -%} + +{%- macro requirement(attribute) -%} +{%- if attribute.requirement_level is mapping -%} +{%- set level = attribute.requirement_level | first -%} +{%- set apply = "(\"" ~ attribute.requirement_level[level] | trim ~ "\")" -%} +{%- else -%} +{%- set level = attribute.requirement_level -%} +{%- set apply = "" -%} +{%- endif -%} +{%- if level == "required" -%} Requirement.required{{ apply }} +{%- elif level == "recommended" -%} Requirement.recommended{{ apply }} +{%- elif level == "conditionally_required" -%} Requirement.conditionallyRequired{{ apply }} +{%- elif level == "opt_in" -%} Requirement.optIn{{ apply }} +{%- else -%} _unknown_requirement_level_{{ attribute.requirement_level }} +{%- endif -%} +{%- endmacro -%} + +{%- macro stability(type) -%} +{%- if type == "experimental" -%} Stability.experimental +{%- elif type == "stable" -%} Stability.stable +{%- else -%} _unknown_stability_type_{{ type }} +{%- endif -%} +{%- endmacro -%} + +{%- macro exampleValue(type, input) %} +{%- if type == "int" -%} {{ input }} +{%- elif type == "double" -%} {{ input }} +{%- elif type == "boolean" -%} {{ input }} +{%- elif type == "string" -%} "{{ input }}" +{%- else -%} _unknown type_{{ input }} +{%- endif -%} +{% endmacro %} + +{%- set object_name = ctx.root_namespace | pascal_case ~ params.object_prefix ~ "Metrics" -%} + +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object {{ object_name }} { + + {% for metric in ctx.metrics | sort(attribute='metric_name') %} + {{ [metric.brief | replace('$', "$$"), concat_if("\n\n@note\n\n", metric.note)] | comment(indent=2) }} + {%- if metric is deprecated %} + @deprecated("{{ metric.deprecated }}", "") + {%- endif %} + object {{ metric.metric_name[ctx.root_namespace | length:] | pascal_case }} { + + val Name = "{{ metric.metric_name }}" + val Description = "{{ metric.brief }}" + val Unit = "{{ metric.unit }}" + {% if metric.attributes | length > 0 %} + object AttributeSpecs { + {% for attribute in metric.attributes | sort(attribute='name') %} + {{ [attribute.brief | replace('$', "$$"), concat_if("\n\n@note\n\n", attribute.note)] | comment(indent=6) }} + {%- if attribute is deprecated %} + @deprecated("{{ attribute.deprecated }}", "") + {%- endif %} + val {{ attribute.name | camel_case }}: AttributeSpec[{{to_scala_key_type(attribute)}}] = + AttributeSpec( + AttributeKey("{{attribute.name}}"), + List( + {% for example in attribute.examples -%} {{ exampleValue(attribute.type | instantiated_type, example) }}, {% endfor %} + ), + {{ requirement(attribute) }}, + {{ stability(attribute.stability) }} + ) + {% endfor %} + val specs: List[AttributeSpec[_]] = + List({%- for attribute in metric.attributes | sort(attribute='name') %} + {{ attribute.name | camel_case }},{% endfor %} + ) + } + {% endif %} + def create[F[_]: Meter]{% if metric.instrument == "histogram" %}(boundaries: BucketBoundaries){% endif %}: F[{{ instrumentType(metric.instrument) }}] = + Meter[F] + .{{ instrumentFactory(metric.instrument) }}(Name) + .withDescription(Description) + .withUnit(Unit) + {%- if metric.instrument == "histogram" %} + .withExplicitBucketBoundaries(boundaries) + .create + {% else %} + .create + {% endif %} + } + {% endfor %} + +} \ No newline at end of file diff --git a/buildscripts/semantic-convention/templates/registry/otel4s/metrics/weaver.yaml b/buildscripts/semantic-convention/templates/registry/otel4s/metrics/weaver.yaml new file mode 100644 index 000000000..e50ee0a7b --- /dev/null +++ b/buildscripts/semantic-convention/templates/registry/otel4s/metrics/weaver.yaml @@ -0,0 +1,38 @@ +params: + excluded_namespaces: ["ios", "aspnetcore", "signalr", "kestrel", "veightjs", "go"] + excluded_attributes: ["messaging.client_id"] + excluded_stability: ["experimental", "deprecated"] # stable - ["experimental", "deprecated"]; experimental - [] + object_prefix: "" # stable - "", experimental - "Experimental" +comment_formats: + scaladoc: + format: html + header: "/**" + prefix: " * " + footer: " */" + old_style_paragraph: true + omit_closing_li: true + inline_code_snippet: "`{{code}}`" + block_code_snippet: "{{{\n{{code}}}\n}}}" + trim: true + remove_trailing_dots: false +default_comment_format: scaladoc +templates: + - pattern: SemanticMetrics.scala.j2 + filter: > + semconv_grouped_metrics({ + "exclude_root_namespace": $excluded_namespaces, + "exclude_stability": $excluded_stability, + "exclude_deprecated": false + }) + application_mode: each + file_name: "{{ctx.root_namespace | pascal_case}}{{params.object_prefix}}Metrics.scala" +text_maps: + scala_key_type: + int: Long + double: Double + boolean: Boolean + string: String + string[]: Seq[String] + int[]: Seq[Long] + double[]: Seq[Double] + boolean[]: Seq[Boolean] \ No newline at end of file diff --git a/project/SemanticConventionsGenerator.scala b/project/SemanticConventionsGenerator.scala index c0026904b..aa262be83 100644 --- a/project/SemanticConventionsGenerator.scala +++ b/project/SemanticConventionsGenerator.scala @@ -7,18 +7,25 @@ object SemanticConventionsGenerator { // generates semantic conventions by using `otel/weaver` in docker def generate(version: String, rootDir: File): Unit = { - generateOne(version, rootDir, experimental = false) - generateOne(version, rootDir, experimental = true) + generateAttributes(version, rootDir) + generateMetrics(version, rootDir) } - private def generateOne( + private def generateAttributes(version: String, rootDir: File): Unit = { + generateAttributes(version, rootDir, experimental = false) + generateAttributes(version, rootDir, experimental = true) + } + + private def generateMetrics(version: String, rootDir: File): Unit = { + generateMetrics(version, rootDir, experimental = false) + generateMetrics(version, rootDir, experimental = true) + } + + private def generateAttributes( version: String, rootDir: File, experimental: Boolean ): Unit = { - val semanticConventionsRepoZip = - s"https://github.com/open-telemetry/semantic-conventions/archive/v$version.zip" - val outputDir = if (experimental) s"$rootDir/semconv/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/attributes" @@ -37,6 +44,44 @@ object SemanticConventionsGenerator { else Nil + invokeWeaverGenerator(version, rootDir, outputDir, target, Nil) + } + + private def generateMetrics( + version: String, + rootDir: File, + experimental: Boolean + ): Unit = { + val outputDir = + if (experimental) + s"$rootDir/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/" + else + s"$rootDir/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/metrics/" + + val target = "otel4s/metrics" + + val params: List[String] = + if (experimental) + List( + "--param=excluded_stability=[]", + "--param=object_prefix=Experimental" + ) + else + Nil + + invokeWeaverGenerator(version, rootDir, outputDir, target, params) + } + + private def invokeWeaverGenerator( + version: String, + rootDir: File, + outputDir: String, + target: String, + params: List[String] + ): Unit = { + val semanticConventionsRepoZip = + s"https://github.com/open-telemetry/semantic-conventions/archive/v$version.zip" + val buildDir = rootDir / "buildscripts" / "semantic-convention" val zip = buildDir / "semantic-conventions.zip" val conventionsDir = buildDir / s"semantic-conventions-$version" @@ -66,4 +111,5 @@ object SemanticConventionsGenerator { Process(command, rootDir).! } + } diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/ContainerExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/ContainerExperimentalMetrics.scala new file mode 100644 index 000000000..c524dc80b --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/ContainerExperimentalMetrics.scala @@ -0,0 +1,192 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object ContainerExperimentalMetrics { + + /** Total CPU time consumed

+ * @note + *

Total CPU time consumed by the specific container on all available CPU cores + */ + object CpuTime { + + val Name = "container.cpu.time" + val Description = "Total CPU time consumed" + val Unit = "s" + + object AttributeSpecs { + + /** The CPU mode for this data point. A container's CPU metric SHOULD be characterized either by data + * points with no `mode` labels, or only data points with `mode` labels.

+ * @note + *

Following states SHOULD be used: `user`, `system`, `kernel` + */ + val cpuMode: AttributeSpec[String] = + AttributeSpec( + AttributeKey("cpu.mode"), + List( + "user", + "system", + ), + Requirement.optIn, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + cpuMode, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Disk bytes for the container.

+ * @note + *

The total number of bytes read/written successfully (aggregated from all disks). + */ + object DiskIo { + + val Name = "container.disk.io" + val Description = "Disk bytes for the container." + val Unit = "By" + + object AttributeSpecs { + + /** The disk IO operation direction. + */ + val diskIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("disk.io.direction"), + List( + "read", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + diskIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Memory usage of the container.

+ * @note + *

Memory usage of the container. + */ + object MemoryUsage { + + val Name = "container.memory.usage" + val Description = "Memory usage of the container." + val Unit = "By" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Network bytes for the container.

+ * @note + *

The number of bytes sent/received on all network interfaces by the container. + */ + object NetworkIo { + + val Name = "container.network.io" + val Description = "Network bytes for the container." + val Unit = "By" + + object AttributeSpecs { + + /** The network IO operation direction. + */ + val networkIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.io.direction"), + List( + "transmit", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + networkIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/DbExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/DbExperimentalMetrics.scala new file mode 100644 index 000000000..e4267a8db --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/DbExperimentalMetrics.scala @@ -0,0 +1,1015 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object DbExperimentalMetrics { + + /** The number of connections that are currently in state described by the `state` attribute + */ + object ClientConnectionCount { + + val Name = "db.client.connection.count" + val Description = + "The number of connections that are currently in state described by the `state` attribute" + val Unit = "{connection}" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + /** The state of a connection in the pool + */ + val dbClientConnectionState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.state"), + List( + "idle", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + dbClientConnectionState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The time it took to create a new connection + */ + object ClientConnectionCreateTime { + + val Name = "db.client.connection.create_time" + val Description = "The time it took to create a new connection" + val Unit = "s" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** The maximum number of idle open connections allowed + */ + object ClientConnectionIdleMax { + + val Name = "db.client.connection.idle.max" + val Description = "The maximum number of idle open connections allowed" + val Unit = "{connection}" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The minimum number of idle open connections allowed + */ + object ClientConnectionIdleMin { + + val Name = "db.client.connection.idle.min" + val Description = "The minimum number of idle open connections allowed" + val Unit = "{connection}" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The maximum number of open connections allowed + */ + object ClientConnectionMax { + + val Name = "db.client.connection.max" + val Description = "The maximum number of open connections allowed" + val Unit = "{connection}" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The number of pending requests for an open connection, cumulative for the entire pool + */ + object ClientConnectionPendingRequests { + + val Name = "db.client.connection.pending_requests" + val Description = + "The number of pending requests for an open connection, cumulative for the entire pool" + val Unit = "{request}" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The number of connection timeouts that have occurred trying to obtain a connection from the pool + */ + object ClientConnectionTimeouts { + + val Name = "db.client.connection.timeouts" + val Description = + "The number of connection timeouts that have occurred trying to obtain a connection from the pool" + val Unit = "{timeout}" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The time between borrowing a connection and returning it to the pool + */ + object ClientConnectionUseTime { + + val Name = "db.client.connection.use_time" + val Description = + "The time between borrowing a connection and returning it to the pool" + val Unit = "s" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** The time it took to obtain an open connection from the pool + */ + object ClientConnectionWaitTime { + + val Name = "db.client.connection.wait_time" + val Description = + "The time it took to obtain an open connection from the pool" + val Unit = "s" + + object AttributeSpecs { + + /** The name of the connection pool; unique within the instrumented application. In case the connection pool + * implementation doesn't provide a name, instrumentation SHOULD use a combination of parameters that would make + * the name unique, for example, combining attributes `server.address`, `server.port`, and `db.namespace`, + * formatted as `server.address:server.port/db.namespace`. Instrumentations that generate connection pool name + * following different patterns SHOULD document it. + */ + val dbClientConnectionPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connection.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionPoolName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Deprecated, use `db.client.connection.create_time` instead. Note: the unit also changed from `ms` to `s`. + */ + @deprecated( + "Replaced by `db.client.connection.create_time`. Note: the unit also changed from `ms` to `s`.", + "" + ) + object ClientConnectionsCreateTime { + + val Name = "db.client.connections.create_time" + val Description = + "Deprecated, use `db.client.connection.create_time` instead. Note: the unit also changed from `ms` to `s`." + val Unit = "ms" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Deprecated, use `db.client.connection.idle.max` instead. + */ + @deprecated("Replaced by `db.client.connection.idle.max`.", "") + object ClientConnectionsIdleMax { + + val Name = "db.client.connections.idle.max" + val Description = "Deprecated, use `db.client.connection.idle.max` instead." + val Unit = "{connection}" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated, use `db.client.connection.idle.min` instead. + */ + @deprecated("Replaced by `db.client.connection.idle.min`.", "") + object ClientConnectionsIdleMin { + + val Name = "db.client.connections.idle.min" + val Description = "Deprecated, use `db.client.connection.idle.min` instead." + val Unit = "{connection}" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated, use `db.client.connection.max` instead. + */ + @deprecated("Replaced by `db.client.connection.max`.", "") + object ClientConnectionsMax { + + val Name = "db.client.connections.max" + val Description = "Deprecated, use `db.client.connection.max` instead." + val Unit = "{connection}" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated, use `db.client.connection.pending_requests` instead. + */ + @deprecated("Replaced by `db.client.connection.pending_requests`.", "") + object ClientConnectionsPendingRequests { + + val Name = "db.client.connections.pending_requests" + val Description = + "Deprecated, use `db.client.connection.pending_requests` instead." + val Unit = "{request}" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated, use `db.client.connection.timeouts` instead. + */ + @deprecated("Replaced by `db.client.connection.timeouts`.", "") + object ClientConnectionsTimeouts { + + val Name = "db.client.connections.timeouts" + val Description = "Deprecated, use `db.client.connection.timeouts` instead." + val Unit = "{timeout}" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated, use `db.client.connection.count` instead. + */ + @deprecated("Replaced by `db.client.connection.count`.", "") + object ClientConnectionsUsage { + + val Name = "db.client.connections.usage" + val Description = "Deprecated, use `db.client.connection.count` instead." + val Unit = "{connection}" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + /** Deprecated, use `db.client.connection.state` instead. + */ + @deprecated("Replaced by `db.client.connection.state`.", "") + val dbClientConnectionsState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.state"), + List( + "idle", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + dbClientConnectionsState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated, use `db.client.connection.use_time` instead. Note: the unit also changed from `ms` to `s`. + */ + @deprecated( + "Replaced by `db.client.connection.use_time`. Note: the unit also changed from `ms` to `s`.", + "" + ) + object ClientConnectionsUseTime { + + val Name = "db.client.connections.use_time" + val Description = + "Deprecated, use `db.client.connection.use_time` instead. Note: the unit also changed from `ms` to `s`." + val Unit = "ms" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Deprecated, use `db.client.connection.wait_time` instead. Note: the unit also changed from `ms` to `s`. + */ + @deprecated( + "Replaced by `db.client.connection.wait_time`. Note: the unit also changed from `ms` to `s`.", + "" + ) + object ClientConnectionsWaitTime { + + val Name = "db.client.connections.wait_time" + val Description = + "Deprecated, use `db.client.connection.wait_time` instead. Note: the unit also changed from `ms` to `s`." + val Unit = "ms" + + object AttributeSpecs { + + /** Deprecated, use `db.client.connection.pool.name` instead. + */ + @deprecated("Replaced by `db.client.connection.pool.name`.", "") + val dbClientConnectionsPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.client.connections.pool.name"), + List( + "myDataSource", + ), + Requirement.required, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + dbClientConnectionsPoolName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Duration of database client operations.

+ * @note + *

Batch operations SHOULD be recorded as a single operation. + */ + object ClientOperationDuration { + + val Name = "db.client.operation.duration" + val Description = "Duration of database client operations." + val Unit = "s" + + object AttributeSpecs { + + /** The name of a collection (table, container) within the database.

+ * @note + *

It is RECOMMENDED to capture the value as provided by the application without attempting to do any case + * normalization. If the collection name is parsed from the query text, it SHOULD be the first collection name + * found in the query and it SHOULD match the value provided in the query text including any schema and + * database name prefix. For batch operations, if the individual operations are known to have the same + * collection name then that collection name SHOULD be used, otherwise `db.collection.name` SHOULD NOT be + * captured. + */ + val dbCollectionName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.collection.name"), + List( + "public.users", + "customers", + ), + Requirement.conditionallyRequired( + "If readily available. The collection name MAY be parsed from the query text, in which case it SHOULD be the first collection name in the query." + ), + Stability.experimental + ) + + /** The name of the database, fully qualified within the server address and port.

+ * @note + *

If a database system has multiple namespace components, they SHOULD be concatenated (potentially using + * database system specific conventions) from most general to most specific namespace component, and more + * specific namespaces SHOULD NOT be captured without the more general namespaces, to ensure that "startswith" + * queries for the more general namespaces will be valid. Semantic conventions for individual database systems + * SHOULD document what `db.namespace` means in the context of that system. It is RECOMMENDED to capture the + * value as provided by the application without attempting to do any case normalization. + */ + val dbNamespace: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.namespace"), + List( + "customers", + "test.users", + ), + Requirement.conditionallyRequired("If available."), + Stability.experimental + ) + + /** The name of the operation or command being executed.

+ * @note + *

It is RECOMMENDED to capture the value as provided by the application without attempting to do any case + * normalization. If the operation name is parsed from the query text, it SHOULD be the first operation name + * found in the query. For batch operations, if the individual operations are known to have the same operation + * name then that operation name SHOULD be used prepended by `BATCH `, otherwise `db.operation.name` SHOULD be + * `BATCH` or some other database system specific term if more applicable. + */ + val dbOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.operation.name"), + List( + "findAndModify", + "HMSET", + "SELECT", + ), + Requirement.conditionallyRequired( + "If readily available. The operation name MAY be parsed from the query text, in which case it SHOULD be the first operation name found in the query." + ), + Stability.experimental + ) + + /** The database management system (DBMS) product as identified by the client instrumentation.

+ * @note + *

The actual DBMS may differ from the one identified by the client. For example, when using PostgreSQL + * client libraries to connect to a CockroachDB, the `db.system` is set to `postgresql` based on the + * instrumentation's best knowledge. + */ + val dbSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("db.system"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD match the error code returned by the database or the client library, the + * canonical name of exception that occurred, or another low-cardinality error identifier. Instrumentations + * SHOULD document the list of errors they report. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If and only if the operation failed." + ), + Stability.stable + ) + + /** Peer address of the database node where the operation was performed.

+ * @note + *

Semantic conventions for individual database systems SHOULD document whether `network.peer.*` attributes + * are applicable. Network peer address and port are useful when the application interacts with individual + * database nodes directly. If a database operation involved multiple network calls (for example retries), the + * address of the last contacted node SHOULD be used. + */ + val networkPeerAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.peer.address"), + List( + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended("If applicable for this database system."), + Stability.stable + ) + + /** Peer port number of the network connection. + */ + val networkPeerPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("network.peer.port"), + List( + 65123, + ), + Requirement.recommended( + "If and only if `network.peer.address` is set." + ), + Stability.stable + ) + + /** Name of the database host.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.conditionallyRequired( + "If using a port other than the default port for this DBMS and if `server.address` is set." + ), + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + dbCollectionName, + dbNamespace, + dbOperationName, + dbSystem, + errorType, + networkPeerAddress, + networkPeerPort, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/DnsExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/DnsExperimentalMetrics.scala new file mode 100644 index 000000000..1d2a3c050 --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/DnsExperimentalMetrics.scala @@ -0,0 +1,94 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object DnsExperimentalMetrics { + + /** Measures the time taken to perform a DNS lookup. + */ + object LookupDuration { + + val Name = "dns.lookup.duration" + val Description = "Measures the time taken to perform a DNS lookup." + val Unit = "s" + + object AttributeSpecs { + + /** The name being queried.

+ * @note + *

If the name field contains non-printable characters (below 32 or above 126), those characters should be + * represented as escaped base 10 integers (\DDD). Back slashes and quotes should be escaped. Tabs, carriage + * returns, and line feeds should be converted to \t, \r, and \n respectively. + */ + val dnsQuestionName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("dns.question.name"), + List( + "www.example.com", + "dot.net", + ), + Requirement.required, + Stability.experimental + ) + + /** Describes the error the DNS lookup failed with.

+ * @note + *

Instrumentations SHOULD use error code such as one of errors reported by `getaddrinfo`(Linux or other POSIX systems / Windows) or one + * reported by the runtime or client library. If error code is not available, the full name of exception type + * SHOULD be used. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "host_not_found", + "no_recovery", + "java.net.UnknownHostException", + ), + Requirement.conditionallyRequired( + "if and only if an error has occurred." + ), + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + dnsQuestionName, + errorType, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/FaasExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/FaasExperimentalMetrics.scala new file mode 100644 index 000000000..07057be57 --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/FaasExperimentalMetrics.scala @@ -0,0 +1,366 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object FaasExperimentalMetrics { + + /** Number of invocation cold starts + */ + object Coldstarts { + + val Name = "faas.coldstarts" + val Description = "Number of invocation cold starts" + val Unit = "{coldstart}" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Distribution of CPU usage per invocation + */ + object CpuUsage { + + val Name = "faas.cpu_usage" + val Description = "Distribution of CPU usage per invocation" + val Unit = "s" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Number of invocation errors + */ + object Errors { + + val Name = "faas.errors" + val Description = "Number of invocation errors" + val Unit = "{error}" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measures the duration of the function's initialization, such as a cold start + */ + object InitDuration { + + val Name = "faas.init_duration" + val Description = + "Measures the duration of the function's initialization, such as a cold start" + val Unit = "s" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Number of successful invocations + */ + object Invocations { + + val Name = "faas.invocations" + val Description = "Number of successful invocations" + val Unit = "{invocation}" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measures the duration of the function's logic execution + */ + object InvokeDuration { + + val Name = "faas.invoke_duration" + val Description = "Measures the duration of the function's logic execution" + val Unit = "s" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Distribution of max memory usage per invocation + */ + object MemUsage { + + val Name = "faas.mem_usage" + val Description = "Distribution of max memory usage per invocation" + val Unit = "By" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Distribution of net I/O usage per invocation + */ + object NetIo { + + val Name = "faas.net_io" + val Description = "Distribution of net I/O usage per invocation" + val Unit = "By" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Number of invocation timeouts + */ + object Timeouts { + + val Name = "faas.timeouts" + val Description = "Number of invocation timeouts" + val Unit = "{timeout}" + + object AttributeSpecs { + + /** Type of the trigger which caused this function invocation. + */ + val faasTrigger: AttributeSpec[String] = + AttributeSpec( + AttributeKey("faas.trigger"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + faasTrigger, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/GenAiExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/GenAiExperimentalMetrics.scala new file mode 100644 index 000000000..03d9c50e2 --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/GenAiExperimentalMetrics.scala @@ -0,0 +1,761 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object GenAiExperimentalMetrics { + + /** GenAI operation duration + */ + object ClientOperationDuration { + + val Name = "gen_ai.client.operation.duration" + val Description = "GenAI operation duration" + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD match the error code returned by the Generative AI provider or the client + * library, the canonical name of exception that occurred, or another low-cardinality error identifier. + * Instrumentations SHOULD document the list of errors they report. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "if the operation ended in an error" + ), + Stability.stable + ) + + /** The name of the operation being performed.

+ * @note + *

If one of the predefined values applies, but specific system uses a different name it's RECOMMENDED to + * document it in the semantic conventions for specific GenAI system and use system-specific name in the + * instrumentation. If a different name is not documented, instrumentation libraries SHOULD use applicable + * predefined value. + */ + val genAiOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.operation.name"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the GenAI model a request is being made to. + */ + val genAiRequestModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.request.model"), + List( + "g", + "p", + "t", + "-", + "4", + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the model that generated the response. + */ + val genAiResponseModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.response.model"), + List( + "gpt-4-0613", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The Generative AI product as identified by the client or server instrumentation.

+ * @note + *

The `gen_ai.system` describes a family of GenAI models with specific model identified by + * `gen_ai.request.model` and `gen_ai.response.model` attributes.

The actual GenAI product may differ from + * the one identified by the client. For example, when using OpenAI client libraries to communicate with + * Mistral, the `gen_ai.system` is set to `openai` based on the instrumentation's best knowledge.

For + * custom model, a custom friendly name SHOULD be used. If none of these options apply, the `gen_ai.system` + * SHOULD be set to `_OTHER`. + */ + val genAiSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.system"), + List( + "o", + "p", + "e", + "n", + "a", + "i", + ), + Requirement.required, + Stability.experimental + ) + + /** GenAI server address.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** GenAI server port.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.conditionallyRequired("If `server.address` is set."), + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + genAiOperationName, + genAiRequestModel, + genAiResponseModel, + genAiSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures number of input and output tokens used + */ + object ClientTokenUsage { + + val Name = "gen_ai.client.token.usage" + val Description = "Measures number of input and output tokens used" + val Unit = "{token}" + + object AttributeSpecs { + + /** The name of the operation being performed.

+ * @note + *

If one of the predefined values applies, but specific system uses a different name it's RECOMMENDED to + * document it in the semantic conventions for specific GenAI system and use system-specific name in the + * instrumentation. If a different name is not documented, instrumentation libraries SHOULD use applicable + * predefined value. + */ + val genAiOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.operation.name"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the GenAI model a request is being made to. + */ + val genAiRequestModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.request.model"), + List( + "g", + "p", + "t", + "-", + "4", + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the model that generated the response. + */ + val genAiResponseModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.response.model"), + List( + "gpt-4-0613", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The Generative AI product as identified by the client or server instrumentation.

+ * @note + *

The `gen_ai.system` describes a family of GenAI models with specific model identified by + * `gen_ai.request.model` and `gen_ai.response.model` attributes.

The actual GenAI product may differ from + * the one identified by the client. For example, when using OpenAI client libraries to communicate with + * Mistral, the `gen_ai.system` is set to `openai` based on the instrumentation's best knowledge.

For + * custom model, a custom friendly name SHOULD be used. If none of these options apply, the `gen_ai.system` + * SHOULD be set to `_OTHER`. + */ + val genAiSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.system"), + List( + "o", + "p", + "e", + "n", + "a", + "i", + ), + Requirement.required, + Stability.experimental + ) + + /** The type of token being counted. + */ + val genAiTokenType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.token.type"), + List( + "input", + "output", + ), + Requirement.required, + Stability.experimental + ) + + /** GenAI server address.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** GenAI server port.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.conditionallyRequired("If `server.address` is set."), + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + genAiOperationName, + genAiRequestModel, + genAiResponseModel, + genAiSystem, + genAiTokenType, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Generative AI server request duration such as time-to-last byte or last output token + */ + object ServerRequestDuration { + + val Name = "gen_ai.server.request.duration" + val Description = + "Generative AI server request duration such as time-to-last byte or last output token" + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD match the error code returned by the Generative AI service, the canonical name + * of exception that occurred, or another low-cardinality error identifier. Instrumentations SHOULD document + * the list of errors they report. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "if the operation ended in an error" + ), + Stability.stable + ) + + /** The name of the operation being performed.

+ * @note + *

If one of the predefined values applies, but specific system uses a different name it's RECOMMENDED to + * document it in the semantic conventions for specific GenAI system and use system-specific name in the + * instrumentation. If a different name is not documented, instrumentation libraries SHOULD use applicable + * predefined value. + */ + val genAiOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.operation.name"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the GenAI model a request is being made to. + */ + val genAiRequestModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.request.model"), + List( + "g", + "p", + "t", + "-", + "4", + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the model that generated the response. + */ + val genAiResponseModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.response.model"), + List( + "gpt-4-0613", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The Generative AI product as identified by the client or server instrumentation.

+ * @note + *

The `gen_ai.system` describes a family of GenAI models with specific model identified by + * `gen_ai.request.model` and `gen_ai.response.model` attributes.

The actual GenAI product may differ from + * the one identified by the client. For example, when using OpenAI client libraries to communicate with + * Mistral, the `gen_ai.system` is set to `openai` based on the instrumentation's best knowledge.

For + * custom model, a custom friendly name SHOULD be used. If none of these options apply, the `gen_ai.system` + * SHOULD be set to `_OTHER`. + */ + val genAiSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.system"), + List( + "o", + "p", + "e", + "n", + "a", + "i", + ), + Requirement.required, + Stability.experimental + ) + + /** GenAI server address.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** GenAI server port.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.conditionallyRequired("If `server.address` is set."), + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + genAiOperationName, + genAiRequestModel, + genAiResponseModel, + genAiSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Time per output token generated after the first token for successful responses + */ + object ServerTimePerOutputToken { + + val Name = "gen_ai.server.time_per_output_token" + val Description = + "Time per output token generated after the first token for successful responses" + val Unit = "s" + + object AttributeSpecs { + + /** The name of the operation being performed.

+ * @note + *

If one of the predefined values applies, but specific system uses a different name it's RECOMMENDED to + * document it in the semantic conventions for specific GenAI system and use system-specific name in the + * instrumentation. If a different name is not documented, instrumentation libraries SHOULD use applicable + * predefined value. + */ + val genAiOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.operation.name"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the GenAI model a request is being made to. + */ + val genAiRequestModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.request.model"), + List( + "g", + "p", + "t", + "-", + "4", + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the model that generated the response. + */ + val genAiResponseModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.response.model"), + List( + "gpt-4-0613", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The Generative AI product as identified by the client or server instrumentation.

+ * @note + *

The `gen_ai.system` describes a family of GenAI models with specific model identified by + * `gen_ai.request.model` and `gen_ai.response.model` attributes.

The actual GenAI product may differ from + * the one identified by the client. For example, when using OpenAI client libraries to communicate with + * Mistral, the `gen_ai.system` is set to `openai` based on the instrumentation's best knowledge.

For + * custom model, a custom friendly name SHOULD be used. If none of these options apply, the `gen_ai.system` + * SHOULD be set to `_OTHER`. + */ + val genAiSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.system"), + List( + "o", + "p", + "e", + "n", + "a", + "i", + ), + Requirement.required, + Stability.experimental + ) + + /** GenAI server address.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** GenAI server port.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.conditionallyRequired("If `server.address` is set."), + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + genAiOperationName, + genAiRequestModel, + genAiResponseModel, + genAiSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Time to generate first token for successful responses + */ + object ServerTimeToFirstToken { + + val Name = "gen_ai.server.time_to_first_token" + val Description = "Time to generate first token for successful responses" + val Unit = "s" + + object AttributeSpecs { + + /** The name of the operation being performed.

+ * @note + *

If one of the predefined values applies, but specific system uses a different name it's RECOMMENDED to + * document it in the semantic conventions for specific GenAI system and use system-specific name in the + * instrumentation. If a different name is not documented, instrumentation libraries SHOULD use applicable + * predefined value. + */ + val genAiOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.operation.name"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the GenAI model a request is being made to. + */ + val genAiRequestModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.request.model"), + List( + "g", + "p", + "t", + "-", + "4", + ), + Requirement.required, + Stability.experimental + ) + + /** The name of the model that generated the response. + */ + val genAiResponseModel: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.response.model"), + List( + "gpt-4-0613", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The Generative AI product as identified by the client or server instrumentation.

+ * @note + *

The `gen_ai.system` describes a family of GenAI models with specific model identified by + * `gen_ai.request.model` and `gen_ai.response.model` attributes.

The actual GenAI product may differ from + * the one identified by the client. For example, when using OpenAI client libraries to communicate with + * Mistral, the `gen_ai.system` is set to `openai` based on the instrumentation's best knowledge.

For + * custom model, a custom friendly name SHOULD be used. If none of these options apply, the `gen_ai.system` + * SHOULD be set to `_OTHER`. + */ + val genAiSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("gen_ai.system"), + List( + "o", + "p", + "e", + "n", + "a", + "i", + ), + Requirement.required, + Stability.experimental + ) + + /** GenAI server address.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** GenAI server port.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.conditionallyRequired("If `server.address` is set."), + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + genAiOperationName, + genAiRequestModel, + genAiResponseModel, + genAiSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/HttpExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/HttpExperimentalMetrics.scala new file mode 100644 index 000000000..7148f274d --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/HttpExperimentalMetrics.scala @@ -0,0 +1,1789 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object HttpExperimentalMetrics { + + /** Number of active HTTP requests. + */ + object ClientActiveRequests { + + val Name = "http.client.active_requests" + val Description = "Number of active HTTP requests." + val Unit = "{request}" + + object AttributeSpecs { + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.recommended, + Stability.stable + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.required, + Stability.stable + ) + + /** Port identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.required, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.optIn, + Stability.stable + ) + + /** The low-cardinality template of an absolute path + * reference.

+ * @note + *

The `url.template` MUST have low cardinality. It is not usually available on HTTP clients, but may be + * known by the application or specialized HTTP instrumentation. + */ + val urlTemplate: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.template"), + List( + "/users/{id}", + "/users/:id", + "/users?id={id}", + ), + Requirement.conditionallyRequired("If available."), + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + httpRequestMethod, + serverAddress, + serverPort, + urlScheme, + urlTemplate, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The duration of the successfully established outbound HTTP connections. + */ + object ClientConnectionDuration { + + val Name = "http.client.connection.duration" + val Description = + "The duration of the successfully established outbound HTTP connections." + val Unit = "s" + + object AttributeSpecs { + + /** Peer address of the network connection - IP address or Unix domain socket name. + */ + val networkPeerAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.peer.address"), + List( + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.1", + "2", + ), + Requirement.recommended, + Stability.stable + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.required, + Stability.stable + ) + + /** Port identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.required, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.optIn, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + networkPeerAddress, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Number of outbound HTTP connections that are currently active or idle on the client. + */ + object ClientOpenConnections { + + val Name = "http.client.open_connections" + val Description = + "Number of outbound HTTP connections that are currently active or idle on the client." + val Unit = "{connection}" + + object AttributeSpecs { + + /** State of the HTTP connection in the HTTP connection pool. + */ + val httpConnectionState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.connection.state"), + List( + "active", + "idle", + ), + Requirement.required, + Stability.experimental + ) + + /** Peer address of the network connection - IP address or Unix domain socket name. + */ + val networkPeerAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.peer.address"), + List( + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.recommended, + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.1", + "2", + ), + Requirement.recommended, + Stability.stable + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.address` + * SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.required, + Stability.stable + ) + + /** Port identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.required, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.optIn, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + httpConnectionState, + networkPeerAddress, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Size of HTTP client request bodies.

+ * @note + *

The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and + * is often, but not always, present as the Content-Length header. For requests + * using transport encoding, this should be the compressed size. + */ + object ClientRequestBodySize { + + val Name = "http.client.request.body.size" + val Description = "Size of HTTP client request bodies." + val Unit = "By" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Host identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

If an HTTP client request is explicitly made to an IP address, e.g. `http://x.x.x.x:8080`, then + * `server.address` SHOULD be the IP address `x.x.x.x`. A DNS lookup SHOULD NOT be used. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.required, + Stability.stable + ) + + /** Port identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.required, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.optIn, + Stability.stable + ) + + /** The low-cardinality template of an absolute path + * reference.

+ * @note + *

The `url.template` MUST have low cardinality. It is not usually available on HTTP clients, but may be + * known by the application or specialized HTTP instrumentation. + */ + val urlTemplate: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.template"), + List( + "/users/{id}", + "/users/:id", + "/users?id={id}", + ), + Requirement.conditionallyRequired("If available."), + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + urlTemplate, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Duration of HTTP client requests. + */ + object ClientRequestDuration { + + val Name = "http.client.request.duration" + val Description = "Duration of HTTP client requests." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Host identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

If an HTTP client request is explicitly made to an IP address, e.g. `http://x.x.x.x:8080`, then + * `server.address` SHOULD be the IP address `x.x.x.x`. A DNS lookup SHOULD NOT be used. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.required, + Stability.stable + ) + + /** Port identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.required, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.optIn, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Size of HTTP client response bodies.

+ * @note + *

The size of the response payload body in bytes. This is the number of bytes transferred excluding headers + * and is often, but not always, present as the Content-Length header. For requests + * using transport encoding, this should be the compressed size. + */ + object ClientResponseBodySize { + + val Name = "http.client.response.body.size" + val Description = "Size of HTTP client response bodies." + val Unit = "By" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Host identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

If an HTTP client request is explicitly made to an IP address, e.g. `http://x.x.x.x:8080`, then + * `server.address` SHOULD be the IP address `x.x.x.x`. A DNS lookup SHOULD NOT be used. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.required, + Stability.stable + ) + + /** Port identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.required, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.optIn, + Stability.stable + ) + + /** The low-cardinality template of an absolute path + * reference.

+ * @note + *

The `url.template` MUST have low cardinality. It is not usually available on HTTP clients, but may be + * known by the application or specialized HTTP instrumentation. + */ + val urlTemplate: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.template"), + List( + "/users/{id}", + "/users/:id", + "/users?id={id}", + ), + Requirement.conditionallyRequired("If available."), + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + urlTemplate, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Number of active HTTP server requests. + */ + object ServerActiveRequests { + + val Name = "http.server.active_requests" + val Description = "Number of active HTTP server requests." + val Unit = "{request}" + + object AttributeSpecs { + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** Name of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.optIn, + Stability.stable + ) + + /** Port of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.optIn, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.required, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + httpRequestMethod, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Size of HTTP server request bodies.

+ * @note + *

The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and + * is often, but not always, present as the Content-Length header. For requests + * using transport encoding, this should be the compressed size. + */ + object ServerRequestBodySize { + + val Name = "http.server.request.body.size" + val Description = "Size of HTTP server request bodies." + val Unit = "By" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** The matched route, that is, the path template in the format used by the respective server framework.

+ * @note + *

MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute + * should have low-cardinality and the URI path can NOT substitute it. SHOULD include the application root if there is one. + */ + val httpRoute: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.route"), + List( + "/users/:userID?", + "{controller}/{action}/{id?}", + ), + Requirement.conditionallyRequired("If and only if it's available"), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Name of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.optIn, + Stability.stable + ) + + /** Port of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.optIn, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol.

+ * @note + *

The scheme of the original client request, if known (e.g. from Forwarded#proto, X-Forwarded-Proto, or a + * similar header). Otherwise, the scheme of the immediate peer request. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.required, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + httpRoute, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Duration of HTTP server requests. + */ + object ServerRequestDuration { + + val Name = "http.server.request.duration" + val Description = "Duration of HTTP server requests." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** The matched route, that is, the path template in the format used by the respective server framework.

+ * @note + *

MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute + * should have low-cardinality and the URI path can NOT substitute it. SHOULD include the application root if there is one. + */ + val httpRoute: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.route"), + List( + "/users/:userID?", + "{controller}/{action}/{id?}", + ), + Requirement.conditionallyRequired("If and only if it's available"), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Name of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.optIn, + Stability.stable + ) + + /** Port of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.optIn, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol.

+ * @note + *

The scheme of the original client request, if known (e.g. from Forwarded#proto, X-Forwarded-Proto, or a + * similar header). Otherwise, the scheme of the immediate peer request. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.required, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + httpRoute, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Size of HTTP server response bodies.

+ * @note + *

The size of the response payload body in bytes. This is the number of bytes transferred excluding headers + * and is often, but not always, present as the Content-Length header. For requests + * using transport encoding, this should be the compressed size. + */ + object ServerResponseBodySize { + + val Name = "http.server.response.body.size" + val Description = "Size of HTTP server response bodies." + val Unit = "By" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** The matched route, that is, the path template in the format used by the respective server framework.

+ * @note + *

MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute + * should have low-cardinality and the URI path can NOT substitute it. SHOULD include the application root if there is one. + */ + val httpRoute: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.route"), + List( + "/users/:userID?", + "{controller}/{action}/{id?}", + ), + Requirement.conditionallyRequired("If and only if it's available"), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Name of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.optIn, + Stability.stable + ) + + /** Port of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.optIn, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol.

+ * @note + *

The scheme of the original client request, if known (e.g. from Forwarded#proto, X-Forwarded-Proto, or a + * similar header). Otherwise, the scheme of the immediate peer request. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.required, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + httpRoute, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/JvmExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/JvmExperimentalMetrics.scala new file mode 100644 index 000000000..594ce43fd --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/JvmExperimentalMetrics.scala @@ -0,0 +1,736 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object JvmExperimentalMetrics { + + /** Number of buffers in the pool. + */ + object BufferCount { + + val Name = "jvm.buffer.count" + val Description = "Number of buffers in the pool." + val Unit = "{buffer}" + + object AttributeSpecs { + + /** Name of the buffer pool.

+ * @note + *

Pool names are generally obtained via BufferPoolMXBean#getName(). + */ + val jvmBufferPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.buffer.pool.name"), + List( + "mapped", + "direct", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmBufferPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of total memory capacity of buffers. + */ + object BufferMemoryLimit { + + val Name = "jvm.buffer.memory.limit" + val Description = "Measure of total memory capacity of buffers." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the buffer pool.

+ * @note + *

Pool names are generally obtained via BufferPoolMXBean#getName(). + */ + val jvmBufferPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.buffer.pool.name"), + List( + "mapped", + "direct", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmBufferPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated, use `jvm.buffer.memory.used` instead. + */ + @deprecated("Replaced by `jvm.buffer.memory.used`.", "") + object BufferMemoryUsage { + + val Name = "jvm.buffer.memory.usage" + val Description = "Deprecated, use `jvm.buffer.memory.used` instead." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the buffer pool.

+ * @note + *

Pool names are generally obtained via BufferPoolMXBean#getName(). + */ + val jvmBufferPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.buffer.pool.name"), + List( + "mapped", + "direct", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmBufferPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of memory used by buffers. + */ + object BufferMemoryUsed { + + val Name = "jvm.buffer.memory.used" + val Description = "Measure of memory used by buffers." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the buffer pool.

+ * @note + *

Pool names are generally obtained via BufferPoolMXBean#getName(). + */ + val jvmBufferPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.buffer.pool.name"), + List( + "mapped", + "direct", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmBufferPoolName, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of classes currently loaded. + */ + object ClassCount { + + val Name = "jvm.class.count" + val Description = "Number of classes currently loaded." + val Unit = "{class}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of classes loaded since JVM start. + */ + object ClassLoaded { + + val Name = "jvm.class.loaded" + val Description = "Number of classes loaded since JVM start." + val Unit = "{class}" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of classes unloaded since JVM start. + */ + object ClassUnloaded { + + val Name = "jvm.class.unloaded" + val Description = "Number of classes unloaded since JVM start." + val Unit = "{class}" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of processors available to the Java virtual machine. + */ + object CpuCount { + + val Name = "jvm.cpu.count" + val Description = + "Number of processors available to the Java virtual machine." + val Unit = "{cpu}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Recent CPU utilization for the process as reported by the JVM.

+ * @note + *

The value range is [0.0,1.0]. This utilization is not defined as being for the specific interval since last + * measurement (unlike `system.cpu.utilization`). Reference. + */ + object CpuRecentUtilization { + + val Name = "jvm.cpu.recent_utilization" + val Description = + "Recent CPU utilization for the process as reported by the JVM." + val Unit = "1" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** CPU time used by the process as reported by the JVM. + */ + object CpuTime { + + val Name = "jvm.cpu.time" + val Description = "CPU time used by the process as reported by the JVM." + val Unit = "s" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Duration of JVM garbage collection actions. + */ + object GcDuration { + + val Name = "jvm.gc.duration" + val Description = "Duration of JVM garbage collection actions." + val Unit = "s" + + object AttributeSpecs { + + /** Name of the garbage collector action.

+ * @note + *

Garbage collector action is generally obtained via GarbageCollectionNotificationInfo#getGcAction(). + */ + val jvmGcAction: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.gc.action"), + List( + "end of minor GC", + "end of major GC", + ), + Requirement.recommended, + Stability.stable + ) + + /** Name of the garbage collector.

+ * @note + *

Garbage collector name is generally obtained via GarbageCollectionNotificationInfo#getGcName(). + */ + val jvmGcName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.gc.name"), + List( + "G1 Young Generation", + "G1 Old Generation", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmGcAction, + jvmGcName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measure of memory committed. + */ + object MemoryCommitted { + + val Name = "jvm.memory.committed" + val Description = "Measure of memory committed." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of initial memory requested. + */ + object MemoryInit { + + val Name = "jvm.memory.init" + val Description = "Measure of initial memory requested." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of max obtainable memory. + */ + object MemoryLimit { + + val Name = "jvm.memory.limit" + val Description = "Measure of max obtainable memory." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of memory used. + */ + object MemoryUsed { + + val Name = "jvm.memory.used" + val Description = "Measure of memory used." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of memory used, as measured after the most recent garbage collection event on this pool. + */ + object MemoryUsedAfterLastGc { + + val Name = "jvm.memory.used_after_last_gc" + val Description = + "Measure of memory used, as measured after the most recent garbage collection event on this pool." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Average CPU load of the whole system for the last minute as reported by the JVM.

+ * @note + *

The value range is [0,n], where n is the number of CPU cores - or a negative number if the value is not + * available. This utilization is not defined as being for the specific interval since last measurement (unlike + * `system.cpu.utilization`). Reference. + */ + object SystemCpuLoad1m { + + val Name = "jvm.system.cpu.load_1m" + val Description = + "Average CPU load of the whole system for the last minute as reported by the JVM." + val Unit = "{run_queue_item}" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Recent CPU utilization for the whole system as reported by the JVM.

+ * @note + *

The value range is [0.0,1.0]. This utilization is not defined as being for the specific interval since last + * measurement (unlike `system.cpu.utilization`). Reference. + */ + object SystemCpuUtilization { + + val Name = "jvm.system.cpu.utilization" + val Description = + "Recent CPU utilization for the whole system as reported by the JVM." + val Unit = "1" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of executing platform threads. + */ + object ThreadCount { + + val Name = "jvm.thread.count" + val Description = "Number of executing platform threads." + val Unit = "{thread}" + + object AttributeSpecs { + + /** Whether the thread is daemon or not. + */ + val jvmThreadDaemon: AttributeSpec[Boolean] = + AttributeSpec( + AttributeKey("jvm.thread.daemon"), + List( + ), + Requirement.recommended, + Stability.stable + ) + + /** State of the thread. + */ + val jvmThreadState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.thread.state"), + List( + "runnable", + "blocked", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmThreadDaemon, + jvmThreadState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/MessagingExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/MessagingExperimentalMetrics.scala new file mode 100644 index 000000000..d6de45983 --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/MessagingExperimentalMetrics.scala @@ -0,0 +1,1377 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object MessagingExperimentalMetrics { + + /** Number of messages that were delivered to the application.

+ * @note + *

Records the number of messages pulled from the broker or number of messages dispatched to the application in + * push-based scenarios. The metric SHOULD be reported once per message delivery. For example, if receiving and + * processing operations are both instrumented for a single message delivery, this counter is incremented when the + * message is received and not reported when it is processed. + */ + object ClientConsumedMessages { + + val Name = "messaging.client.consumed.messages" + val Description = + "Number of messages that were delivered to the application." + val Unit = "{message}" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The name of the consumer group with which a consumer is associated.

+ * @note + *

Semantic conventions for individual messaging systems SHOULD document whether + * `messaging.consumer.group.name` is applicable and what it means in the context of that system. + */ + val messagingConsumerGroupName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.consumer.group.name"), + List( + "my-group", + "indexer", + ), + Requirement.conditionallyRequired("if applicable."), + Stability.experimental + ) + + /** The message destination name

+ * @note + *

Destination name SHOULD uniquely identify a specific queue, topic or other entity within the broker. If + * the broker doesn't have such notion, the destination name SHOULD uniquely identify the broker. + */ + val messagingDestinationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.name"), + List( + "MyQueue", + "MyTopic", + ), + Requirement.conditionallyRequired( + "if and only if `messaging.destination.name` is known to have low cardinality. Otherwise, `messaging.destination.template` MAY be populated." + ), + Stability.experimental + ) + + /** The identifier of the partition messages are sent to or received from, unique within the + * `messaging.destination.name`. + */ + val messagingDestinationPartitionId: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.partition.id"), + List( + "1", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The name of the destination subscription from which a message is consumed.

+ * @note + *

Semantic conventions for individual messaging systems SHOULD document whether + * `messaging.destination.subscription.name` is applicable and what it means in the context of that system. + */ + val messagingDestinationSubscriptionName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.subscription.name"), + List( + "subscription-a", + ), + Requirement.conditionallyRequired("if applicable."), + Stability.experimental + ) + + /** Low cardinality representation of the messaging destination name

+ * @note + *

Destination names could be constructed from templates. An example would be a destination name involving + * a user name or product id. Although the destination name in this case is of high cardinality, the underlying + * template is of low cardinality and can be effectively used for grouping and aggregation. + */ + val messagingDestinationTemplate: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.template"), + List( + "/customers/{customerId}", + ), + Requirement.conditionallyRequired("if available."), + Stability.experimental + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "receive", + "peek", + "poll", + "consume", + ), + Requirement.required, + Stability.experimental + ) + + /** The messaging system as identified by the client instrumentation.

+ * @note + *

The actual messaging system may differ from the one known by the client. For example, when using Kafka + * client libraries to communicate with Azure Event Hubs, the `messaging.system` is set to `kafka` based on the + * instrumentation's best knowledge. + */ + val messagingSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.system"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingConsumerGroupName, + messagingDestinationName, + messagingDestinationPartitionId, + messagingDestinationSubscriptionName, + messagingDestinationTemplate, + messagingOperationName, + messagingSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Duration of messaging operation initiated by a producer or consumer client.

+ * @note + *

This metric SHOULD NOT be used to report processing duration - processing duration is reported in + * `messaging.process.duration` metric. + */ + object ClientOperationDuration { + + val Name = "messaging.client.operation.duration" + val Description = + "Duration of messaging operation initiated by a producer or consumer client." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The name of the consumer group with which a consumer is associated.

+ * @note + *

Semantic conventions for individual messaging systems SHOULD document whether + * `messaging.consumer.group.name` is applicable and what it means in the context of that system. + */ + val messagingConsumerGroupName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.consumer.group.name"), + List( + "my-group", + "indexer", + ), + Requirement.conditionallyRequired("if applicable."), + Stability.experimental + ) + + /** The message destination name

+ * @note + *

Destination name SHOULD uniquely identify a specific queue, topic or other entity within the broker. If + * the broker doesn't have such notion, the destination name SHOULD uniquely identify the broker. + */ + val messagingDestinationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.name"), + List( + "MyQueue", + "MyTopic", + ), + Requirement.conditionallyRequired( + "if and only if `messaging.destination.name` is known to have low cardinality. Otherwise, `messaging.destination.template` MAY be populated." + ), + Stability.experimental + ) + + /** The identifier of the partition messages are sent to or received from, unique within the + * `messaging.destination.name`. + */ + val messagingDestinationPartitionId: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.partition.id"), + List( + "1", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The name of the destination subscription from which a message is consumed.

+ * @note + *

Semantic conventions for individual messaging systems SHOULD document whether + * `messaging.destination.subscription.name` is applicable and what it means in the context of that system. + */ + val messagingDestinationSubscriptionName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.subscription.name"), + List( + "subscription-a", + ), + Requirement.conditionallyRequired("if applicable."), + Stability.experimental + ) + + /** Low cardinality representation of the messaging destination name

+ * @note + *

Destination names could be constructed from templates. An example would be a destination name involving + * a user name or product id. Although the destination name in this case is of high cardinality, the underlying + * template is of low cardinality and can be effectively used for grouping and aggregation. + */ + val messagingDestinationTemplate: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.template"), + List( + "/customers/{customerId}", + ), + Requirement.conditionallyRequired("if available."), + Stability.experimental + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "send", + "receive", + "ack", + ), + Requirement.required, + Stability.experimental + ) + + /** A string identifying the type of the messaging operation.

+ * @note + *

If a custom value is used, it MUST be of low cardinality. + */ + val messagingOperationType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.type"), + List( + ), + Requirement.conditionallyRequired("If applicable."), + Stability.experimental + ) + + /** The messaging system as identified by the client instrumentation.

+ * @note + *

The actual messaging system may differ from the one known by the client. For example, when using Kafka + * client libraries to communicate with Azure Event Hubs, the `messaging.system` is set to `kafka` based on the + * instrumentation's best knowledge. + */ + val messagingSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.system"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingConsumerGroupName, + messagingDestinationName, + messagingDestinationPartitionId, + messagingDestinationSubscriptionName, + messagingDestinationTemplate, + messagingOperationName, + messagingOperationType, + messagingSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Number of messages producer attempted to publish to the broker.

+ * @note + *

This metric MUST NOT count messages that were created haven't yet been attempted to be published. + */ + object ClientPublishedMessages { + + val Name = "messaging.client.published.messages" + val Description = + "Number of messages producer attempted to publish to the broker." + val Unit = "{message}" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The message destination name

+ * @note + *

Destination name SHOULD uniquely identify a specific queue, topic or other entity within the broker. If + * the broker doesn't have such notion, the destination name SHOULD uniquely identify the broker. + */ + val messagingDestinationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.name"), + List( + "MyQueue", + "MyTopic", + ), + Requirement.conditionallyRequired( + "if and only if `messaging.destination.name` is known to have low cardinality. Otherwise, `messaging.destination.template` MAY be populated." + ), + Stability.experimental + ) + + /** The identifier of the partition messages are sent to or received from, unique within the + * `messaging.destination.name`. + */ + val messagingDestinationPartitionId: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.partition.id"), + List( + "1", + ), + Requirement.recommended, + Stability.experimental + ) + + /** Low cardinality representation of the messaging destination name

+ * @note + *

Destination names could be constructed from templates. An example would be a destination name involving + * a user name or product id. Although the destination name in this case is of high cardinality, the underlying + * template is of low cardinality and can be effectively used for grouping and aggregation. + */ + val messagingDestinationTemplate: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.template"), + List( + "/customers/{customerId}", + ), + Requirement.conditionallyRequired("if available."), + Stability.experimental + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "send", + "schedule", + "enqueue", + ), + Requirement.required, + Stability.experimental + ) + + /** The messaging system as identified by the client instrumentation.

+ * @note + *

The actual messaging system may differ from the one known by the client. For example, when using Kafka + * client libraries to communicate with Azure Event Hubs, the `messaging.system` is set to `kafka` based on the + * instrumentation's best knowledge. + */ + val messagingSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.system"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingDestinationName, + messagingDestinationPartitionId, + messagingDestinationTemplate, + messagingOperationName, + messagingSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Duration of processing operation.

+ * @note + *

This metric MUST be reported for operations with `messaging.operation.type` that matches `process`. + */ + object ProcessDuration { + + val Name = "messaging.process.duration" + val Description = "Duration of processing operation." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The name of the consumer group with which a consumer is associated.

+ * @note + *

Semantic conventions for individual messaging systems SHOULD document whether + * `messaging.consumer.group.name` is applicable and what it means in the context of that system. + */ + val messagingConsumerGroupName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.consumer.group.name"), + List( + "my-group", + "indexer", + ), + Requirement.conditionallyRequired("if applicable."), + Stability.experimental + ) + + /** The message destination name

+ * @note + *

Destination name SHOULD uniquely identify a specific queue, topic or other entity within the broker. If + * the broker doesn't have such notion, the destination name SHOULD uniquely identify the broker. + */ + val messagingDestinationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.name"), + List( + "MyQueue", + "MyTopic", + ), + Requirement.conditionallyRequired( + "if and only if `messaging.destination.name` is known to have low cardinality. Otherwise, `messaging.destination.template` MAY be populated." + ), + Stability.experimental + ) + + /** The identifier of the partition messages are sent to or received from, unique within the + * `messaging.destination.name`. + */ + val messagingDestinationPartitionId: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.partition.id"), + List( + "1", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The name of the destination subscription from which a message is consumed.

+ * @note + *

Semantic conventions for individual messaging systems SHOULD document whether + * `messaging.destination.subscription.name` is applicable and what it means in the context of that system. + */ + val messagingDestinationSubscriptionName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.subscription.name"), + List( + "subscription-a", + ), + Requirement.conditionallyRequired("if applicable."), + Stability.experimental + ) + + /** Low cardinality representation of the messaging destination name

+ * @note + *

Destination names could be constructed from templates. An example would be a destination name involving + * a user name or product id. Although the destination name in this case is of high cardinality, the underlying + * template is of low cardinality and can be effectively used for grouping and aggregation. + */ + val messagingDestinationTemplate: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.destination.template"), + List( + "/customers/{customerId}", + ), + Requirement.conditionallyRequired("if available."), + Stability.experimental + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "process", + "consume", + "handle", + ), + Requirement.required, + Stability.experimental + ) + + /** The messaging system as identified by the client instrumentation.

+ * @note + *

The actual messaging system may differ from the one known by the client. For example, when using Kafka + * client libraries to communicate with Azure Event Hubs, the `messaging.system` is set to `kafka` based on the + * instrumentation's best knowledge. + */ + val messagingSystem: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.system"), + List( + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingConsumerGroupName, + messagingDestinationName, + messagingDestinationPartitionId, + messagingDestinationSubscriptionName, + messagingDestinationTemplate, + messagingOperationName, + messagingSystem, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Deprecated. Use `messaging.client.consumed.messages` instead. + */ + @deprecated("Replaced by `messaging.client.consumed.messages`.", "") + object ProcessMessages { + + val Name = "messaging.process.messages" + val Description = + "Deprecated. Use `messaging.client.consumed.messages` instead." + val Unit = "{message}" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "ack", + "nack", + "send", + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingOperationName, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated. Use `messaging.client.operation.duration` instead. + */ + @deprecated("Replaced by `messaging.client.operation.duration`.", "") + object PublishDuration { + + val Name = "messaging.publish.duration" + val Description = + "Deprecated. Use `messaging.client.operation.duration` instead." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "ack", + "nack", + "send", + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingOperationName, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Deprecated. Use `messaging.client.produced.messages` instead. + */ + @deprecated("Replaced by `messaging.client.produced.messages`.", "") + object PublishMessages { + + val Name = "messaging.publish.messages" + val Description = + "Deprecated. Use `messaging.client.produced.messages` instead." + val Unit = "{message}" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "ack", + "nack", + "send", + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingOperationName, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Deprecated. Use `messaging.client.operation.duration` instead. + */ + @deprecated("Replaced by `messaging.client.operation.duration`.", "") + object ReceiveDuration { + + val Name = "messaging.receive.duration" + val Description = + "Deprecated. Use `messaging.client.operation.duration` instead." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "ack", + "nack", + "send", + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingOperationName, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Deprecated. Use `messaging.client.consumed.messages` instead. + */ + @deprecated("Replaced by `messaging.client.consumed.messages`.", "") + object ReceiveMessages { + + val Name = "messaging.receive.messages" + val Description = + "Deprecated. Use `messaging.client.consumed.messages` instead." + val Unit = "{message}" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

The `error.type` SHOULD be predictable, and SHOULD have low cardinality.

When `error.type` is set to + * a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD + * be used.

Instrumentations SHOULD document the list of errors they report.

The cardinality of + * `error.type` within one instrumentation library SHOULD be low. Telemetry consumers that aggregate data from + * multiple instrumentation libraries and applications should be prepared for `error.type` to have high + * cardinality at query time when no additional filters are applied.

If the operation has completed + * successfully, instrumentations SHOULD NOT set `error.type`.

If a specific domain defines its own set of + * error identifiers (such as HTTP or gRPC status codes), it's RECOMMENDED to:

+ */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "amqp:decode-error", + "KAFKA_STORAGE_ERROR", + "channel-error", + ), + Requirement.conditionallyRequired( + "If and only if the messaging operation has failed." + ), + Stability.stable + ) + + /** The system-specific name of the messaging operation. + */ + val messagingOperationName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("messaging.operation.name"), + List( + "ack", + "nack", + "send", + ), + Requirement.required, + Stability.experimental + ) + + /** Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + *

+ * @note + *

Server domain name of the broker if available without reverse DNS lookup; otherwise, IP address or Unix + * domain socket name. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.conditionallyRequired("If available."), + Stability.stable + ) + + /** Server port number.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + messagingOperationName, + serverAddress, + serverPort, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/NodejsExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/NodejsExperimentalMetrics.scala new file mode 100644 index 000000000..f70ac424e --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/NodejsExperimentalMetrics.scala @@ -0,0 +1,187 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object NodejsExperimentalMetrics { + + /** Event loop maximum delay.

+ * @note + *

Value can be retrieved from value `histogram.max` of `perf_hooks.monitorEventLoopDelay([options])` + */ + object EventloopDelayMax { + + val Name = "nodejs.eventloop.delay.max" + val Description = "Event loop maximum delay." + val Unit = "s" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Event loop mean delay.

+ * @note + *

Value can be retrieved from value `histogram.mean` of `perf_hooks.monitorEventLoopDelay([options])` + */ + object EventloopDelayMean { + + val Name = "nodejs.eventloop.delay.mean" + val Description = "Event loop mean delay." + val Unit = "s" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Event loop minimum delay.

+ * @note + *

Value can be retrieved from value `histogram.min` of `perf_hooks.monitorEventLoopDelay([options])` + */ + object EventloopDelayMin { + + val Name = "nodejs.eventloop.delay.min" + val Description = "Event loop minimum delay." + val Unit = "s" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Event loop 50 percentile delay.

+ * @note + *

Value can be retrieved from value `histogram.percentile(50)` of `perf_hooks.monitorEventLoopDelay([options])` + */ + object EventloopDelayP50 { + + val Name = "nodejs.eventloop.delay.p50" + val Description = "Event loop 50 percentile delay." + val Unit = "s" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Event loop 90 percentile delay.

+ * @note + *

Value can be retrieved from value `histogram.percentile(90)` of `perf_hooks.monitorEventLoopDelay([options])` + */ + object EventloopDelayP90 { + + val Name = "nodejs.eventloop.delay.p90" + val Description = "Event loop 90 percentile delay." + val Unit = "s" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Event loop 99 percentile delay.

+ * @note + *

Value can be retrieved from value `histogram.percentile(99)` of `perf_hooks.monitorEventLoopDelay([options])` + */ + object EventloopDelayP99 { + + val Name = "nodejs.eventloop.delay.p99" + val Description = "Event loop 99 percentile delay." + val Unit = "s" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Event loop standard deviation delay.

+ * @note + *

Value can be retrieved from value `histogram.stddev` of `perf_hooks.monitorEventLoopDelay([options])` + */ + object EventloopDelayStddev { + + val Name = "nodejs.eventloop.delay.stddev" + val Description = "Event loop standard deviation delay." + val Unit = "s" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Event loop utilization.

+ * @note + *

The value range is [0.0,1.0] and can be retrieved from value `performance.eventLoopUtilization([utilization1[, + * utilization2]])` + */ + object EventloopUtilization { + + val Name = "nodejs.eventloop.utilization" + val Description = "Event loop utilization." + val Unit = "1" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/ProcessExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/ProcessExperimentalMetrics.scala new file mode 100644 index 000000000..9e5ceff15 --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/ProcessExperimentalMetrics.scala @@ -0,0 +1,325 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object ProcessExperimentalMetrics { + + /** Number of times the process has been context switched. + */ + object ContextSwitches { + + val Name = "process.context_switches" + val Description = "Number of times the process has been context switched." + val Unit = "{count}" + + object AttributeSpecs { + + /** Specifies whether the context switches for this data point were voluntary or involuntary. + */ + val processContextSwitchType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("process.context_switch_type"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + processContextSwitchType, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Total CPU seconds broken down by different states. + */ + object CpuTime { + + val Name = "process.cpu.time" + val Description = "Total CPU seconds broken down by different states." + val Unit = "s" + + object AttributeSpecs { + + /** A process SHOULD be characterized either by data points with no `mode` labels, or only data + * points with `mode` labels.

+ * @note + *

Following states SHOULD be used: `user`, `system`, `wait` + */ + val cpuMode: AttributeSpec[String] = + AttributeSpec( + AttributeKey("cpu.mode"), + List( + "user", + "system", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + cpuMode, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs + * available to the process. + */ + object CpuUtilization { + + val Name = "process.cpu.utilization" + val Description = + "Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs available to the process." + val Unit = "1" + + object AttributeSpecs { + + /** A process SHOULD be characterized either by data points with no `mode` labels, or only data + * points with `mode` labels.

+ * @note + *

Following states SHOULD be used: `user`, `system`, `wait` + */ + val cpuMode: AttributeSpec[String] = + AttributeSpec( + AttributeKey("cpu.mode"), + List( + "user", + "system", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + cpuMode, + ) + } + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Disk bytes transferred. + */ + object DiskIo { + + val Name = "process.disk.io" + val Description = "Disk bytes transferred." + val Unit = "By" + + object AttributeSpecs { + + /** The disk IO operation direction. + */ + val diskIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("disk.io.direction"), + List( + "read", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + diskIoDirection, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The amount of physical memory in use. + */ + object MemoryUsage { + + val Name = "process.memory.usage" + val Description = "The amount of physical memory in use." + val Unit = "By" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** The amount of committed virtual memory. + */ + object MemoryVirtual { + + val Name = "process.memory.virtual" + val Description = "The amount of committed virtual memory." + val Unit = "By" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Network bytes transferred. + */ + object NetworkIo { + + val Name = "process.network.io" + val Description = "Network bytes transferred." + val Unit = "By" + + object AttributeSpecs { + + /** The network IO operation direction. + */ + val networkIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.io.direction"), + List( + "transmit", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + networkIoDirection, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of file descriptors in use by the process. + */ + object OpenFileDescriptorCount { + + val Name = "process.open_file_descriptor.count" + val Description = "Number of file descriptors in use by the process." + val Unit = "{count}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of page faults the process has made. + */ + object PagingFaults { + + val Name = "process.paging.faults" + val Description = "Number of page faults the process has made." + val Unit = "{fault}" + + object AttributeSpecs { + + /** The type of page fault for this data point. Type `major` is for major/hard page faults, and `minor` is for + * minor/soft page faults. + */ + val processPagingFaultType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("process.paging.fault_type"), + List( + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + processPagingFaultType, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Process threads count. + */ + object ThreadCount { + + val Name = "process.thread.count" + val Description = "Process threads count." + val Unit = "{thread}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/RpcExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/RpcExperimentalMetrics.scala new file mode 100644 index 000000000..6092b3947 --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/RpcExperimentalMetrics.scala @@ -0,0 +1,256 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object RpcExperimentalMetrics { + + /** Measures the duration of outbound RPC.

+ * @note + *

While streaming RPCs may record this metric as start-of-batch to end-of-batch, it's hard to interpret in + * practice.

Streaming: N/A. + */ + object ClientDuration { + + val Name = "rpc.client.duration" + val Description = "Measures the duration of outbound RPC." + val Unit = "ms" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the size of RPC request messages (uncompressed).

+ * @note + *

Streaming: Recorded per message in a streaming batch + */ + object ClientRequestSize { + + val Name = "rpc.client.request.size" + val Description = + "Measures the size of RPC request messages (uncompressed)." + val Unit = "By" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the number of messages received per RPC.

+ * @note + *

Should be 1 for all non-streaming RPCs.

Streaming: This metric is required for server + * and client streaming RPCs + */ + object ClientRequestsPerRpc { + + val Name = "rpc.client.requests_per_rpc" + val Description = "Measures the number of messages received per RPC." + val Unit = "{count}" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the size of RPC response messages (uncompressed).

+ * @note + *

Streaming: Recorded per response in a streaming batch + */ + object ClientResponseSize { + + val Name = "rpc.client.response.size" + val Description = + "Measures the size of RPC response messages (uncompressed)." + val Unit = "By" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the number of messages sent per RPC.

+ * @note + *

Should be 1 for all non-streaming RPCs.

Streaming: This metric is required for server + * and client streaming RPCs + */ + object ClientResponsesPerRpc { + + val Name = "rpc.client.responses_per_rpc" + val Description = "Measures the number of messages sent per RPC." + val Unit = "{count}" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the duration of inbound RPC.

+ * @note + *

While streaming RPCs may record this metric as start-of-batch to end-of-batch, it's hard to interpret in + * practice.

Streaming: N/A. + */ + object ServerDuration { + + val Name = "rpc.server.duration" + val Description = "Measures the duration of inbound RPC." + val Unit = "ms" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the size of RPC request messages (uncompressed).

+ * @note + *

Streaming: Recorded per message in a streaming batch + */ + object ServerRequestSize { + + val Name = "rpc.server.request.size" + val Description = + "Measures the size of RPC request messages (uncompressed)." + val Unit = "By" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the number of messages received per RPC.

+ * @note + *

Should be 1 for all non-streaming RPCs.

Streaming : This metric is required for server + * and client streaming RPCs + */ + object ServerRequestsPerRpc { + + val Name = "rpc.server.requests_per_rpc" + val Description = "Measures the number of messages received per RPC." + val Unit = "{count}" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the size of RPC response messages (uncompressed).

+ * @note + *

Streaming: Recorded per response in a streaming batch + */ + object ServerResponseSize { + + val Name = "rpc.server.response.size" + val Description = + "Measures the size of RPC response messages (uncompressed)." + val Unit = "By" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measures the number of messages sent per RPC.

+ * @note + *

Should be 1 for all non-streaming RPCs.

Streaming: This metric is required for server + * and client streaming RPCs + */ + object ServerResponsesPerRpc { + + val Name = "rpc.server.responses_per_rpc" + val Description = "Measures the number of messages sent per RPC." + val Unit = "{count}" + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + +} diff --git a/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/SystemExperimentalMetrics.scala b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/SystemExperimentalMetrics.scala new file mode 100644 index 000000000..e55c91d6d --- /dev/null +++ b/semconv/metrics/experimental/src/main/scala/org/typelevel/otel4s/semconv/experimental/metrics/SystemExperimentalMetrics.scala @@ -0,0 +1,1311 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object SystemExperimentalMetrics { + + /** Reports the current frequency of the CPU in Hz + */ + object CpuFrequency { + + val Name = "system.cpu.frequency" + val Description = "Reports the current frequency of the CPU in Hz" + val Unit = "{Hz}" + + object AttributeSpecs { + + /** The logical CPU number [0..n-1] + */ + val systemCpuLogicalNumber: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("system.cpu.logical_number"), + List( + 1, + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemCpuLogicalNumber, + ) + } + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Reports the number of logical (virtual) processor cores created by the operating system to manage multitasking + */ + object CpuLogicalCount { + + val Name = "system.cpu.logical.count" + val Description = + "Reports the number of logical (virtual) processor cores created by the operating system to manage multitasking" + val Unit = "{cpu}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Reports the number of actual physical processor cores on the hardware + */ + object CpuPhysicalCount { + + val Name = "system.cpu.physical.count" + val Description = + "Reports the number of actual physical processor cores on the hardware" + val Unit = "{cpu}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Seconds each logical CPU spent on each mode + */ + object CpuTime { + + val Name = "system.cpu.time" + val Description = "Seconds each logical CPU spent on each mode" + val Unit = "s" + + object AttributeSpecs { + + /** The CPU mode for this data point. A system's CPU SHOULD be characterized either by data points with + * no `mode` labels, or only data points with `mode` labels.

+ * @note + *

Following states SHOULD be used: `user`, `system`, `nice`, `idle`, `iowait`, `interrupt`, `steal` + */ + val cpuMode: AttributeSpec[String] = + AttributeSpec( + AttributeKey("cpu.mode"), + List( + "user", + "system", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The logical CPU number [0..n-1] + */ + val systemCpuLogicalNumber: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("system.cpu.logical_number"), + List( + 1, + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + cpuMode, + systemCpuLogicalNumber, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Difference in system.cpu.time since the last measurement, divided by the elapsed time and number of logical CPUs + */ + object CpuUtilization { + + val Name = "system.cpu.utilization" + val Description = + "Difference in system.cpu.time since the last measurement, divided by the elapsed time and number of logical CPUs" + val Unit = "1" + + object AttributeSpecs { + + /** The CPU mode for this data point. A system's CPU SHOULD be characterized either by data points with + * no `mode` labels, or only data points with `mode` labels.

+ * @note + *

Following modes SHOULD be used: `user`, `system`, `nice`, `idle`, `iowait`, `interrupt`, `steal` + */ + val cpuMode: AttributeSpec[String] = + AttributeSpec( + AttributeKey("cpu.mode"), + List( + "user", + "system", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The logical CPU number [0..n-1] + */ + val systemCpuLogicalNumber: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("system.cpu.logical_number"), + List( + 1, + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + cpuMode, + systemCpuLogicalNumber, + ) + } + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object DiskIo { + + val Name = "system.disk.io" + val Description = "" + val Unit = "By" + + object AttributeSpecs { + + /** The disk IO operation direction. + */ + val diskIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("disk.io.direction"), + List( + "read", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + diskIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Time disk spent activated

+ * @note + *

The real elapsed time ("wall clock") used in the I/O path (time from operations running in parallel are not + * counted). Measured as:

+ */ + object DiskIoTime { + + val Name = "system.disk.io_time" + val Description = "Time disk spent activated" + val Unit = "s" + + object AttributeSpecs { + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object DiskMerged { + + val Name = "system.disk.merged" + val Description = "" + val Unit = "{operation}" + + object AttributeSpecs { + + /** The disk IO operation direction. + */ + val diskIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("disk.io.direction"), + List( + "read", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + diskIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Sum of the time each operation took to complete

+ * @note + *

Because it is the sum of time each request took, parallel-issued requests each contribute to make the count + * grow. Measured as:

+ */ + object DiskOperationTime { + + val Name = "system.disk.operation_time" + val Description = "Sum of the time each operation took to complete" + val Unit = "s" + + object AttributeSpecs { + + /** The disk IO operation direction. + */ + val diskIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("disk.io.direction"), + List( + "read", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + diskIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object DiskOperations { + + val Name = "system.disk.operations" + val Description = "" + val Unit = "{operation}" + + object AttributeSpecs { + + /** The disk IO operation direction. + */ + val diskIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("disk.io.direction"), + List( + "read", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + diskIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object FilesystemUsage { + + val Name = "system.filesystem.usage" + val Description = "" + val Unit = "By" + + object AttributeSpecs { + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem mode + */ + val systemFilesystemMode: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.mode"), + List( + "rw, ro", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem mount path + */ + val systemFilesystemMountpoint: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.mountpoint"), + List( + "/mnt/data", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem state + */ + val systemFilesystemState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.state"), + List( + "used", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem type + */ + val systemFilesystemType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.type"), + List( + "ext4", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemDevice, + systemFilesystemMode, + systemFilesystemMountpoint, + systemFilesystemState, + systemFilesystemType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object FilesystemUtilization { + + val Name = "system.filesystem.utilization" + val Description = "" + val Unit = "1" + + object AttributeSpecs { + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem mode + */ + val systemFilesystemMode: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.mode"), + List( + "rw, ro", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem mount path + */ + val systemFilesystemMountpoint: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.mountpoint"), + List( + "/mnt/data", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem state + */ + val systemFilesystemState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.state"), + List( + "used", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The filesystem type + */ + val systemFilesystemType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.filesystem.type"), + List( + "ext4", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemDevice, + systemFilesystemMode, + systemFilesystemMountpoint, + systemFilesystemState, + systemFilesystemType, + ) + } + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** An estimate of how much memory is available for starting new applications, without causing swapping

+ * @note + *

This is an alternative to `system.memory.usage` metric with `state=free`. Linux starting from 3.14 exports + * "available" memory. It takes "free" memory as a baseline, and then factors in kernel-specific values. This is + * supposed to be more accurate than just "free" memory. For reference, see the calculations here. See also `MemAvailable` in /proc/meminfo. + */ + object LinuxMemoryAvailable { + + val Name = "system.linux.memory.available" + val Description = + "An estimate of how much memory is available for starting new applications, without causing swapping" + val Unit = "By" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Reports the memory used by the Linux kernel for managing caches of frequently used objects.

+ * @note + *

The sum over the `reclaimable` and `unreclaimable` state values in `linux.memory.slab.usage` SHOULD be equal + * to the total slab memory available on the system. Note that the total slab memory is not constant and may vary + * over time. See also the Slab allocator and + * `Slab` in /proc/meminfo. + */ + object LinuxMemorySlabUsage { + + val Name = "system.linux.memory.slab.usage" + val Description = + "Reports the memory used by the Linux kernel for managing caches of frequently used objects." + val Unit = "By" + + object AttributeSpecs { + + /** The Linux Slab memory state + */ + val linuxMemorySlabState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("linux.memory.slab.state"), + List( + "reclaimable", + "unreclaimable", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + linuxMemorySlabState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Total memory available in the system.

+ * @note + *

Its value SHOULD equal the sum of `system.memory.state` over all states. + */ + object MemoryLimit { + + val Name = "system.memory.limit" + val Description = "Total memory available in the system." + val Unit = "By" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Shared memory used (mostly by tmpfs).

+ * @note + *

Equivalent of `shared` from `free` command + * or `Shmem` from `/proc/meminfo`" + */ + object MemoryShared { + + val Name = "system.memory.shared" + val Description = "Shared memory used (mostly by tmpfs)." + val Unit = "By" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Reports memory in use by state.

+ * @note + *

The sum over all `system.memory.state` values SHOULD equal the total memory available on the system, that is + * `system.memory.limit`. + */ + object MemoryUsage { + + val Name = "system.memory.usage" + val Description = "Reports memory in use by state." + val Unit = "By" + + object AttributeSpecs { + + /** The memory state + */ + val systemMemoryState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.memory.state"), + List( + "free", + "cached", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemMemoryState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object MemoryUtilization { + + val Name = "system.memory.utilization" + val Description = "" + val Unit = "1" + + object AttributeSpecs { + + /** The memory state + */ + val systemMemoryState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.memory.state"), + List( + "free", + "cached", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemMemoryState, + ) + } + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object NetworkConnections { + + val Name = "system.network.connections" + val Description = "" + val Unit = "{connection}" + + object AttributeSpecs { + + /** OSI transport layer or inter-process communication method.

+ * @note + *

The value SHOULD be normalized to lowercase.

Consider always setting the transport when setting a + * port number, since a port number is ambiguous without knowing the transport. For example different processes + * could be listening on TCP port 12345 and UDP port 12345. + */ + val networkTransport: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.transport"), + List( + "tcp", + "udp", + ), + Requirement.recommended, + Stability.stable + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + /** A stateless protocol MUST NOT set this attribute + */ + val systemNetworkState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.network.state"), + List( + "close_wait", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + networkTransport, + systemDevice, + systemNetworkState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Count of packets that are dropped or discarded even though there was no error

+ * @note + *

Measured as:

+ */ + object NetworkDropped { + + val Name = "system.network.dropped" + val Description = + "Count of packets that are dropped or discarded even though there was no error" + val Unit = "{packet}" + + object AttributeSpecs { + + /** The network IO operation direction. + */ + val networkIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.io.direction"), + List( + "transmit", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + networkIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Count of network errors detected

+ * @note + *

Measured as:

+ */ + object NetworkErrors { + + val Name = "system.network.errors" + val Description = "Count of network errors detected" + val Unit = "{error}" + + object AttributeSpecs { + + /** The network IO operation direction. + */ + val networkIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.io.direction"), + List( + "transmit", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + networkIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object NetworkIo { + + val Name = "system.network.io" + val Description = "" + val Unit = "By" + + object AttributeSpecs { + + /** The network IO operation direction. + */ + val networkIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.io.direction"), + List( + "transmit", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + networkIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object NetworkPackets { + + val Name = "system.network.packets" + val Description = "" + val Unit = "{packet}" + + object AttributeSpecs { + + /** The network IO operation direction. + */ + val networkIoDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.io.direction"), + List( + "transmit", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The device identifier + */ + val systemDevice: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.device"), + List( + "(identifier)", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + networkIoDirection, + systemDevice, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object PagingFaults { + + val Name = "system.paging.faults" + val Description = "" + val Unit = "{fault}" + + object AttributeSpecs { + + /** The memory paging type + */ + val systemPagingType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.paging.type"), + List( + "minor", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemPagingType, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object PagingOperations { + + val Name = "system.paging.operations" + val Description = "" + val Unit = "{operation}" + + object AttributeSpecs { + + /** The paging access direction + */ + val systemPagingDirection: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.paging.direction"), + List( + "in", + ), + Requirement.recommended, + Stability.experimental + ) + + /** The memory paging type + */ + val systemPagingType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.paging.type"), + List( + "minor", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemPagingDirection, + systemPagingType, + ) + } + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Unix swap or windows pagefile usage + */ + object PagingUsage { + + val Name = "system.paging.usage" + val Description = "Unix swap or windows pagefile usage" + val Unit = "By" + + object AttributeSpecs { + + /** The memory paging state + */ + val systemPagingState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.paging.state"), + List( + "free", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemPagingState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** */ + object PagingUtilization { + + val Name = "system.paging.utilization" + val Description = "" + val Unit = "1" + + object AttributeSpecs { + + /** The memory paging state + */ + val systemPagingState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.paging.state"), + List( + "free", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemPagingState, + ) + } + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Total number of processes in each state + */ + object ProcessCount { + + val Name = "system.process.count" + val Description = "Total number of processes in each state" + val Unit = "{process}" + + object AttributeSpecs { + + /** The process state, e.g., Linux + * Process State Codes + */ + val systemProcessStatus: AttributeSpec[String] = + AttributeSpec( + AttributeKey("system.process.status"), + List( + "running", + ), + Requirement.recommended, + Stability.experimental + ) + + val specs: List[AttributeSpec[_]] = + List( + systemProcessStatus, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Total number of processes created over uptime of the host + */ + object ProcessCreated { + + val Name = "system.process.created" + val Description = + "Total number of processes created over uptime of the host" + val Unit = "{process}" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +} diff --git a/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/AttributeSpec.scala b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/AttributeSpec.scala new file mode 100644 index 000000000..d19cb5c76 --- /dev/null +++ b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/AttributeSpec.scala @@ -0,0 +1,62 @@ +/* + * Copyright 2024 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.semconv + +import org.typelevel.otel4s.AttributeKey + +/** The attribute's specification. + * + * @tparam A + * the type of the attribute + */ +sealed trait AttributeSpec[A] { + + /** The attribute's key. + */ + def key: AttributeKey[A] + + /** The list of value examples. + */ + def examples: List[A] + + /** The requirement level of the attribute. + */ + def requirement: Requirement + + /** The stability of the attribute. + */ + def stability: Stability +} + +object AttributeSpec { + + def apply[A]( + key: AttributeKey[A], + examples: List[A], + requirement: Requirement, + stability: Stability + ): AttributeSpec[A] = + Impl(key, examples, requirement, stability) + + final case class Impl[A]( + key: AttributeKey[A], + examples: List[A], + requirement: Requirement, + stability: Stability + ) extends AttributeSpec[A] + +} diff --git a/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/Requirement.scala b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/Requirement.scala new file mode 100644 index 000000000..04608e352 --- /dev/null +++ b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/Requirement.scala @@ -0,0 +1,61 @@ +/* + * Copyright 2024 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.semconv + +/** Indicates requirement level of the attribute. + */ +sealed trait Requirement { + def level: Requirement.Level + def note: Option[String] +} + +object Requirement { + sealed trait Level + object Level { + case object Required extends Level + case object ConditionalRequired extends Level + case object Recommended extends Level + case object OptIn extends Level + } + + def required: Requirement = + Impl(Level.Required, None) + + def conditionallyRequired: Requirement = + Impl(Level.ConditionalRequired, None) + + def conditionallyRequired(note: String): Requirement = + Impl(Level.ConditionalRequired, Some(note)) + + def recommended: Requirement = + Impl(Level.Recommended, None) + + def recommended(note: String): Requirement = + Impl(Level.Recommended, Some(note)) + + def optIn: Requirement = + Impl(Level.OptIn, None) + + def optIn(note: String): Requirement = + Impl(Level.OptIn, Some(note)) + + private final case class Impl( + level: Level, + note: Option[String] + ) extends Requirement + +} diff --git a/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/Stability.scala b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/Stability.scala new file mode 100644 index 000000000..b1a5d1340 --- /dev/null +++ b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/Stability.scala @@ -0,0 +1,30 @@ +/* + * Copyright 2024 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.semconv + +/** Indicates stability of the attribute. + */ +sealed trait Stability + +object Stability { + + def stable: Stability = Stable + def experimental: Stability = Experimental + + private case object Stable extends Stability + private case object Experimental extends Stability +} diff --git a/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/metrics/HttpMetrics.scala b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/metrics/HttpMetrics.scala new file mode 100644 index 000000000..b8070993c --- /dev/null +++ b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/metrics/HttpMetrics.scala @@ -0,0 +1,437 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object HttpMetrics { + + /** Duration of HTTP client requests. + */ + object ClientRequestDuration { + + val Name = "http.client.request.duration" + val Description = "Duration of HTTP client requests." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Host identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

If an HTTP client request is explicitly made to an IP address, e.g. `http://x.x.x.x:8080`, then + * `server.address` SHOULD be the IP address `x.x.x.x`. A DNS lookup SHOULD NOT be used. + */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.required, + Stability.stable + ) + + /** Port identifier of the "URI origin" + * HTTP request is sent to.

+ * @note + *

When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD + * represent the server port behind any intermediaries, for example proxies, if it's available. + */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.required, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.optIn, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Duration of HTTP server requests. + */ + object ServerRequestDuration { + + val Name = "http.server.request.duration" + val Description = "Duration of HTTP server requests." + val Unit = "s" + + object AttributeSpecs { + + /** Describes a class of error the operation ended with.

+ * @note + *

If the request fails with an error before response status code was sent or received, `error.type` SHOULD + * be set to exception type (its fully-qualified class name, if applicable) or a component-specific low + * cardinality error identifier.

If response status code was sent or received and status indicates an error + * according to HTTP span status definition, `error.type` SHOULD be set + * to the status code number (represented as a string), an exception type (if thrown) or a component-specific + * error identifier.

The `error.type` value SHOULD be predictable and SHOULD have low cardinality. + * Instrumentations SHOULD document the list of errors they report.

The cardinality of `error.type` within + * one instrumentation library SHOULD be low, but telemetry consumers that aggregate data from multiple + * instrumentation libraries and applications should be prepared for `error.type` to have high cardinality at + * query time, when no additional filters are applied.

If the request has completed successfully, + * instrumentations SHOULD NOT set `error.type`. + */ + val errorType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("error.type"), + List( + "timeout", + "java.net.UnknownHostException", + "server_certificate_invalid", + "500", + ), + Requirement.conditionallyRequired( + "If request has ended with an error." + ), + Stability.stable + ) + + /** HTTP request method.

+ * @note + *

HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines + * "known" methods as the ones listed in RFC9110 and the PATCH method defined in + * RFC5789.

If the HTTP request method is not + * known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`.

If the HTTP + * instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way + * to override the list of known HTTP methods. If this override is done via environment variable, then the + * environment variable MUST be named OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated + * list of case-sensitive known HTTP methods (this list MUST be a full override of the default known method, it + * is not a list of known methods in addition to the defaults).

HTTP method names are case-sensitive and + * `http.request.method` attribute value MUST match a known HTTP method name exactly. Instrumentations for + * specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical + * equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the + * original value. + */ + val httpRequestMethod: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.request.method"), + List( + "GET", + "POST", + "HEAD", + ), + Requirement.required, + Stability.stable + ) + + /** HTTP response status code. + */ + val httpResponseStatusCode: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("http.response.status_code"), + List( + 200, + ), + Requirement.conditionallyRequired( + "If and only if one was received/sent." + ), + Stability.stable + ) + + /** The matched route, that is, the path template in the format used by the respective server framework.

+ * @note + *

MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute + * should have low-cardinality and the URI path can NOT substitute it. SHOULD include the application root if there is one. + */ + val httpRoute: AttributeSpec[String] = + AttributeSpec( + AttributeKey("http.route"), + List( + "/users/:userID?", + "{controller}/{action}/{id?}", + ), + Requirement.conditionallyRequired("If and only if it's available"), + Stability.stable + ) + + /** OSI application layer or non-OSI equivalent.

+ * @note + *

The value SHOULD be normalized to lowercase. + */ + val networkProtocolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.name"), + List( + "http", + "spdy", + ), + Requirement.conditionallyRequired( + "If not `http` and `network.protocol.version` is set." + ), + Stability.stable + ) + + /** The actual version of the protocol used for network communication.

+ * @note + *

If protocol version is subject to negotiation (for example using ALPN), this attribute SHOULD be set to the negotiated + * version. If the actual protocol version is not known, this attribute SHOULD NOT be set. + */ + val networkProtocolVersion: AttributeSpec[String] = + AttributeSpec( + AttributeKey("network.protocol.version"), + List( + "1.0", + "1.1", + "2", + "3", + ), + Requirement.recommended, + Stability.stable + ) + + /** Name of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverAddress: AttributeSpec[String] = + AttributeSpec( + AttributeKey("server.address"), + List( + "example.com", + "10.1.2.80", + "/tmp/my.sock", + ), + Requirement.optIn, + Stability.stable + ) + + /** Port of the local HTTP server that received the request.

+ * @note + *

See Setting + * `server.address` and `server.port` attributes.

Warning Since this + * attribute is based on HTTP headers, opting in to it may allow an attacker to trigger cardinality limits, + * degrading the usefulness of the metric.
+ */ + val serverPort: AttributeSpec[Long] = + AttributeSpec( + AttributeKey("server.port"), + List( + 80, + 8080, + 443, + ), + Requirement.optIn, + Stability.stable + ) + + /** The URI scheme component identifying the used + * protocol.

+ * @note + *

The scheme of the original client request, if known (e.g. from Forwarded#proto, X-Forwarded-Proto, or a + * similar header). Otherwise, the scheme of the immediate peer request. + */ + val urlScheme: AttributeSpec[String] = + AttributeSpec( + AttributeKey("url.scheme"), + List( + "http", + "https", + ), + Requirement.required, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + errorType, + httpRequestMethod, + httpResponseStatusCode, + httpRoute, + networkProtocolName, + networkProtocolVersion, + serverAddress, + serverPort, + urlScheme, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + +} diff --git a/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/metrics/JvmMetrics.scala b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/metrics/JvmMetrics.scala new file mode 100644 index 000000000..157be625c --- /dev/null +++ b/semconv/metrics/stable/src/main/scala/org/typelevel/otel4s/semconv/metrics/JvmMetrics.scala @@ -0,0 +1,470 @@ +/* + * Copyright 2024 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 +package semconv +package metrics + +import org.typelevel.otel4s.metrics._ + +// DO NOT EDIT, this is an Auto-generated file from buildscripts/templates/registry/otel4s/metrics/SemanticMetrics.scala.j2 +object JvmMetrics { + + /** Number of classes currently loaded. + */ + object ClassCount { + + val Name = "jvm.class.count" + val Description = "Number of classes currently loaded." + val Unit = "{class}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of classes loaded since JVM start. + */ + object ClassLoaded { + + val Name = "jvm.class.loaded" + val Description = "Number of classes loaded since JVM start." + val Unit = "{class}" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of classes unloaded since JVM start. + */ + object ClassUnloaded { + + val Name = "jvm.class.unloaded" + val Description = "Number of classes unloaded since JVM start." + val Unit = "{class}" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of processors available to the Java virtual machine. + */ + object CpuCount { + + val Name = "jvm.cpu.count" + val Description = + "Number of processors available to the Java virtual machine." + val Unit = "{cpu}" + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Recent CPU utilization for the process as reported by the JVM.

+ * @note + *

The value range is [0.0,1.0]. This utilization is not defined as being for the specific interval since last + * measurement (unlike `system.cpu.utilization`). Reference. + */ + object CpuRecentUtilization { + + val Name = "jvm.cpu.recent_utilization" + val Description = + "Recent CPU utilization for the process as reported by the JVM." + val Unit = "1" + + def create[F[_]: Meter]: F[Gauge[F, Long]] = + Meter[F] + .gauge[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** CPU time used by the process as reported by the JVM. + */ + object CpuTime { + + val Name = "jvm.cpu.time" + val Description = "CPU time used by the process as reported by the JVM." + val Unit = "s" + + def create[F[_]: Meter]: F[Counter[F, Long]] = + Meter[F] + .counter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Duration of JVM garbage collection actions. + */ + object GcDuration { + + val Name = "jvm.gc.duration" + val Description = "Duration of JVM garbage collection actions." + val Unit = "s" + + object AttributeSpecs { + + /** Name of the garbage collector action.

+ * @note + *

Garbage collector action is generally obtained via GarbageCollectionNotificationInfo#getGcAction(). + */ + val jvmGcAction: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.gc.action"), + List( + "end of minor GC", + "end of major GC", + ), + Requirement.recommended, + Stability.stable + ) + + /** Name of the garbage collector.

+ * @note + *

Garbage collector name is generally obtained via GarbageCollectionNotificationInfo#getGcName(). + */ + val jvmGcName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.gc.name"), + List( + "G1 Young Generation", + "G1 Old Generation", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmGcAction, + jvmGcName, + ) + } + + def create[F[_]: Meter]( + boundaries: BucketBoundaries + ): F[Histogram[F, Double]] = + Meter[F] + .histogram[Double](Name) + .withDescription(Description) + .withUnit(Unit) + .withExplicitBucketBoundaries(boundaries) + .create + + } + + /** Measure of memory committed. + */ + object MemoryCommitted { + + val Name = "jvm.memory.committed" + val Description = "Measure of memory committed." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of max obtainable memory. + */ + object MemoryLimit { + + val Name = "jvm.memory.limit" + val Description = "Measure of max obtainable memory." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of memory used. + */ + object MemoryUsed { + + val Name = "jvm.memory.used" + val Description = "Measure of memory used." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Measure of memory used, as measured after the most recent garbage collection event on this pool. + */ + object MemoryUsedAfterLastGc { + + val Name = "jvm.memory.used_after_last_gc" + val Description = + "Measure of memory used, as measured after the most recent garbage collection event on this pool." + val Unit = "By" + + object AttributeSpecs { + + /** Name of the memory pool.

+ * @note + *

Pool names are generally obtained via MemoryPoolMXBean#getName(). + */ + val jvmMemoryPoolName: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.pool.name"), + List( + "G1 Old Gen", + "G1 Eden space", + "G1 Survivor Space", + ), + Requirement.recommended, + Stability.stable + ) + + /** The type of memory. + */ + val jvmMemoryType: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.memory.type"), + List( + "heap", + "non_heap", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmMemoryPoolName, + jvmMemoryType, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + + /** Number of executing platform threads. + */ + object ThreadCount { + + val Name = "jvm.thread.count" + val Description = "Number of executing platform threads." + val Unit = "{thread}" + + object AttributeSpecs { + + /** Whether the thread is daemon or not. + */ + val jvmThreadDaemon: AttributeSpec[Boolean] = + AttributeSpec( + AttributeKey("jvm.thread.daemon"), + List( + ), + Requirement.recommended, + Stability.stable + ) + + /** State of the thread. + */ + val jvmThreadState: AttributeSpec[String] = + AttributeSpec( + AttributeKey("jvm.thread.state"), + List( + "runnable", + "blocked", + ), + Requirement.recommended, + Stability.stable + ) + + val specs: List[AttributeSpec[_]] = + List( + jvmThreadDaemon, + jvmThreadState, + ) + } + + def create[F[_]: Meter]: F[UpDownCounter[F, Long]] = + Meter[F] + .upDownCounter[Long](Name) + .withDescription(Description) + .withUnit(Unit) + .create + + } + +}