Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create generic statsReporter in metrics package #8084

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
60 changes: 60 additions & 0 deletions pkg/metrics/stats_reporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2020 The Knative 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 metrics

import (
"context"
"log"

"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
"knative.dev/pkg/metrics"
)

type MetricArgs interface {
GenerateTag(tags ...tag.Mutator) (context.Context, error)
}

func init() {
Register([]stats.Measure{}, nil)
}

// StatsReporter defines the interface for sending filter metrics.
type StatsReporter interface {
}

func Register(customMetrics []stats.Measure, customViews []*view.View, customTagKeys ...tag.Key) {
allTagKeys := append(customTagKeys)

Check failure on line 42 in pkg/metrics/stats_reporter.go

View workflow job for this annotation

GitHub Actions / style / Golang / Lint

appends: append with no values (govet)
allViews := append(customViews)

Check failure on line 43 in pkg/metrics/stats_reporter.go

View workflow job for this annotation

GitHub Actions / style / Golang / Lint

appends: append with no values (govet)

// Add custom views for custom metrics
for _, metric := range customMetrics {
allViews = append(allViews, &view.View{
Description: metric.Description(),
Name: metric.Name(),
Measure: metric,
Aggregation: view.LastValue(),
TagKeys: allTagKeys,
})
}

// Append custom views
if err := metrics.RegisterResourceView(allViews...); err != nil {
log.Printf("Failed to register opencensus views: %v", err)
}
}
146 changes: 146 additions & 0 deletions pkg/metrics/stats_repoter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
Copyright 2020 The Knative 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 metrics_test

Check failure on line 16 in pkg/metrics/stats_repoter_test.go

View workflow job for this annotation

GitHub Actions / style / Golang / Boilerplate Check (go)

[Go headers] reported by reviewdog 🐶 found mismatched boilerplate lines: Raw Output: pkg/metrics/stats_repoter_test.go:16: found mismatched boilerplate lines: {[]string}[0]: -: "" +: "package metrics_test"

import (
"context"
"testing"

"go.opencensus.io/resource"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
eventingmetrics "knative.dev/eventing/pkg/metrics"
"knative.dev/pkg/metrics/metricskey"
"knative.dev/pkg/metrics/metricstest"
_ "knative.dev/pkg/metrics/testing"
)

type testMetricArgs struct {
ns string
trigger string
broker string
testParam string
}

func (args *testMetricArgs) GenerateTag(tags ...tag.Mutator) (context.Context, error) {
ctx := metricskey.WithResource(context.Background(), resource.Resource{
Type: eventingmetrics.ResourceTypeKnativeTrigger,
Labels: map[string]string{
eventingmetrics.LabelNamespaceName: args.ns,
eventingmetrics.LabelBrokerName: args.broker,
eventingmetrics.LabelTriggerName: args.trigger,
},
})
ctx, err := tag.New(
ctx,
append(tags,
tag.Insert(customTagKey, args.testParam),
)...)
return ctx, err
}

var (
customMetricM = stats.Int64(
"custom_metric",
"A custom metric for testing",
stats.UnitDimensionless,
)

customTagKey = tag.MustNewKey("custom_tag")
)

// StatsReporter interface definition
type StatsReporter interface {
eventingmetrics.StatsReporter
ReportCustomMetric(args eventingmetrics.MetricArgs, value int64) error
}

// reporter struct definition
type reporter struct {
container string
uniqueName string
}

// ReportCustomMetric records a custom metric value.
func (r *reporter) ReportCustomMetric(args eventingmetrics.MetricArgs, value int64) error {
// Create base tags
baseTags := []tag.Mutator{
tag.Insert(tag.MustNewKey("unique_name"), r.uniqueName),
tag.Insert(tag.MustNewKey("container_name"), r.container),
}

// Generate context with all tags, including any custom ones from args.GenerateTag
ctx, err := args.GenerateTag(baseTags...)
if err != nil {
return err
}

// Record the custom metric
stats.Record(ctx, customMetricM.M(value))
return nil
}

func TestRegisterCustomMetrics(t *testing.T) {
setup()

args := &testMetricArgs{
ns: "test-namespace",
trigger: "test-trigger",
broker: "test-broker",
testParam: "test-param",
}
r := &reporter{
container: "testcontainer",
uniqueName: "testpod",
}

customMetrics := []stats.Measure{customMetricM}

customTagKeys := []tag.Key{customTagKey}

// No need for customViews, as the custom metric will be used to create the view
eventingmetrics.Register(customMetrics, nil, customTagKeys...)

// Verify that the custom view is registered
if v := view.Find("custom_metric"); v == nil {
t.Error("Custom view was not registered")
}

// Record a value for the custom metric
expectSuccess(t, func() error {
return r.ReportCustomMetric(args, 100)
})

// Check if the value was recorded correctly
metricstest.CheckLastValueData(t, "custom_metric", map[string]string{"custom_tag": "test-param"}, 100)
}

func expectSuccess(t *testing.T, f func() error) {
t.Helper()
if err := f(); err != nil {
t.Errorf("Reporter expected success but got error: %v", err)
}
}

func setup() {
resetMetrics()
}

func resetMetrics() {
metricstest.Unregister("custom_metric")
eventingmetrics.Register([]stats.Measure{}, nil)
}
Loading