Skip to content

Commit

Permalink
[pkg/otlp/model] Add option to send instrumentation library metadata …
Browse files Browse the repository at this point in the history
…tags with metrics (#9472)

Backport of open-telemetry/opentelemetry-collector-contrib#5431 to the `pkg/otlp/model` go module.
  • Loading branch information
KSerrania authored Oct 15, 2021
1 parent de43889 commit 09291cd
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 32 deletions.
35 changes: 35 additions & 0 deletions pkg/otlp/model/internal/instrumentationlibrary/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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 instrumentationlibrary

import (
"go.opentelemetry.io/collector/model/pdata"

"github.com/DataDog/datadog-agent/pkg/otlp/model/internal/utils"
)

const (
instrumentationLibraryTag = "instrumentation_library"
instrumentationLibraryVersionTag = "instrumentation_library_version"
)

// TagsFromInstrumentationLibraryMetadata takes the name and version of
// the instrumentation library and converts them to Datadog tags.
func TagsFromInstrumentationLibraryMetadata(il pdata.InstrumentationLibrary) []string {
return []string{
utils.FormatKeyValueTag(instrumentationLibraryTag, il.Name()),
utils.FormatKeyValueTag(instrumentationLibraryVersionTag, il.Version()),
}
}
45 changes: 45 additions & 0 deletions pkg/otlp/model/internal/instrumentationlibrary/metadata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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 instrumentationlibrary

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/model/pdata"
)

func TestTagsFromInstrumentationLibraryMetadata(t *testing.T) {
tests := []struct {
name string
version string
expectedTags []string
}{
{"test-il", "1.0.0", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "test-il"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "1.0.0")}},
{"test-il", "", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "test-il"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "n/a")}},
{"", "1.0.0", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "n/a"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "1.0.0")}},
{"", "", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "n/a"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "n/a")}},
}

for _, testInstance := range tests {
il := pdata.NewInstrumentationLibrary()
il.SetName(testInstance.name)
il.SetVersion(testInstance.version)
tags := TagsFromInstrumentationLibraryMetadata(il)

assert.ElementsMatch(t, testInstance.expectedTags, tags)
}
}
28 changes: 28 additions & 0 deletions pkg/otlp/model/internal/utils/tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 utils

import (
"fmt"
)

// FormatKeyValueTag takes a key-value pair, and creates a tag string out of it
// Tags can't end with ":" so we replace empty values with "n/a"
func FormatKeyValueTag(key, value string) string {
if value == "" {
value = "n/a"
}
return fmt.Sprintf("%s:%s", key, value)
}
36 changes: 36 additions & 0 deletions pkg/otlp/model/internal/utils/tags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 utils

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestFormatKeyValueTag(t *testing.T) {
tests := []struct {
key string
value string
expectedTag string
}{
{"a.test.tag", "a.test.value", "a.test.tag:a.test.value"},
{"a.test.tag", "", "a.test.tag:n/a"},
}

for _, testInstance := range tests {
assert.Equal(t, testInstance.expectedTag, FormatKeyValueTag(testInstance.key, testInstance.value))
}
}
19 changes: 14 additions & 5 deletions pkg/otlp/model/translator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import "fmt"

type translatorConfig struct {
// metrics export behavior
HistMode HistogramMode
SendCountSum bool
Quantiles bool
SendMonotonic bool
ResourceAttributesAsTags bool
HistMode HistogramMode
SendCountSum bool
Quantiles bool
SendMonotonic bool
ResourceAttributesAsTags bool
InstrumentationLibraryMetadataAsTags bool

// cache configuration
sweepInterval int64
Expand Down Expand Up @@ -76,6 +77,14 @@ func WithResourceAttributesAsTags() Option {
}
}

// WithInstrumentationLibraryMetadataAsTags sets instrumentation library metadata as tags.
func WithInstrumentationLibraryMetadataAsTags() Option {
return func(t *translatorConfig) error {
t.InstrumentationLibraryMetadataAsTags = true
return nil
}
}

