mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-14 10:13:10 +02:00
125 lines
4.0 KiB
Go
125 lines
4.0 KiB
Go
|
// 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 metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
|
||
|
"go.opentelemetry.io/otel/internal/global"
|
||
|
"go.opentelemetry.io/otel/sdk/metric/aggregation"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
errMultiInst = errors.New("name replacement for multiple instruments")
|
||
|
|
||
|
emptyView = func(Instrument) (Stream, bool) { return Stream{}, false }
|
||
|
)
|
||
|
|
||
|
// View is an override to the default behavior of the SDK. It defines how data
|
||
|
// should be collected for certain instruments. It returns true and the exact
|
||
|
// Stream to use for matching Instruments. Otherwise, if the view does not
|
||
|
// match, false is returned.
|
||
|
type View func(Instrument) (Stream, bool)
|
||
|
|
||
|
// NewView returns a View that applies the Stream mask for all instruments that
|
||
|
// match criteria. The returned View will only apply mask if all non-zero-value
|
||
|
// fields of criteria match the corresponding Instrument passed to the view. If
|
||
|
// no criteria are provided, all field of criteria are their zero-values, a
|
||
|
// view that matches no instruments is returned. If you need to match a
|
||
|
// zero-value field, create a View directly.
|
||
|
//
|
||
|
// The Name field of criteria supports wildcard pattern matching. The wildcard
|
||
|
// "*" is recognized as matching zero or more characters, and "?" is recognized
|
||
|
// as matching exactly one character. For example, a pattern of "*" will match
|
||
|
// all instrument names.
|
||
|
//
|
||
|
// The Stream mask only applies updates for non-zero-value fields. By default,
|
||
|
// the Instrument the View matches against will be use for the Name,
|
||
|
// Description, and Unit of the returned Stream and no Aggregation or
|
||
|
// AttributeFilter are set. All non-zero-value fields of mask are used instead
|
||
|
// of the default. If you need to zero out an Stream field returned from a
|
||
|
// View, create a View directly.
|
||
|
func NewView(criteria Instrument, mask Stream) View {
|
||
|
if criteria.empty() {
|
||
|
return emptyView
|
||
|
}
|
||
|
|
||
|
var matchFunc func(Instrument) bool
|
||
|
if strings.ContainsAny(criteria.Name, "*?") {
|
||
|
if mask.Name != "" {
|
||
|
global.Error(
|
||
|
errMultiInst, "dropping view",
|
||
|
"criteria", criteria,
|
||
|
"mask", mask,
|
||
|
)
|
||
|
return emptyView
|
||
|
}
|
||
|
|
||
|
// Handle branching here in NewView instead of criteria.matches so
|
||
|
// criteria.matches remains inlinable for the simple case.
|
||
|
pattern := regexp.QuoteMeta(criteria.Name)
|
||
|
pattern = "^" + pattern + "$"
|
||
|
pattern = strings.ReplaceAll(pattern, `\?`, ".")
|
||
|
pattern = strings.ReplaceAll(pattern, `\*`, ".*")
|
||
|
re := regexp.MustCompile(pattern)
|
||
|
matchFunc = func(i Instrument) bool {
|
||
|
return re.MatchString(i.Name) &&
|
||
|
criteria.matchesDescription(i) &&
|
||
|
criteria.matchesKind(i) &&
|
||
|
criteria.matchesUnit(i) &&
|
||
|
criteria.matchesScope(i)
|
||
|
}
|
||
|
} else {
|
||
|
matchFunc = criteria.matches
|
||
|
}
|
||
|
|
||
|
var agg aggregation.Aggregation
|
||
|
if mask.Aggregation != nil {
|
||
|
agg = mask.Aggregation.Copy()
|
||
|
if err := agg.Err(); err != nil {
|
||
|
global.Error(
|
||
|
err, "not using aggregation with view",
|
||
|
"criteria", criteria,
|
||
|
"mask", mask,
|
||
|
)
|
||
|
agg = nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return func(i Instrument) (Stream, bool) {
|
||
|
if matchFunc(i) {
|
||
|
return Stream{
|
||
|
Name: nonZero(mask.Name, i.Name),
|
||
|
Description: nonZero(mask.Description, i.Description),
|
||
|
Unit: nonZero(mask.Unit, i.Unit),
|
||
|
Aggregation: agg,
|
||
|
AttributeFilter: mask.AttributeFilter,
|
||
|
}, true
|
||
|
}
|
||
|
return Stream{}, false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// nonZero returns v if it is non-zero-valued, otherwise alt.
|
||
|
func nonZero[T comparable](v, alt T) T {
|
||
|
var zero T
|
||
|
if v != zero {
|
||
|
return v
|
||
|
}
|
||
|
return alt
|
||
|
}
|