1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-03 18:35:08 +02:00
Files
Tyler Yahn 6cb0e90c0e Generate gRPC Client target parsing func (#7424)
In order to add [gRPC server attributes for exporter
observability](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/otel/sdk-metrics.md#metric-otelsdkexporterspaninflight),
this information needs to be parsed into a host and port. The added
generated files provides `ParseCanonicalTarget` for this functionality.

This is added as a generated template as it is expected to be needed for
all OTLP exporters (e.g. #7404, #7353).

Split from work added to #7404 

cc @yumosx 

### Benchamarks

```
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
                                 │ otlptracegrpc-observ-target.bmark.result │
                                 │                  sec/op                  │
ParseTarget/HostName-8                                         80.90n ±  1%
ParseTarget/HostPort-8                                         123.2n ±  4%
ParseTarget/IPv4WithoutPort-8                                  94.25n ±  2%
ParseTarget/IPv4WithPort-8                                     136.2n ±  1%
ParseTarget/IPv6Bare-8                                         195.5n ±  2%
ParseTarget/IPv6Bracket-8                                      191.2n ±  3%
ParseTarget/IPv6WithPort-8                                     128.6n ±  4%
ParseTarget/UnixSocket-8                                       15.73n ±  4%
ParseTarget/UnixAbstractSocket-8                               15.71n ±  6%
ParseTarget/Passthrough-8                                      129.3n ± 18%
geomean                                                        84.98n

                                 │ otlptracegrpc-observ-target.bmark.result │
                                 │                   B/op                   │
ParseTarget/HostName-8                                         48.00 ± 0%
ParseTarget/HostPort-8                                         48.00 ± 0%
ParseTarget/IPv4WithoutPort-8                                  16.00 ± 0%
ParseTarget/IPv4WithPort-8                                     48.00 ± 0%
ParseTarget/IPv6Bare-8                                         16.00 ± 0%
ParseTarget/IPv6Bracket-8                                      16.00 ± 0%
ParseTarget/IPv6WithPort-8                                     48.00 ± 0%
ParseTarget/UnixSocket-8                                       0.000 ± 0%
ParseTarget/UnixAbstractSocket-8                               0.000 ± 0%
ParseTarget/Passthrough-8                                      48.00 ± 0%
geomean                                                                   ¹
¹ summaries must be >0 to compute geomean

                                 │ otlptracegrpc-observ-target.bmark.result │
                                 │                allocs/op                 │
ParseTarget/HostName-8                                         1.000 ± 0%
ParseTarget/HostPort-8                                         1.000 ± 0%
ParseTarget/IPv4WithoutPort-8                                  1.000 ± 0%
ParseTarget/IPv4WithPort-8                                     1.000 ± 0%
ParseTarget/IPv6Bare-8                                         1.000 ± 0%
ParseTarget/IPv6Bracket-8                                      1.000 ± 0%
ParseTarget/IPv6WithPort-8                                     1.000 ± 0%
ParseTarget/UnixSocket-8                                       0.000 ± 0%
ParseTarget/UnixAbstractSocket-8                               0.000 ± 0%
ParseTarget/Passthrough-8                                      1.000 ± 0%
geomean                                                                   ¹
¹ summaries must be >0 to compute geomean
```
2025-09-29 07:33:08 -07:00

163 lines
6.2 KiB
Cheetah

// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/otlp/observ/target_test.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package {{ .pkg }}
import "testing"
func TestParseTarget(t *testing.T) {
// gRPC target naming is defined here:
// https://github.com/grpc/grpc/blob/74232c6bd3c0f4bc35bad035dbeecf5cbc834a11/doc/naming.md
//
// The Go gRPC client only supports the "dns", "unix", "unix-abstract", and
// "passthrough" schemes natively with "dns" being the default:
// https://pkg.go.dev/google.golang.org/grpc@v1.75.1/internal/resolver
//
// Other schemes (e.g., "consul", "zk") are supported via custom resolvers
// that can be registered with the gRPC resolver package. These custom
// resolvers are still expected to follow the general target string format
// when rendered with the CanonicalTarget method:
//
// <scheme>://<authority>/<endpoint>
//
// All target strings in these tests are rendered with the
// CanonicalTarget method. Therefore they all follow the above format.
tests := []struct {
target string
host string
port int
}{
// DNS scheme: hostname and port.
{target: "dns:///:8080", host: "", port: 8080},
{target: "dns:///example.com", host: "example.com", port: -1},
{target: "dns:///example.com%eth0", host: "example.com%eth0", port: -1},
{target: "dns:///example.com:42", host: "example.com", port: 42},
{target: "dns:///example.com%eth0:42", host: "example.com%eth0", port: 42},
// DNS scheme: hostname and port with authority.
{target: "dns://8.8.8.8/example.com", host: "example.com", port: -1},
{target: "dns://8.8.8.8/example.com%eth0", host: "example.com%eth0", port: -1},
{target: "dns://8.8.8.8/example.com:42", host: "example.com", port: 42},
{target: "dns://8.8.8.8/example.com%eth0:42", host: "example.com%eth0", port: 42},
// DNS scheme: IPv4 address and port.
{target: "dns:///192.168.1.1", host: "192.168.1.1", port: -1},
{target: "dns:///192.168.1.1%eth0", host: "192.168.1.1%eth0", port: -1},
{target: "dns:///192.168.1.1:8080", host: "192.168.1.1", port: 8080},
{target: "dns:///192.168.1.1%eth0:8080", host: "192.168.1.1%eth0", port: 8080},
// DNS scheme: IPv6 address and port.
{target: "dns:///2001:0db8:85a3:0000:0000:8a2e:0370:7334", host: "2001:db8:85a3::8a2e:370:7334", port: -1},
{target: "dns:///2001:db8:85a3:0:0:8a2e:370:7334", host: "2001:db8:85a3::8a2e:370:7334", port: -1},
{target: "dns:///2001:db8:85a3::8a2e:370:7334", host: "2001:db8:85a3::8a2e:370:7334", port: -1},
{target: "dns:///2001:db8:85a3::8a2e:370:7334%eth0", host: "2001:db8:85a3::8a2e:370:7334%eth0", port: -1},
{target: "dns:///[2001:db8:85a3::8a2e:370:7334]", host: "2001:db8:85a3::8a2e:370:7334", port: -1},
{target: "dns:///[2001:db8:85a3::8a2e:370:7334%eth0]", host: "2001:db8:85a3::8a2e:370:7334%eth0", port: -1},
{target: "dns:///[::1]:9090", host: "::1", port: 9090},
{target: "dns:///[::1%eth0]:9090", host: "::1%eth0", port: 9090},
// Unix domain sockets.
{target: "unix:///tmp/grpc.sock", host: "/tmp/grpc.sock", port: -1},
{target: "unix:///absolute_path", host: "/absolute_path", port: -1},
// Unix domain socket in abstract namespace.
{target: "unix-abstract:///abstract-socket-name", host: "/abstract-socket-name", port: -1},
// International domain names.
{target: "dns:///测试.example.com:8080", host: "测试.example.com", port: 8080},
// Port edge cases.
{target: "dns:///example.com:0", host: "example.com", port: 0},
{target: "dns:///example.com:65535", host: "example.com", port: 65535},
// Case sensitivity.
{target: "dns:///EXAMPLE.COM:8080", host: "EXAMPLE.COM", port: 8080},
{target: "dns:///Example.Com:8080", host: "Example.Com", port: 8080},
// Custom and passthrough resolvers scheme
{target: "passthrough:///localhost:50051", host: "localhost", port: 50051},
{target: "passthrough:///10.0.0.2:7777", host: "10.0.0.2", port: 7777},
{target: "consul:///my-service", host: "my-service", port: -1},
{target: "zk:///services/my-service", host: "services/my-service", port: -1},
}
for _, tt := range tests {
host, port, err := ParseCanonicalTarget(tt.target)
if err != nil {
t.Errorf("parseTarget(%q) unexpected error: %v", tt.target, err)
continue
}
if host != tt.host {
t.Errorf("parseTarget(%q) host = %q, want %q", tt.target, host, tt.host)
}
if port != tt.port {
t.Errorf("parseTarget(%q) port = %d, want %d", tt.target, port, tt.port)
}
}
}
func TestParseTargetErrors(t *testing.T) {
targets := []string{
"dns:///example.com:invalid", // Non-numeric port in URL.
"dns:///example.com:8080:9090", // Multiple colons in port.
"dns:///example.com:99999", // Port out of range.
"dns:///example.com:-1", // Port out of range.
"unix://localhost/sock", // Non-empty authority for unix scheme.
"unix:", // Empty unix scheme.
"unix-abstract://", // Empty unix-abstract scheme.
"unix-abstract://authority/sock", // Non-empty authority for unix-abstract scheme.
"contains-cont\roll-cha\rs", // Invalid URL.
}
for _, target := range targets {
host, port, err := ParseCanonicalTarget(target)
if err == nil {
t.Errorf("parseTarget(%q) expected error, got nil", target)
}
if host != "" {
t.Errorf("parseTarget(%q) host = %q, want empty", target, host)
}
if port != -1 {
t.Errorf("parseTarget(%q) port = %d, want -1", target, port)
}
}
}
func BenchmarkParseTarget(b *testing.B) {
benchmarks := []struct {
name string
target string
}{
{"HostName", "dns:///example.com"},
{"HostPort", "dns:///example.com:8080"},
{"IPv4WithoutPort", "dns:///192.168.1.1"},
{"IPv4WithPort", "dns:///192.168.1.1:8080"},
{"IPv6Bare", "dns:///2001:db8::1"},
{"IPv6Bracket", "dns:///[2001:db8::1]"},
{"IPv6WithPort", "dns:///[2001:db8::1]:8080"},
{"UnixSocket", "unix:///tmp/grpc.sock"},
{"UnixAbstractSocket", "unix-abstract:///abstract-socket-name"},
{"Passthrough", "passthrough:///localhost:50051"},
}
var (
host string
port int
err error
)
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
host, port, err = ParseCanonicalTarget(bm.target)
}
})
}
_, _, _ = host, port, err
}