Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add View, NewView, Instrument, Stream, and InstrumentKind to sdk/metr…
…ic with unit tests (#3459) * Add the InstrumentKind type and vars to sdk/metric * Add the Instrument type to sdk/metric * Add the Stream type to sdk/metric * Add the View type to sdk/metric * Add NewView to create Views matching OTel spec * Add unit tests for NewView * Add changes to changelog * Apply suggestions from code review Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> * Update CHANGELOG.md * Update match and mask comments of Instrument * Explain wildcard logic in NewView with comment * Drop views that replace name for multi-inst match * Comment how users are expected to match zero-vals * Remove InstrumentKind and Scope from Stream * Fix redundant word in NewView comment Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> Co-authored-by: Chester Cheung <cheung.zhy.csu@gmail.com>
- Loading branch information
1 parent
404f999
commit 2e780d8
Showing
5 changed files
with
733 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// 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 | ||
} |
Oops, something went wrong.