You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
6cb0e90c0e
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 ```
163 lines
6.2 KiB
Cheetah
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
|
|
}
|