Skip to content

Commit

Permalink
Document the sdk/metric/view package (#3086)
Browse files Browse the repository at this point in the history
* Add package documentation for sdk/metric/view

* Refer to views not configs in WithReader docs

* Fix vanity url for view_test.go

* Add example tests for view options

* Add package example

* Fix view type docs

* Remove build constraint for doc.go

* Fix lint
  • Loading branch information
MrAlias committed Aug 19, 2022
1 parent d3f0d81 commit 5c9ff25
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 14 deletions.
2 changes: 1 addition & 1 deletion sdk/metric/config.go
Expand Up @@ -116,7 +116,7 @@ func WithResource(res *resource.Resource) Option {
}

// WithReader associates a Reader with a MeterProvider. Any passed view config
// will be used to associate a view with the Reader. If no configs are passed
// will be used to associate a view with the Reader. If no views are passed
// the default view will be use for the Reader.
//
// Passing this option multiple times for the same Reader will overwrite. The
Expand Down
20 changes: 20 additions & 0 deletions sdk/metric/view/doc.go
@@ -0,0 +1,20 @@
// 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 view provides types and functionality that customize the metric
// telemetry an SDK will produce. The View type is used when a Reader is
// registered with a MeterProvider in the go.opentelemetry.io/otel/sdk/metric
// package. See the WithReader option in that package for more information on
// how this registration takes place.
package view // import "go.opentelemetry.io/otel/sdk/metric/view"
199 changes: 199 additions & 0 deletions sdk/metric/view/example_test.go
@@ -0,0 +1,199 @@
// 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.

//go:build go1.18
// +build go1.18

package view // import "go.opentelemetry.io/otel/sdk/metric/view"

import (
"fmt"

"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/aggregation"
)

func Example() {
// The "active-users" instrument created by the
// "github.com/super/noisy/instrumentation/package" your project includes
// has a bug, it records a measurment any time a user has any activity.
// This is causing a lot of strain on your program without providing any
// value to you. The next version of
// "github.com/super/noisy/instrumentation/package" corrects the
// instrumentation to only record a value when a user logs in, but it
// isn't out yet.
//
// Use a View to drop these measurments while you wait for the fix to come
// from upstream.

v, err := New(
MatchInstrumentName("active-users"),
MatchInstrumentationScope(instrumentation.Scope{
Name: "github.com/super/noisy/instrumentation/package",
Version: "v0.22.0", // Only match the problematic instrumentation version.
}),
WithSetAggregation(aggregation.Drop{}),
)
if err != nil {
panic(err)
}

// The SDK this view is registered with calls TransformInstrument when an
// instrument is created. Test that our fix will work as intended.
i, _ := v.TransformInstrument(Instrument{
Name: "active-users",
Scope: instrumentation.Scope{
Name: "github.com/super/noisy/instrumentation/package",
Version: "v0.22.0",
},
Aggregation: aggregation.LastValue{},
})
fmt.Printf("Instrument{%q: %s}: %#v\n", i.Name, i.Scope.Version, i.Aggregation)

// Also, ensure the next version will not be transformed.
_, ok := v.TransformInstrument(Instrument{
Name: "active-users",
Scope: instrumentation.Scope{
Name: "github.com/super/noisy/instrumentation/package",
Version: "v0.23.0",
},
Aggregation: aggregation.LastValue{},
})
fmt.Printf("Instrument{\"active-users\": v0.23.0} matched: %t\n", ok)
// Output:
//
// Instrument{"active-users": v0.22.0}: aggregation.Drop{}
// Instrument{"active-users": v0.23.0} matched: false
}

func ExampleMatchInstrumentName() {
v, err := New(MatchInstrumentName("request-*")) // Wildcard match.
if err != nil {
panic(err)
}

for _, i := range []Instrument{
{Name: "request-count"},
{Name: "request-rate"},
{Name: "latency"},
} {
// The SDK calls TransformInstrument when an instrument is created.
_, ok := v.TransformInstrument(i)
fmt.Printf("Instrument{%q} matched: %t\n", i.Name, ok)
}
// Output:
// Instrument{"request-count"} matched: true
// Instrument{"request-rate"} matched: true
// Instrument{"latency"} matched: false
}

func ExampleMatchInstrumentKind() {
v, err := New(MatchInstrumentKind(SyncCounter))
if err != nil {
panic(err)
}

for _, i := range []Instrument{
{Name: "synchronous counter", Kind: SyncCounter},
{Name: "synchronous histogram", Kind: SyncHistogram},
{Name: "asynchronous counter", Kind: AsyncCounter},
} {
// The SDK calls TransformInstrument when an instrument is created.
_, ok := v.TransformInstrument(i)
fmt.Printf("Instrument{%q} matched: %t\n", i.Name, ok)
}
// Output:
// Instrument{"synchronous counter"} matched: true
// Instrument{"synchronous histogram"} matched: false
// Instrument{"asynchronous counter"} matched: false
}

func ExampleMatchInstrumentationScope() {
v, err := New(MatchInstrumentationScope(instrumentation.Scope{
Name: "custom/instrumentation/package",
Version: "v0.22.0", // Only match this version of instrumentation.
}))
if err != nil {
panic(err)
}

for _, i := range []Instrument{
{Name: "v1.0.0 instrumentation", Scope: instrumentation.Scope{
Name: "custom/instrumentation/package",
Version: "v1.0.0",
}},
{Name: "v0.22.0 instrumentation", Scope: instrumentation.Scope{
Name: "custom/instrumentation/package",
Version: "v0.22.0",
}},
} {
// The SDK calls TransformInstrument when an instrument is created.
_, ok := v.TransformInstrument(i)
fmt.Printf("Instrument{%q} matched: %t\n", i.Name, ok)
}
// Output:
// Instrument{"v1.0.0 instrumentation"} matched: false
// Instrument{"v0.22.0 instrumentation"} matched: true
}

func ExampleWithRename() {
v, err := New(MatchInstrumentName("bad-name"), WithRename("good-name"))
if err != nil {
panic(err)
}

// The SDK calls TransformInstrument when an instrument is created.
i, _ := v.TransformInstrument(Instrument{Name: "bad-name"})
fmt.Printf("Instrument{%q}\n", i.Name)
// Output: Instrument{"good-name"}
}

func ExampleWithSetDescription() {
v, err := New(
MatchInstrumentName("requests"),
WithSetDescription("Number of requests received"),
)
if err != nil {
panic(err)
}

// The SDK calls TransformInstrument when an instrument is created.
i, _ := v.TransformInstrument(Instrument{
Name: "requests",
Description: "incorrect description",
})
fmt.Printf("Instrument{%q: %s}\n", i.Name, i.Description)
// Output: Instrument{"requests": Number of requests received}
}

func ExampleWithSetAggregation() {
v, err := New(MatchInstrumentationScope(instrumentation.Scope{
Name: "super/noisy/instrumentation/package",
}), WithSetAggregation(aggregation.Drop{}))
if err != nil {
panic(err)
}

// The SDK calls TransformInstrument when an instrument is created.
i, _ := v.TransformInstrument(Instrument{
Name: "active-users",
Scope: instrumentation.Scope{
Name: "super/noisy/instrumentation/package",
Version: "v0.5.0",
},
Aggregation: aggregation.LastValue{},
})
fmt.Printf("Instrument{%q}: %#v\n", i.Name, i.Aggregation)
// Output: Instrument{"active-users"}: aggregation.Drop{}
}
22 changes: 10 additions & 12 deletions sdk/metric/view/view.go
Expand Up @@ -29,14 +29,11 @@ import (
)

// View provides users with the flexibility to customize the metrics that are
// output by the SDK. A View can be used to:
// output by the SDK. A View can be used to ignore, change the name,
// description, and aggregation of, and customize which attribute(s) are to be
// reported by Instruments.
//
// * Ignore Instruments.
// * Change the name of an Instrument.
// * Change the aggregation of an Instrument.
// * Customize which attribute(s) are to be reported by the Instrument.
//
// An empty config will match all instruments, and do no modifications.
// An empty View will match all instruments, and do no transformations.
type View struct {
instrumentName *regexp.Regexp
hasWildcard bool
Expand All @@ -50,7 +47,10 @@ type View struct {
}

// New returns a new configured View. If there are any duplicate Options passed,
// the last one passed will take precedence.
// the last one passed will take precedence. The unique, de-duplicated,
// Options are all applied to the View. An instrument needs to match all of
// the match Options passed for the View to be applied to it. Similarly, all
// transform operation Options are applied to matched Instruments.
func New(opts ...Option) (View, error) {
v := View{}

Expand Down Expand Up @@ -91,8 +91,7 @@ func (v View) TransformInstrument(inst Instrument) (transformed Instrument, matc
}

// AttributeFilter returns a function that returns only attributes specified by
// WithFilterAttributes.
// If no filter was provided nil is returned.
// WithFilterAttributes. If no filter was provided nil is returned.
func (v View) AttributeFilter() func(attribute.Set) attribute.Set {
if v.filter == nil {
return nil
Expand Down Expand Up @@ -131,8 +130,7 @@ func (v View) match(i Instrument) bool {
v.matchInstrumentKind(i.Kind)
}

// Option applies a Configuration option value to a View. All options
// will be used together to determine match and transforms.
// Option applies a configuration option value to a View.
type Option interface {
apply(View) View
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/metric/view/view_test.go
Expand Up @@ -15,7 +15,7 @@
//go:build go1.18
// +build go1.18

package view
package view // import "go.opentelemetry.io/otel/sdk/metric/view"

import (
"testing"
Expand Down

0 comments on commit 5c9ff25

Please sign in to comment.