1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-19 21:45:50 +02:00
Tyler Yahn 9587c57d48 Optimize attribute slice conversion (#8039)
This PR refactors the internal slice conversion helpers in
`attribute/internal` to use generics and adds explicit short-length fast
paths for slice-backed attribute values.

The main changes are:

- replace the old type-specific internal helpers with generic
`SliceValue[T]` and `AsSlice[T]`
- preserve the comparable-array storage model used by `Value` and
`KeyValue`
- add fixed-size fast paths for lengths `0..3`
- add a matching short-length fast path in `IntSliceValue` before
falling back to `[]int64` conversion
- keep reflection as the fallback for larger slice lengths
- keep the existing `Value.As*Slice()` methods in `attribute` as thin
wrappers over the generic internal helpers
- add type-mismatch coverage for `AsSlice[T]`
- expand the benchmark suite so it measures both:
  - short slices that hit the new fixed-size path
  - longer slices that still use the reflective fallback

## Rationale

The package still needs reflection for arbitrary runtime lengths because
slice values are stored as comparable arrays behind `any`, and Go cannot
construct a runtime-sized array type without `reflect.ArrayOf`.

The fast path is intentionally small. The cutoff of `0..3` is based on a
combination of:

- benchmark gains for short slices
- semantic convention examples where `1..3` values are common
- downstream source analysis, which found that most external call sites
pass variables rather than inline literals, so static source scanning
does not justify a larger cutoff

## External Usage Validation

Downstream scan:

- sampled repos cloned and scanned: `285`
- most external call sites pass dynamic slice variables, not inline
literals
- final literal counts recovered statically:
- `StringSlice`: total `131`, dynamic `121`, literal len `0`: `1`, len
`1`: `8`, len `2`: `1`
  - `IntSlice`: total `5`, all dynamic
  - `Int64Slice`: total `6`, all dynamic
  - `BoolSlice`: total `3`, all dynamic
  - `Float64Slice`: total `3`, all dynamic

Semantic conventions reviewed locally in `semantic-conventions` include
several slice-valued attributes where short lists are normal, for
example:

- `browser.brands`
- `gen_ai.request.stop_sequences`
- `gen_ai.request.encoding_formats`
- `gen_ai.response.finish_reasons`
- `user.roles`
- `file.attributes`

There are also clearly unbounded cases like headers, metadata, command
args, and some cloud/provider arrays. That combination supports a small
fast path, but not an assumption that all real-world slices are tiny.

## Benchmarks

Changes to the benchmarks:

- internal microbenchmarks now run both `Len2` and `Len8`
- public `attribute` benchmarks now run both `Len2` and `Len8`
- repeated benchmark runs were compared with `benchstat`

`Len2` exercises the new fixed-size path. `Len8` exercises the
reflective fallback path.

Headline result:

- short slices improve substantially
- large slices stay close to baseline because they still use reflection
- `IntSlice` now gets the same short-slice win as the other slice types,
but larger `[]int` values still pay the `[]int` to `[]int64` conversion
cost

### Internal Helpers

```text
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/attribute/internal
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
                         │ /tmp/bench-internal-base-v2.txt │  /tmp/bench-internal-current-v2.txt  │
                         │             sec/op              │    sec/op     vs base                │
BoolSliceValue/Len2-8                        124.00n ±  4%   19.36n ± 12%  -84.39% (p=0.000 n=12)
BoolSliceValue/Len8-8                         129.9n ±  2%   136.8n ±  3%   +5.31% (p=0.000 n=12)
Int64SliceValue/Len2-8                       146.00n ± 26%   26.13n ±  3%  -82.10% (p=0.000 n=12)
Int64SliceValue/Len8-8                        172.1n ±  4%   174.9n ±  5%        ~ (p=0.443 n=12)
Float64SliceValue/Len2-8                     151.70n ±  2%   26.25n ±  2%  -82.70% (p=0.000 n=12)
Float64SliceValue/Len8-8                      173.6n ±  2%   169.7n ±  4%        ~ (p=0.155 n=12)
StringSliceValue/Len2-8                      177.15n ±  2%   42.03n ±  3%  -76.27% (p=0.000 n=12)
StringSliceValue/Len8-8                       217.2n ±  6%   219.1n ±  6%        ~ (p=0.504 n=12)
AsFloat64Slice/Len2-8                         96.77n ±  3%   63.05n ± 27%  -34.84% (p=0.000 n=12)
AsFloat64Slice/Len8-8                         123.8n ± 18%   117.1n ±  4%        ~ (p=1.000 n=12)
geomean                                       147.6n         71.85n        -51.33%

                         │ /tmp/bench-internal-base-v2.txt │  /tmp/bench-internal-current-v2.txt  │
                         │              B/op               │    B/op     vs base                  │
BoolSliceValue/Len2-8                           4.000 ± 0%   2.000 ± 0%  -50.00% (p=0.000 n=12)
BoolSliceValue/Len8-8                           16.00 ± 0%   16.00 ± 0%        ~ (p=1.000 n=12) ¹
Int64SliceValue/Len2-8                          32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
Int64SliceValue/Len8-8                          128.0 ± 0%   128.0 ± 0%        ~ (p=1.000 n=12) ¹
Float64SliceValue/Len2-8                        32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
Float64SliceValue/Len8-8                        128.0 ± 0%   128.0 ± 0%        ~ (p=1.000 n=12) ¹
StringSliceValue/Len2-8                         64.00 ± 0%   32.00 ± 0%  -50.00% (p=0.000 n=12)
StringSliceValue/Len8-8                         256.0 ± 0%   256.0 ± 0%        ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len2-8                           40.00 ± 0%   40.00 ± 0%        ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len8-8                           88.00 ± 0%   88.00 ± 0%        ~ (p=1.000 n=12) ¹
geomean                                         47.77        36.21       -24.21%
¹ all samples are equal

                         │ /tmp/bench-internal-base-v2.txt │  /tmp/bench-internal-current-v2.txt  │
                         │            allocs/op            │ allocs/op   vs base                  │
BoolSliceValue/Len2-8                           2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
BoolSliceValue/Len8-8                           2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
Int64SliceValue/Len2-8                          2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
Int64SliceValue/Len8-8                          2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
Float64SliceValue/Len2-8                        2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
Float64SliceValue/Len8-8                        2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
StringSliceValue/Len2-8                         2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
StringSliceValue/Len8-8                         2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len2-8                           2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
AsFloat64Slice/Len8-8                           2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
geomean                                         2.000        1.516       -24.21%
¹ all samples are equal
```

### Public `attribute` API

```text
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/attribute
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
                                   │ /tmp/bench-attr-base-v2.txt │    /tmp/bench-attr-current-v3.txt    │
                                   │           sec/op            │    sec/op     vs base                │
BoolSlice/Len2/Value-8                             130.65n ±  3%   16.90n ±  3%  -87.06% (p=0.000 n=12)
BoolSlice/Len2/KeyValue-8                          135.85n ± 14%   23.49n ±  2%  -82.71% (p=0.000 n=12)
BoolSlice/Len2/AsBoolSlice-8                        50.22n ±  1%   19.12n ± 15%  -61.92% (p=0.000 n=12)
BoolSlice/Len2/Emit-8                               343.9n ±  1%   293.4n ±  1%  -14.68% (p=0.000 n=12)
BoolSlice/Len8/Value-8                              134.7n ±  2%   136.4n ±  2%   +1.22% (p=0.011 n=12)
BoolSlice/Len8/KeyValue-8                           137.7n ±  2%   140.6n ±  2%   +2.11% (p=0.046 n=12)
BoolSlice/Len8/AsBoolSlice-8                        53.59n ± 22%   61.15n ± 22%  +14.11% (p=0.020 n=12)
BoolSlice/Len8/Emit-8                               773.5n ±  1%   788.5n ±  2%   +1.93% (p=0.028 n=12)
IntSlice/Len2/Value-8                              140.85n ±  2%   36.20n ±  3%  -74.30% (p=0.000 n=12)
IntSlice/Len2/KeyValue-8                           149.65n ±  2%   42.84n ±  3%  -71.37% (p=0.000 n=12)
IntSlice/Len2/Emit-8                                318.2n ±  3%   279.6n ± 15%  -12.15% (p=0.012 n=12)
IntSlice/Len8/Value-8                               217.9n ±  1%   228.8n ±  2%   +5.00% (p=0.001 n=12)
IntSlice/Len8/KeyValue-8                            225.6n ± 29%   232.3n ±  3%        ~ (p=0.767 n=12)
IntSlice/Len8/Emit-8                                480.0n ±  1%   478.6n ±  2%        ~ (p=0.899 n=12)
Int64Slice/Len2/Value-8                            150.90n ±  1%   27.43n ±  3%  -81.82% (p=0.000 n=12)
Int64Slice/Len2/KeyValue-8                         152.05n ±  1%   34.34n ±  3%  -77.42% (p=0.000 n=12)
Int64Slice/Len2/AsInt64Slice-8                      58.69n ±  4%   28.58n ±  3%  -51.30% (p=0.000 n=12)
Int64Slice/Len2/Emit-8                              318.4n ±  3%   273.9n ±  1%  -13.99% (p=0.000 n=12)
Int64Slice/Len8/Value-8                             173.0n ±  8%   177.8n ±  2%        ~ (p=0.173 n=12)
Int64Slice/Len8/KeyValue-8                          184.0n ± 24%   184.3n ±  2%        ~ (p=0.701 n=12)
Int64Slice/Len8/AsInt64Slice-8                      72.04n ±  2%   83.05n ±  2%  +15.30% (p=0.000 n=12)
Int64Slice/Len8/Emit-8                              474.9n ± 19%   501.9n ± 18%   +5.67% (p=0.020 n=12)
Float64Slice/Len2/Value-8                          150.95n ±  3%   26.92n ±  3%  -82.17% (p=0.000 n=12)
Float64Slice/Len2/KeyValue-8                       153.95n ±  3%   33.08n ±  2%  -78.52% (p=0.000 n=12)
Float64Slice/Len2/AsFloat64Slice-8                  60.31n ± 24%   27.02n ±  1%  -55.19% (p=0.000 n=12)
Float64Slice/Len2/Emit-8                            434.2n ±  2%   380.4n ±  1%  -12.40% (p=0.000 n=12)
Float64Slice/Len8/Value-8                           173.7n ±  2%   175.2n ± 25%        ~ (p=0.248 n=12)
Float64Slice/Len8/KeyValue-8                        174.0n ±  4%   175.2n ±  2%        ~ (p=0.702 n=12)
Float64Slice/Len8/AsFloat64Slice-8                  71.17n ±  8%   78.00n ±  4%   +9.58% (p=0.007 n=12)
Float64Slice/Len8/Emit-8                            920.6n ± 20%   909.9n ±  8%        ~ (p=0.378 n=12)
StringSlice/Len2/Value-8                           174.00n ±  5%   41.97n ± 20%  -75.88% (p=0.000 n=12)
StringSlice/Len2/KeyValue-8                        179.85n ±  2%   46.76n ±  2%  -74.00% (p=0.000 n=12)
StringSlice/Len2/AsStringSlice-8                    76.03n ±  5%   39.83n ±  3%  -47.61% (p=0.000 n=12)
StringSlice/Len2/Emit-8                             386.0n ±  2%   332.3n ±  3%  -13.91% (p=0.000 n=12)
StringSlice/Len8/Value-8                            225.4n ±  4%   226.2n ±  4%        ~ (p=0.311 n=12)
StringSlice/Len8/KeyValue-8                         228.1n ±  7%   234.2n ±  4%        ~ (p=0.386 n=12)
StringSlice/Len8/AsStringSlice-8                    110.8n ± 25%   117.8n ±  4%        ~ (p=0.173 n=12)
StringSlice/Len8/Emit-8                             658.7n ±  3%   647.9n ±  5%        ~ (p=0.319 n=12)
geomean                                             181.7n         110.7n        -39.08%

                                   │ /tmp/bench-attr-base-v2.txt │    /tmp/bench-attr-current-v3.txt    │
                                   │            B/op             │    B/op     vs base                  │
BoolSlice/Len2/Value-8                                4.000 ± 0%   2.000 ± 0%  -50.00% (p=0.000 n=12)
BoolSlice/Len2/KeyValue-8                             4.000 ± 0%   2.000 ± 0%  -50.00% (p=0.000 n=12)
BoolSlice/Len2/AsBoolSlice-8                          2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len2/Emit-8                                 40.00 ± 0%   40.00 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Value-8                                16.00 ± 0%   16.00 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/KeyValue-8                             16.00 ± 0%   16.00 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/AsBoolSlice-8                          8.000 ± 0%   8.000 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Emit-8                                 88.00 ± 0%   88.00 ± 0%        ~ (p=1.000 n=12) ¹
IntSlice/Len2/Value-8                                 32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
IntSlice/Len2/KeyValue-8                              32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
IntSlice/Len2/Emit-8                                  56.00 ± 0%   56.00 ± 0%        ~ (p=1.000 n=12) ¹
IntSlice/Len8/Value-8                                 128.0 ± 0%   192.0 ± 0%  +50.00% (p=0.000 n=12)
IntSlice/Len8/KeyValue-8                              128.0 ± 0%   192.0 ± 0%  +50.00% (p=0.000 n=12)
IntSlice/Len8/Emit-8                                  136.0 ± 0%   136.0 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Value-8                               32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
Int64Slice/Len2/KeyValue-8                            32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
Int64Slice/Len2/AsInt64Slice-8                        16.00 ± 0%   16.00 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Emit-8                                56.00 ± 0%   56.00 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Value-8                               128.0 ± 0%   128.0 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/KeyValue-8                            128.0 ± 0%   128.0 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/AsInt64Slice-8                        64.00 ± 0%   64.00 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Emit-8                                136.0 ± 0%   136.0 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Value-8                             32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
Float64Slice/Len2/KeyValue-8                          32.00 ± 0%   16.00 ± 0%  -50.00% (p=0.000 n=12)
Float64Slice/Len2/AsFloat64Slice-8                    16.00 ± 0%   16.00 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Emit-8                              56.00 ± 0%   56.00 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Value-8                             128.0 ± 0%   128.0 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/KeyValue-8                          128.0 ± 0%   128.0 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/AsFloat64Slice-8                    64.00 ± 0%   64.00 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Emit-8                              136.0 ± 0%   136.0 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len2/Value-8                              64.00 ± 0%   32.00 ± 0%  -50.00% (p=0.000 n=12)
StringSlice/Len2/KeyValue-8                           64.00 ± 0%   32.00 ± 0%  -50.00% (p=0.000 n=12)
StringSlice/Len2/AsStringSlice-8                      32.00 ± 0%   32.00 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len2/Emit-8                               120.0 ± 0%   120.0 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/Value-8                              256.0 ± 0%   256.0 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/KeyValue-8                           256.0 ± 0%   256.0 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/AsStringSlice-8                      128.0 ± 0%   128.0 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/Emit-8                               344.0 ± 0%   344.0 ± 0%        ~ (p=1.000 n=12) ¹
geomean                                               49.39        42.05       -14.88%
¹ all samples are equal

                                   │ /tmp/bench-attr-base-v2.txt │    /tmp/bench-attr-current-v3.txt    │
                                   │          allocs/op          │ allocs/op   vs base                  │
BoolSlice/Len2/Value-8                                2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
BoolSlice/Len2/KeyValue-8                             2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
BoolSlice/Len2/AsBoolSlice-8                          1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len2/Emit-8                                 5.000 ± 0%   5.000 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Value-8                                2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/KeyValue-8                             2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/AsBoolSlice-8                          1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
BoolSlice/Len8/Emit-8                                 11.00 ± 0%   11.00 ± 0%        ~ (p=1.000 n=12) ¹
IntSlice/Len2/Value-8                                 2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
IntSlice/Len2/KeyValue-8                              2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
IntSlice/Len2/Emit-8                                  4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
IntSlice/Len8/Value-8                                 2.000 ± 0%   3.000 ± 0%  +50.00% (p=0.000 n=12)
IntSlice/Len8/KeyValue-8                              2.000 ± 0%   3.000 ± 0%  +50.00% (p=0.000 n=12)
IntSlice/Len8/Emit-8                                  4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Value-8                               2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
Int64Slice/Len2/KeyValue-8                            2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
Int64Slice/Len2/AsInt64Slice-8                        1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len2/Emit-8                                4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Value-8                               2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/KeyValue-8                            2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/AsInt64Slice-8                        1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
Int64Slice/Len8/Emit-8                                4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Value-8                             2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
Float64Slice/Len2/KeyValue-8                          2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
Float64Slice/Len2/AsFloat64Slice-8                    1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len2/Emit-8                              4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Value-8                             2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/KeyValue-8                          2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/AsFloat64Slice-8                    1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
Float64Slice/Len8/Emit-8                              4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len2/Value-8                              2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
StringSlice/Len2/KeyValue-8                           2.000 ± 0%   1.000 ± 0%  -50.00% (p=0.000 n=12)
StringSlice/Len2/AsStringSlice-8                      1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len2/Emit-8                               4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/Value-8                              2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/KeyValue-8                           2.000 ± 0%   2.000 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/AsStringSlice-8                      1.000 ± 0%   1.000 ± 0%        ~ (p=1.000 n=12) ¹
StringSlice/Len8/Emit-8                               4.000 ± 0%   4.000 ± 0%        ~ (p=1.000 n=12) ¹
geomean                                               2.143        1.824       -14.88%
¹ all samples are equal
```

## Notes

- `Len2` is where the wins show up because those calls avoid
`reflect.ArrayOf` and one allocation.
- `Len8` stays much closer to baseline because it still uses the
reflective path.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: David Ashpole <dashpole@google.com>
2026-03-12 09:33:23 -07:00
2025-07-29 10:19:11 +02:00
2026-03-02 22:25:05 +01:00
2025-01-29 14:20:03 -08:00
2025-07-23 17:51:03 -07:00
2025-11-19 11:06:20 +01:00
2025-07-02 09:38:02 +02:00
2026-03-02 22:25:05 +01:00

OpenTelemetry-Go

ci codecov.io PkgGoDev Go Report Card OpenSSF Scorecard OpenSSF Best Practices Fuzzing Status FOSSA Status Slack

OpenTelemetry-Go is the Go implementation of OpenTelemetry. It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms.

Project Status

Signal Status
Traces Stable
Metrics Stable
Logs Beta1

Progress and status specific to this repository is tracked in our project boards and milestones.

Project versioning information and stability guarantees can be found in the versioning documentation.

Compatibility

OpenTelemetry-Go ensures compatibility with the current supported versions of the Go language:

Each major Go release is supported until there are two newer major releases. For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release.

For versions of Go that are no longer supported upstream, opentelemetry-go will stop ensuring compatibility with these versions in the following manner:

  • A minor release of opentelemetry-go will be made to add support for the new supported release of Go.
  • The following minor release of opentelemetry-go will remove compatibility testing for the oldest (now archived upstream) version of Go. This, and future, releases of opentelemetry-go may include features only supported by the currently supported versions of Go.

Currently, this project supports the following environments.

OS Go Version Architecture
Ubuntu 1.26 amd64
Ubuntu 1.25 amd64
Ubuntu 1.26 386
Ubuntu 1.25 386
Ubuntu 1.26 arm64
Ubuntu 1.25 arm64
macOS 1.26 amd64
macOS 1.25 amd64
macOS 1.26 arm64
macOS 1.25 arm64
Windows 1.26 amd64
Windows 1.25 amd64
Windows 1.26 386
Windows 1.25 386

While this project should work for other systems, no compatibility guarantees are made for those systems currently.

Getting Started

You can find a getting started guide on opentelemetry.io.

OpenTelemetry's goal is to provide a single set of APIs to capture distributed traces and metrics from your application and send them to an observability platform. This project allows you to do just that for applications written in Go. There are two steps to this process: instrument your application, and configure an exporter.

Instrumentation

To start capturing distributed traces and metric events from your application it first needs to be instrumented. The easiest way to do this is by using an instrumentation library for your code. Be sure to check out the officially supported instrumentation libraries.

If you need to extend the telemetry an instrumentation library provides or want to build your own instrumentation for your application directly you will need to use the Go otel package. The examples are a good way to see some practical uses of this process.

Export

Now that your application is instrumented to collect telemetry, it needs an export pipeline to send that telemetry to an observability platform.

All officially supported exporters for the OpenTelemetry project are contained in the exporters directory.

Exporter Logs Metrics Traces
OTLP
Prometheus
stdout
Zipkin

Contributing

See the contributing documentation.

Languages
Go 98.8%
Go Template 1.1%