// HistogramMode is an export mode for OTLP Histogram metrics.
type HistogramMode string

Expand Down
59 changes: 32 additions & 27 deletions pkg/otlp/model/translator/metrics_translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"go.uber.org/zap"

"github.com/DataDog/datadog-agent/pkg/otlp/model/attributes"
"github.com/DataDog/datadog-agent/pkg/otlp/model/internal/instrumentationlibrary"
"github.com/DataDog/datadog-agent/pkg/otlp/model/internal/utils"
)

const metricName string = "metric name"
Expand All @@ -39,14 +41,15 @@ type Translator struct {
// New creates a new translator with given options.
func New(logger *zap.Logger, options ...Option) (*Translator, error) {
cfg := translatorConfig{
HistMode: HistogramModeDistributions,
SendCountSum: false,
Quantiles: false,
SendMonotonic: true,
ResourceAttributesAsTags: false,
sweepInterval: 1800,
deltaTTL: 3600,
fallbackHostnameProvider: &noHostProvider{},
HistMode: HistogramModeDistributions,
SendCountSum: false,
Quantiles: false,
SendMonotonic: true,
ResourceAttributesAsTags: false,
InstrumentationLibraryMetadataAsTags: false,
sweepInterval: 1800,
deltaTTL: 3600,
fallbackHostnameProvider: &noHostProvider{},
}

for _, opt := range options {
Expand All @@ -69,11 +72,7 @@ func getTags(labels pdata.AttributeMap) []string {
tags := make([]string, 0, labels.Len())
labels.Range(func(key string, value pdata.AttributeValue) bool {
v := value.AsString()
if v == "" {
// Tags can't end with ":" so we replace empty values with "n/a"
v = "n/a"
}
tags = append(tags, fmt.Sprintf("%s:%s", key, v))
tags = append(tags, utils.FormatKeyValueTag(key, v))
return true
})
return tags
Expand Down Expand Up @@ -106,14 +105,14 @@ func (t *Translator) mapNumberMetrics(
name string,
dt MetricDataType,
slice pdata.NumberDataPointSlice,
attrTags []string,
additionalTags []string,
host string,
) {

for i := 0; i < slice.Len(); i++ {
p := slice.At(i)
tags := getTags(p.Attributes())
tags = append(tags, attrTags...)
tags = append(tags, additionalTags...)
var val float64
switch p.Type() {
case pdata.MetricValueTypeDouble:
Expand All @@ -136,14 +135,14 @@ func (t *Translator) mapNumberMonotonicMetrics(
consumer TimeSeriesConsumer,
name string,
slice pdata.NumberDataPointSlice,
attrTags []string,
additionalTags []string,
host string,
) {
for i := 0; i < slice.Len(); i++ {
p := slice.At(i)
ts := uint64(p.Timestamp())
tags := getTags(p.Attributes())
tags = append(tags, attrTags...)
tags = append(tags, additionalTags...)

var val float64
switch p.Type() {
Expand Down Expand Up @@ -258,14 +257,14 @@ func (t *Translator) mapHistogramMetrics(
name string,
slice pdata.HistogramDataPointSlice,
delta bool,
attrTags []string,
additionalTags []string,
host string,
) {
for i := 0; i < slice.Len(); i++ {
p := slice.At(i)
ts := uint64(p.Timestamp())
tags := getTags(p.Attributes())
tags = append(tags, attrTags...)
tags = append(tags, additionalTags...)

if t.cfg.SendCountSum {
count := float64(p.Count())
Expand Down Expand Up @@ -331,15 +330,15 @@ func (t *Translator) mapSummaryMetrics(
consumer TimeSeriesConsumer,
name string,
slice pdata.SummaryDataPointSlice,
attrTags []string,
additionalTags []string,
host string,
) {

for i := 0; i < slice.Len(); i++ {
p := slice.At(i)
ts := uint64(p.Timestamp())
tags := getTags(p.Attributes())
tags = append(tags, attrTags...)
tags = append(tags, additionalTags...)

// count and sum are increasing; we treat them as cumulative monotonic sums.
{
Expand Down Expand Up @@ -408,21 +407,27 @@ func (t *Translator) MapMetrics(ctx context.Context, md pdata.Metrics, consumer
for j := 0; j < ilms.Len(); j++ {
ilm := ilms.At(j)
metricsArray := ilm.Metrics()

var additionalTags []string
if t.cfg.InstrumentationLibraryMetadataAsTags {
additionalTags = append(attributeTags, instrumentationlibrary.TagsFromInstrumentationLibraryMetadata(ilm.InstrumentationLibrary())...)
}

for k := 0; k < metricsArray.Len(); k++ {
md := metricsArray.At(k)
switch md.DataType() {
case pdata.MetricDataTypeGauge:
t.mapNumberMetrics(ctx, consumer, md.Name(), Gauge, md.Gauge().DataPoints(), attributeTags, host)
t.mapNumberMetrics(ctx, consumer, md.Name(), Gauge, md.Gauge().DataPoints(), additionalTags, host)
case pdata.MetricDataTypeSum:
switch md.Sum().AggregationTemporality() {
case pdata.AggregationTemporalityCumulative:
if t.cfg.SendMonotonic && isCumulativeMonotonic(md) {
t.mapNumberMonotonicMetrics(ctx, consumer, md.Name(), md.Sum().DataPoints(), attributeTags, host)
t.mapNumberMonotonicMetrics(ctx, consumer, md.Name(), md.Sum().DataPoints(), additionalTags, host)
} else {
t.mapNumberMetrics(ctx, consumer, md.Name(), Gauge, md.Sum().DataPoints(), attributeTags, host)
t.mapNumberMetrics(ctx, consumer, md.Name(), Gauge, md.Sum().DataPoints(), additionalTags, host)
}
case pdata.AggregationTemporalityDelta:
t.mapNumberMetrics(ctx, consumer, md.Name(), Count, md.Sum().DataPoints(), attributeTags, host)
t.mapNumberMetrics(ctx, consumer, md.Name(), Count, md.Sum().DataPoints(), additionalTags, host)
default: // pdata.AggregationTemporalityUnspecified or any other not supported type
t.logger.Debug("Unknown or unsupported aggregation temporality",
zap.String(metricName, md.Name()),
Expand All @@ -434,7 +439,7 @@ func (t *Translator) MapMetrics(ctx context.Context, md pdata.Metrics, consumer
switch md.Histogram().AggregationTemporality() {
case pdata.AggregationTemporalityCumulative, pdata.AggregationTemporalityDelta:
delta := md.Histogram().AggregationTemporality() == pdata.AggregationTemporalityDelta
t.mapHistogramMetrics(ctx, consumer, md.Name(), md.Histogram().DataPoints(), delta, attributeTags, host)
t.mapHistogramMetrics(ctx, consumer, md.Name(), md.Histogram().DataPoints(), delta, additionalTags, host)
default: // pdata.AggregationTemporalityUnspecified or any other not supported type
t.logger.Debug("Unknown or unsupported aggregation temporality",
zap.String("metric name", md.Name()),
Expand All @@ -443,7 +448,7 @@ func (t *Translator) MapMetrics(ctx context.Context, md pdata.Metrics, consumer
continue
}
case pdata.MetricDataTypeSummary:
t.mapSummaryMetrics(ctx, consumer, md.Name(), md.Summary().DataPoints(), attributeTags, host)
t.mapSummaryMetrics(ctx, consumer, md.Name(), md.Summary().DataPoints(), additionalTags, host)
default: // pdata.MetricDataTypeNone or any other not supported type
t.logger.Debug("Unknown or unsupported metric type", zap.String(metricName, md.Name()), zap.Any("data type", md.DataType()))
continue
Expand Down

0 comments on commit 09291cd

Please sign in to comment.