Skip to content

Commit

Permalink
Add service.name resource attribute to the collector's own telemetry
Browse files Browse the repository at this point in the history
Resolves open-telemetry#6136

- The default service.name is set to "io.opentelemetry.collector". I think this
  is a good choice since it is unambiguous and is still short enough to be readable.
  This also matches the OpAMP recommendations: https://github.com/open-telemetry/opamp-spec/blob/main/specification.md#agentdescriptionidentifying_attributes
- I made the service.name a setting in BuildInfo which can be overridden when building the
  Collector. Other distros may choose a different service.name.
- The service.name can be also overridden by the end user via telemetry config setting.
  • Loading branch information
tigrannajaryan committed Sep 26, 2022
1 parent 65ea4f0 commit 7ddfc9f
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 21 deletions.
3 changes: 3 additions & 0 deletions cmd/builder/internal/builder/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
)

const defaultOtelColVersion = "0.60.0"
const defaultOtelColServiceName = "io.opentelemetry.collector"

// ErrInvalidGoMod indicates an invalid gomod
var ErrInvalidGoMod = errors.New("invalid gomod specification for module")
Expand Down Expand Up @@ -55,6 +56,7 @@ type Distribution struct {
OtelColVersion string `mapstructure:"otelcol_version"`
OutputPath string `mapstructure:"output_path"`
Version string `mapstructure:"version"`
ServiceName string `mapstructure:"service_name"`
BuildTags string `mapstructure:"build_tags"`
}

