Skip to content

Commit

Permalink
Add WithServiceName config option for instrumentation (#353)
Browse files Browse the repository at this point in the history
* Add WithServiceName condig option for instrumentation

* Update changelog

* Apply suggestions from code review

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

* Add support for OTEL_RESOURCE_ATTRIBUTES and defualt service name

* Apply suggestions from code review

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

* Fix compilation

* Fix lint

* Update instrumentation.go

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

---------

Co-authored-by: Tyler Yahn <[email protected]>
  • Loading branch information
RonFed and MrAlias authored Sep 29, 2023
1 parent f31d619 commit d239377
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http

## [Unreleased]

### Added

- Add `WithServiceName` config option for instrumentation. ([#353](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/353))

### Changed

- Fix runtime panic if OTEL_GO_AUTO_TARGET_EXE is not set. ([#339](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/339))
Expand Down
55 changes: 55 additions & 0 deletions instConfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 (
"fmt"
"os"
"testing"

"github.com/stretchr/testify/assert"

semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
)

func TestWithServiceName(t *testing.T) {
testServiceName := "test_serviceName"

// Use WithServiceName to config the service name
c := newInstConfig([]InstrumentationOption{WithServiceName((testServiceName))})
assert.Equal(t, testServiceName, c.serviceName)

// No service name provided - check for default value
c = newInstConfig([]InstrumentationOption{})
assert.Equal(t, serviceNameDefault, c.serviceName)

// OTEL_RESOURCE_ATTRIBUTES
resServiceName := "resValue"
err := os.Setenv(envResourceAttrKey, fmt.Sprintf("key1=val1,%s=%s", string(semconv.ServiceNameKey), resServiceName))
if err != nil {
t.Error(err)
}
c = newInstConfig([]InstrumentationOption{WithServiceName((testServiceName))})
assert.Equal(t, resServiceName, c.serviceName)

// Add env var to take precedence
envServiceName := "env_serviceName"
err = os.Setenv(envServiceNameKey, envServiceName)
if err != nil {
t.Error(err)
}
c = newInstConfig([]InstrumentationOption{WithServiceName((testServiceName))})
assert.Equal(t, envServiceName, c.serviceName)
}
77 changes: 72 additions & 5 deletions instrumentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,29 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"strings"

semconv "go.opentelemetry.io/otel/semconv/v1.21.0"

"go.opentelemetry.io/auto/internal/pkg/instrumentors"
"go.opentelemetry.io/auto/internal/pkg/log"
"go.opentelemetry.io/auto/internal/pkg/opentelemetry"
"go.opentelemetry.io/auto/internal/pkg/process"
)

// envTargetExeKey is the key for the environment variable value pointing to the
// target binary to instrument.
const envTargetExeKey = "OTEL_GO_AUTO_TARGET_EXE"
const (
// envTargetExeKey is the key for the environment variable value pointing to the
// target binary to instrument.
envTargetExeKey = "OTEL_GO_AUTO_TARGET_EXE"
// envServiceName is the key for the envoriment variable value containing the service name.
envServiceNameKey = "OTEL_SERVICE_NAME"
// envResourceAttrKey is the key for the environment variable value containing
// OpenTelemetry Resource attributes.
envResourceAttrKey = "OTEL_RESOURCE_ATTRIBUTES"
// serviceNameDefault is the default service name prefix used if a user does not provide one.
serviceNameDefault = "unknown_service"
)

// Instrumentation manages and controls all OpenTelemetry Go
// auto-instrumentation.
Expand Down Expand Up @@ -55,7 +68,7 @@ func NewInstrumentation(opts ...InstrumentationOption) (*Instrumentation, error)
return nil, err
}

ctrl, err := opentelemetry.NewController(Version())
ctrl, err := opentelemetry.NewController(Version(), c.serviceName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -104,7 +117,8 @@ type InstrumentationOption interface {
}

type instConfig struct {
target *process.TargetArgs
target *process.TargetArgs
serviceName string
}

func newInstConfig(opts []InstrumentationOption) instConfig {
Expand All @@ -120,6 +134,45 @@ func (c instConfig) applyEnv() instConfig {
if v, ok := os.LookupEnv(envTargetExeKey); ok {
c.target = &process.TargetArgs{ExePath: v}
}
if v, ok := os.LookupEnv(envServiceNameKey); ok {
c.serviceName = v
} else {
c = c.applyResourceAtrrEnv()
if c.serviceName == "" {
c = c.setDefualtServiceName()
}
}
return c
}

func (c instConfig) setDefualtServiceName() instConfig {
if c.target != nil {
c.serviceName = fmt.Sprintf("%s:%s", serviceNameDefault, filepath.Base(c.target.ExePath))
} else {
c.serviceName = serviceNameDefault
}
return c
}

func (c instConfig) applyResourceAtrrEnv() instConfig {
attrs := strings.TrimSpace(os.Getenv(envResourceAttrKey))

if attrs == "" {
return c
}

pairs := strings.Split(attrs, ",")
for _, p := range pairs {
k, v, found := strings.Cut(p, "=")
if !found {
continue
}
key := strings.TrimSpace(k)
if key == string(semconv.ServiceNameKey) {
c.serviceName = strings.TrimSpace(v)
}
}

return c
}

Expand Down Expand Up @@ -148,3 +201,17 @@ func WithTarget(path string) InstrumentationOption {
return c
})
}

// WithServiceName returns an [InstrumentationOption] defining the name of the service running.
//
// If multiple of these options are provided to an [Instrumentation], the last
// one will be used.
//
// If OTEL_SERVICE_NAME is defined it will take precedence over any value
// passed here.
func WithServiceName(serviceName string) InstrumentationOption {
return fnOpt(func(c instConfig) instConfig {
c.serviceName = serviceName
return c
})
}
12 changes: 1 addition & 11 deletions internal/pkg/opentelemetry/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package opentelemetry
import (
"context"
"fmt"
"os"
"runtime"
"strings"
"time"
Expand All @@ -36,10 +35,6 @@ import (
"go.opentelemetry.io/auto/internal/pkg/log"
)

const (
otelServiceNameEnvVar = "OTEL_SERVICE_NAME"
)

// Information about the runtime environment for inclusion in User-Agent, e.g. "go/1.18.2 (linux/amd64)".
var runtimeInfo = fmt.Sprintf("%s (%s/%s)", strings.Replace(runtime.Version(), "go", "go/", 1), runtime.GOOS, runtime.GOARCH)

Expand Down Expand Up @@ -90,12 +85,7 @@ func (c *Controller) convertTime(t int64) time.Time {
}

// NewController returns a new initialized [Controller].
func NewController(version string) (*Controller, error) {
serviceName, exists := os.LookupEnv(otelServiceNameEnvVar)
if !exists {
return nil, fmt.Errorf("%s env var must be set", otelServiceNameEnvVar)
}

func NewController(version string, serviceName string) (*Controller, error) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithAttributes(
Expand Down

0 comments on commit d239377

Please sign in to comment.