mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
eb698ed5cea266d14b2aa0d0ea678c201e8dc40e
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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>
|
||
|
|
3264bf171b |
refactor: modernize code (#7850)
Enable the [`modernize`](https://golangci-lint.run/docs/linters/configuration/#modernize) linter in the golangci-lint configuration and fix the reported issues by running `golangci-lint run --enable-only modernize --fix`. The `modernize` linter is internally the same as [gopls modernize](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize). Similar to #7089. |
||
|
|
5e1c62a2d5 |
Modernize (#7089)
Use https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize to update code to new style. --------- Co-authored-by: Flc゛ <four_leaf_clover@foxmail.com> Co-authored-by: Damien Mathieu <42@dmathieu.com> |
||
|
|
5bf8691520 |
Functionality moved from internal/attribute to attribute (#6580)
Resolve #6525 ## Description This pull request addresses the issue of moving internal functionality from the `internal/attribute` package to the `attribute `package within the open-telemetry/opentelemetry-go repository. The goal is to ensure that none of this functionality is added to the public API of the attribute or any other public package while improving the naming, layout, and following Go idioms. ## Changes made 1. Moved Functions to `attribute/internal` - Created a new internal package within the `attribute` package: `attribute/internal`. - Moved `attribute.go` and `attribute_test.go` into the `attribute/internal` - Moved the following functions from `internal/attribute` to `attribute/internal` 2 . Changes in .golangci.yml - Removed line 67 and 68 from .golangci.yml --------- Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> |