Expand Down Expand Up @@ -84,6 +86,7 @@ func NewDefaultConfig() Config {
OutputPath: outputDir,
OtelColVersion: defaultOtelColVersion,
Module: "go.opentelemetry.io/collector/cmd/builder",
ServiceName: defaultOtelColServiceName,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions cmd/builder/internal/builder/templates/main.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func main() {
Command: "{{ .Distribution.Name }}",
Description: "{{ .Distribution.Description }}",
Version: "{{ .Distribution.Version }}",
ServiceName: "{{ .Distribution.ServiceName }}",
}

if err := run(service.CollectorSettings{BuildInfo: info, Factories: factories}); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions cmd/builder/internal/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (
distributionNameFlag = "name"
distributionDescriptionFlag = "description"
distributionVersionFlag = "version"
distributionServiceNameFlag = "service-name"
distributionOtelColVersionFlag = "otelcol-version"
distributionOutputPathFlag = "output-path"
distributionGoFlag = "go"
Expand Down Expand Up @@ -180,6 +181,9 @@ func applyCfgFromFile(flags *flag.FlagSet, cfgFromFile builder.Config) {
if !flags.Changed(distributionVersionFlag) && cfgFromFile.Distribution.Version != "" {
cfg.Distribution.Version = cfgFromFile.Distribution.Version
}
if !flags.Changed(distributionServiceNameFlag) && cfgFromFile.Distribution.ServiceName != "" {
cfg.Distribution.ServiceName = cfgFromFile.Distribution.ServiceName
}
if !flags.Changed(distributionOtelColVersionFlag) && cfgFromFile.Distribution.OtelColVersion != "" {
cfg.Distribution.OtelColVersion = cfgFromFile.Distribution.OtelColVersion
}
Expand Down
1 change: 1 addition & 0 deletions cmd/builder/internal/config/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dist:
description: Local OpenTelemetry Collector binary, testing only.
version: 0.60.0-dev
otelcol_version: 0.60.0
service_name: io.opentelemetry.collector

receivers:
- import: go.opentelemetry.io/collector/receiver/otlpreceiver
Expand Down
1 change: 1 addition & 0 deletions cmd/otelcorecol/builder-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dist:
description: Local OpenTelemetry Collector binary, testing only.
version: 0.60.0-dev
otelcol_version: 0.60.0
service_name: io.opentelemetry.collector

receivers:
- import: go.opentelemetry.io/collector/receiver/otlpreceiver
Expand Down
1 change: 1 addition & 0 deletions cmd/otelcorecol/main.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions component/build_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ type BuildInfo struct {

// Version string.
Version string

// ServiceName is the value of "service.name" attribute in the telemetry
// that the Collector produces about itself. This can be overridden
// by the end user via telemetry.Config.Resource setting.
ServiceName string
}

// NewDefaultBuildInfo returns a default BuildInfo.
Expand All @@ -33,5 +38,6 @@ func NewDefaultBuildInfo() BuildInfo {
Command: "otelcol",
Description: "OpenTelemetry Collector",
Version: "latest",
ServiceName: "io.opentelemetry.collector",
}
}
54 changes: 33 additions & 21 deletions service/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,27 +112,8 @@ func (tel *telemetryInitializer) initOnce(buildInfo component.BuildInfo, logger

logger.Info("Setting up own telemetry...")

// Construct telemetry attributes from resource attributes.
telAttrs := map[string]string{}
for k, v := range cfg.Resource {
// nil value indicates that the attribute should not be included in the telemetry.
if v != nil {
telAttrs[k] = *v
}
}

if _, ok := cfg.Resource[semconv.AttributeServiceInstanceID]; !ok {
// AttributeServiceInstanceID is not specified in the config. Auto-generate one.
instanceUUID, _ := uuid.NewRandom()
instanceID := instanceUUID.String()
telAttrs[semconv.AttributeServiceInstanceID] = instanceID
}

if _, ok := cfg.Resource[semconv.AttributeServiceVersion]; !ok {
// AttributeServiceVersion is not specified in the config. Use the actual
// build version.
telAttrs[semconv.AttributeServiceVersion] = buildInfo.Version
}
// Construct telemetry attributes from build info and config's resource attributes.
telAttrs := buildTelAttrs(buildInfo, cfg)

if tp, err := textMapPropagatorFromConfig(cfg.Traces.Propagators); err == nil {
otel.SetTextMapPropagator(tp)
Expand Down Expand Up @@ -174,6 +155,37 @@ func (tel *telemetryInitializer) initOnce(buildInfo component.BuildInfo, logger
return nil
}

func buildTelAttrs(buildInfo component.BuildInfo, cfg telemetry.Config) map[string]string {
telAttrs := map[string]string{}

for k, v := range cfg.Resource {
// nil value indicates that the attribute should not be included in the telemetry.
if v != nil {
telAttrs[k] = *v
}
}

if _, ok := cfg.Resource[semconv.AttributeServiceName]; !ok {
// AttributeServiceName is not specified in the config. Use the Service name.
telAttrs[semconv.AttributeServiceName] = buildInfo.ServiceName
}

if _, ok := cfg.Resource[semconv.AttributeServiceInstanceID]; !ok {
// AttributeServiceInstanceID is not specified in the config. Auto-generate one.
instanceUUID, _ := uuid.NewRandom()
instanceID := instanceUUID.String()
telAttrs[semconv.AttributeServiceInstanceID] = instanceID
}

if _, ok := cfg.Resource[semconv.AttributeServiceVersion]; !ok {
// AttributeServiceVersion is not specified in the config. Use the actual
// build version.
telAttrs[semconv.AttributeServiceVersion] = buildInfo.Version
}

return telAttrs
}

func (tel *telemetryInitializer) initOpenCensus(cfg telemetry.Config, telAttrs map[string]string) (http.Handler, error) {
tel.ocRegistry = ocmetric.NewRegistry()
metricproducer.GlobalManager().AddProducer(tel.ocRegistry)
Expand Down
69 changes: 69 additions & 0 deletions service/telemetry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 service

import (
"testing"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/collector/component"
semconv "go.opentelemetry.io/collector/semconv/v1.5.0"
"go.opentelemetry.io/collector/service/telemetry"
)

func TestBuildTelAttrs(t *testing.T) {
buildInfo := component.NewDefaultBuildInfo()

// Check default config
cfg := telemetry.Config{}
telAttrs := buildTelAttrs(buildInfo, cfg)

assert.Len(t, telAttrs, 3)
assert.Equal(t, buildInfo.ServiceName, telAttrs[semconv.AttributeServiceName])
assert.Equal(t, buildInfo.Version, telAttrs[semconv.AttributeServiceVersion])

_, exists := telAttrs[semconv.AttributeServiceInstanceID]
assert.True(t, exists)

// Check override by nil
cfg = telemetry.Config{
Resource: map[string]*string{
semconv.AttributeServiceName: nil,
semconv.AttributeServiceVersion: nil,
semconv.AttributeServiceInstanceID: nil,
},
}
telAttrs = buildTelAttrs(buildInfo, cfg)

// Attributes should not exist since we nil-ified all.
assert.Len(t, telAttrs, 0)

// Check override values
strPtr := func(v string) *string { return &v }
cfg = telemetry.Config{
Resource: map[string]*string{
semconv.AttributeServiceName: strPtr("a"),
semconv.AttributeServiceVersion: strPtr("b"),
semconv.AttributeServiceInstanceID: strPtr("c"),
},
}
telAttrs = buildTelAttrs(buildInfo, cfg)

assert.Len(t, telAttrs, 3)
assert.Equal(t, "a", telAttrs[semconv.AttributeServiceName])
assert.Equal(t, "b", telAttrs[semconv.AttributeServiceVersion])
assert.Equal(t, "c", telAttrs[semconv.AttributeServiceInstanceID])
}

0 comments on commit 7ddfc9f

Please sign in to comment.