Skip to content

Commit

Permalink
[feat] custom user-agent (& expanding OTEL_EXPORTER_OTLP_* env var su…
Browse files Browse the repository at this point in the history
…pport) (#94)

* customize the user-agent of the OLTP exporter

Instead of the Go autoinst agent appearing to telemetry data receivers
as only the version of grpc-go against which it was built, this adds an
identifier to the User-Agent header via the dial options parameter
support within otlptracegrpc.

To help troubleshooting data transmission, runtime operating system and
CPU architecture is included in the user-agent within a parenthetical
comment in accordance with the header's specification.[1]

This replaces the low-level build up of a gRPC client connection with a
reuse of the OTel Go SDK's grpc trace client constructor. A happy
byproduct of using the SDK's constructor is that all of the
OTEL_EXPORTER_OTLP_* environment variables related to exporting traces
ought to be supported now, obviating the need to maintain code here to
retrieve, interpret, and handle them.

[1] RFC 7231 https://www.rfc-editor.org/rfc/rfc7231#section-5.5.3
    though more readable documentation about this header spec is
    available at MDN Web Docs:
    https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent

* add release version to user-agent and Auto attribute

* an attempt at adding unit tests

This is an expendable commit if folks don't like it.

* tack tests onto end of generate instead?

* include scheme for endpoint

Spec and therefore SDK require the URL scheme in ENDPOINT values.

* update e2e expectations

This Go auto-inst adds its version to resource attributes now.

* new version.go for the auto package

The version declared here will get bumped when using the multimod
releaser utility.

The agent's controller uses this version to include in the User-Agent
header and in a resource attribute for outgoing telemetry.

* test potentially testable modules

Modeled after the test targets in OpenTelemetry Go.

Removes the "maybe use gotestsum if you've got it!" option.

* improve code comment for auto.Version()

Co-authored-by: Tyler Yahn <[email protected]>

* test is PHONY

Always Be Testing.

Co-authored-by: Tyler Yahn <[email protected]>

---------

Co-authored-by: Tyler Yahn <[email protected]>
  • Loading branch information
robbkidd and MrAlias authored Apr 27, 2023
1 parent 4101e5d commit 5741371
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 25 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:

jobs:
generate:
generate-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
Expand All @@ -24,6 +24,9 @@ jobs:
- name: verify output
run: |
git diff --exit-code || (echo 'generated diff, please run "make generate"' && exit 1)
- name: Run unit tests
run: |
make test
docker-build:
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/e2e/k8s/sample-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ spec:
- name: OTEL_GO_AUTO_TARGET_EXE
value: /sample-app/main
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "test-opentelemetry-collector:4317"
value: "http://test-opentelemetry-collector:4317"
- name: OTEL_SERVICE_NAME
value: "sample-app"
- name: OTEL_PROPAGATORS
Expand Down
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ $(TOOLS)/go-licenses: PACKAGE=github.com/google/go-licenses
.PHONY: tools
tools: $(GOLICENSES)

ALL_GO_MODS := $(shell find . -type f -name 'go.mod' ! -path '$(TOOLS_MOD_DIR)/*' ! -path './LICENSES/*' | sort)
GO_MODS_TO_TEST := $(ALL_GO_MODS:%=test/%)

.PHONY: test
test: $(GO_MODS_TO_TEST)
test/%: GO_MOD=$*
test/%:
cd $(shell dirname $(GO_MOD)) && go test -v ./...

.PHONY: generate
generate: export CFLAGS := $(BPF_INCLUDE)
generate:
Expand Down
8 changes: 4 additions & 4 deletions docs/getting-started/emojivoto-instrumented.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ spec:
- name: OTEL_GO_AUTO_TARGET_EXE
value: /usr/local/bin/emojivoto-emoji-svc
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "jaeger:4317"
value: "http://jaeger:4317"
- name: OTEL_SERVICE_NAME
value: "emojivoto-emoji"
securityContext:
Expand Down Expand Up @@ -143,7 +143,7 @@ spec:
- name: OTEL_GO_AUTO_TARGET_EXE
value: /usr/local/bin/emojivoto-voting-svc
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "jaeger:4317"
value: "http://jaeger:4317"
- name: OTEL_SERVICE_NAME
value: "emojivoto-voting"
securityContext:
Expand Down Expand Up @@ -227,7 +227,7 @@ spec:
- name: OTEL_GO_AUTO_TARGET_EXE
value: /usr/local/bin/emojivoto-web
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "jaeger:4317"
value: "http://jaeger:4317"
- name: OTEL_SERVICE_NAME
value: "emojivoto-web"
securityContext:
Expand All @@ -244,4 +244,4 @@ spec:
emptyDir: {}
- name: kernel-debug
hostPath:
path: /sys/kernel/debug
path: /sys/kernel/debug
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,27 @@ require (
github.com/hashicorp/go-version v1.6.0
github.com/pkg/errors v0.9.1
github.com/prometheus/procfs v0.9.0
github.com/stretchr/testify v1.8.2
go.opentelemetry.io/otel v1.14.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0
go.opentelemetry.io/otel/sdk v1.14.0
go.opentelemetry.io/otel/trace v1.14.0
go.uber.org/zap v1.24.0
golang.org/x/arch v0.3.0
golang.org/x/sys v0.7.0
google.golang.org/grpc v1.54.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,16 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -454,6 +459,7 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand All @@ -462,6 +468,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
38 changes: 20 additions & 18 deletions pkg/opentelemetry/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ import (
"fmt"
"os"
"runtime"
"strings"
"time"

"github.com/prometheus/procfs"
"go.opentelemetry.io/auto"
"go.opentelemetry.io/auto/pkg/instrumentors/events"
"go.opentelemetry.io/auto/pkg/log"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
Expand All @@ -34,10 +37,20 @@ import (
)

const (
otelEndpointEnvVar = "OTEL_EXPORTER_OTLP_ENDPOINT"
otelServiceNameEnvVar = "OTEL_SERVICE_NAME"
)

var (
// Controller-local reference to the auto-instrumentation release version.
releaseVersion = auto.Version()
// Start of this auto-instrumentation's exporter User-Agent header, e.g. ""OTel-Go-Auto-Instrumentation/1.2.3"
baseUserAgent = fmt.Sprintf("OTel-Go-Auto-Instrumentation/%s", releaseVersion)
// Information about the runtime environment for inclusion in User-Agent, e.g. "go/1.18.2 (linux/amd64)"
runtimeInfo = fmt.Sprintf("%s (%s/%s)", strings.Replace(runtime.Version(), "go", "go/", 1), runtime.GOOS, runtime.GOARCH)
// Combined User-Agent identifying this auto-instrumentation and its runtime environment, see RFC7231 for format considerations.
autoinstUserAgent = fmt.Sprintf("%s %s", baseUserAgent, runtimeInfo)
)

type Controller struct {
tracerProvider trace.TracerProvider
tracersMap map[string]trace.Tracer
Expand Down Expand Up @@ -83,11 +96,6 @@ func (c *Controller) convertTime(t int64) time.Time {
}

func NewController() (*Controller, error) {
endpoint, exists := os.LookupEnv(otelEndpointEnvVar)
if !exists {
return nil, fmt.Errorf("%s env var must be set", otelEndpointEnvVar)
}

serviceName, exists := os.LookupEnv(otelServiceNameEnvVar)
if !exists {
return nil, fmt.Errorf("%s env var must be set", otelServiceNameEnvVar)
Expand All @@ -98,26 +106,20 @@ func NewController() (*Controller, error) {
resource.WithAttributes(
semconv.ServiceNameKey.String(serviceName),
semconv.TelemetrySDKLanguageGo,
semconv.TelemetryAutoVersionKey.String(releaseVersion),
),
)
if err != nil {
return nil, err
}

log.Logger.V(0).Info("Establishing connection to OpenTelemetry collector ...")
timeoutContext, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()
conn, err := grpc.DialContext(timeoutContext, endpoint, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Logger.Error(err, "unable to connect to OpenTelemetry collector", "addr", endpoint)
return nil, err
}

traceExporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithGRPCConn(conn),
log.Logger.V(0).Info("Establishing connection to OTLP receiver ...")
otlpTraceClient := otlptracegrpc.NewClient(
otlptracegrpc.WithDialOption(grpc.WithUserAgent(autoinstUserAgent)),
)

traceExporter, err := otlptrace.New(ctx, otlpTraceClient)
if err != nil {
log.Logger.Error(err, "unable to connect to OTLP endpoint")
return nil, err
}

Expand Down
27 changes: 27 additions & 0 deletions pkg/opentelemetry/controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright The OpenTelemetry Authors
//
// 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 opentelemetry

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/auto"
)

func TestUserAgent(t *testing.T) {
assert.Contains(t, autoinstUserAgent, fmt.Sprintf("OTel-Go-Auto-Instrumentation/%s", auto.Version()))
}
6 changes: 6 additions & 0 deletions test/e2e/gorillamux/traces.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
"stringValue": "sample-app"
}
},
{
"key": "telemetry.auto.version",
"value": {
"stringValue": "v0.1.0-alpha"
}
},
{
"key": "telemetry.sdk.language",
"value": {
Expand Down
6 changes: 6 additions & 0 deletions test/e2e/nethttp/traces.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
"stringValue": "sample-app"
}
},
{
"key": "telemetry.auto.version",
"value": {
"stringValue": "v0.1.0-alpha"
}
},
{
"key": "telemetry.sdk.language",
"value": {
Expand Down
20 changes: 20 additions & 0 deletions version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright The OpenTelemetry Authors
//
// 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 auto

// Version is the current release version of OpenTelemetry Go auto-instrumentation in use.
func Version() string {
return "v0.1.0-alpha"
}
54 changes: 54 additions & 0 deletions version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright The OpenTelemetry Authors
//
// 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 auto

import (
"io/ioutil"
"regexp"
"testing"

"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)

// regex taken from https://github.com/Masterminds/semver/tree/v3.1.1
var versionRegex = regexp.MustCompile(`^v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?$`)

func TestVersionSemver(t *testing.T) {
v := Version()
assert.NotNil(t, versionRegex.FindStringSubmatch(v), "version is not semver: %s", v)
}

func TestVersionMatchesYaml(t *testing.T) {
versionYaml, err := ioutil.ReadFile("versions.yaml")
if err != nil {
t.Fatalf("Couldn't read versions.yaml file: %e", err)
return
}

var versionInfo map[string]interface{}

err = yaml.Unmarshal(versionYaml, &versionInfo)
if err != nil {
t.Fatalf("Couldn't parse version.yaml: %e", err)
return
}

// incredibad, but it's where the intended version is declared at the moment
expectedVersion := versionInfo["module-sets"].(map[string]interface{})["alpha"].(map[string]interface{})["version"]
assert.Equal(t, expectedVersion, Version(), "Build version should match versions.yaml.")
}

0 comments on commit 5741371

Please sign in to comment.