blob: 15861b3204e9c2cc35bf6c6c2493e5601b80a018 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package event
import (
"context"
"fmt"
"time"
)
type Metric interface {
Descriptor() MetricDescriptor
}
type MetricDescriptor struct {
namespace string
name string
Description string
// For unit, follow otel, or define Go types for common units? We don't need
// a time unit because we'll use time.Duration, and the only other unit otel
// currently defines (besides dimensionless) is bytes.
}
// TODO: how to force a non-empty namespace?
func NewMetricDescriptor(name string) MetricDescriptor {
if name == "" {
panic("name cannot be empty")
}
m := MetricDescriptor{name: name}
// TODO: make this work right whether called from in this package or externally.
// Set namespace to the caller's import path.
// Depth:
// 0 runtime.Callers
// 1 importPath
// 2 this function
// 3 caller of this function (one of the NewXXX methods in this package)
// 4 caller's caller
m.namespace = importPath(4, nil)
return m
}
func (m *MetricDescriptor) String() string {
return fmt.Sprintf("Metric(\"%s/%s\")", m.namespace, m.name)
}
func (m *MetricDescriptor) WithNamespace(ns string) *MetricDescriptor {
if ns == "" {
panic("namespace cannot be empty")
}
m.namespace = ns
return m
}
func (m *MetricDescriptor) Name() string { return m.name }
func (m *MetricDescriptor) Namespace() string { return m.namespace }
// A counter is a metric that counts something cumulatively.
type Counter struct {
MetricDescriptor
}
func NewCounter(name string) *Counter {
return &Counter{NewMetricDescriptor(name)}
}
func (c *Counter) Descriptor() MetricDescriptor {
return c.MetricDescriptor
}
func (c *Counter) To(ctx context.Context) CounterBuilder {
b := CounterBuilder{builderCommon: builderCommon{ctx: ctx}, c: c}
b.data = newBuilder(ctx)
if b.data != nil {
b.builderID = b.data.id
}
return b
}
type CounterBuilder struct {
builderCommon
c *Counter
}
func (b CounterBuilder) With(label Label) CounterBuilder {
b.addLabel(label)
return b
}
func (b CounterBuilder) WithAll(labels ...Label) CounterBuilder {
b.addLabels(labels)
return b
}
func (b CounterBuilder) Record(v uint64) {
record(b.builderCommon, b.c, Uint64Of(v))
}
func record(b builderCommon, m Metric, v Value) {
if b.data == nil {
return
}
checkValid(b.data, b.builderID)
if b.data.exporter.metricsEnabled() {
b.data.exporter.mu.Lock()
defer b.data.exporter.mu.Unlock()
b.data.Event.Labels = append(b.data.Event.Labels, MetricValue.Of(v), MetricKey.Of(ValueOf(m)))
b.data.exporter.prepare(&b.data.Event)
b.data.exporter.handler.Metric(b.ctx, &b.data.Event)
}
b.done()
}
// A FloatGauge records a single floating-point value that may go up or down.
// TODO(generics): Gauge[T]
type FloatGauge struct {
MetricDescriptor
}
func NewFloatGauge(name string) *FloatGauge {
return &FloatGauge{NewMetricDescriptor(name)}
}
func (g *FloatGauge) Descriptor() MetricDescriptor {
return g.MetricDescriptor
}
func (g *FloatGauge) To(ctx context.Context) FloatGaugeBuilder {
b := FloatGaugeBuilder{builderCommon: builderCommon{ctx: ctx}, g: g}
b.data = newBuilder(ctx)
if b.data != nil {
b.builderID = b.data.id
}
return b
}
type FloatGaugeBuilder struct {
builderCommon
g *FloatGauge
}
func (b FloatGaugeBuilder) With(label Label) FloatGaugeBuilder {
b.addLabel(label)
return b
}
func (b FloatGaugeBuilder) WithAll(labels ...Label) FloatGaugeBuilder {
b.addLabels(labels)
return b
}
func (b FloatGaugeBuilder) Record(v float64) {
record(b.builderCommon, b.g, Float64Of(v))
}
// A Duration records a distribution of durations.
// TODO(generics): Distribution[T]
type Duration struct {
MetricDescriptor
}
func NewDuration(name string) *Duration {
return &Duration{NewMetricDescriptor(name)}
}
func (d *Duration) Descriptor() MetricDescriptor {
return d.MetricDescriptor
}
func (d *Duration) To(ctx context.Context) DurationBuilder {
b := DurationBuilder{builderCommon: builderCommon{ctx: ctx}, d: d}
b.data = newBuilder(ctx)
if b.data != nil {
b.builderID = b.data.id
}
return b
}
type DurationBuilder struct {
builderCommon
d *Duration
}
func (b DurationBuilder) With(label Label) DurationBuilder {
b.addLabel(label)
return b
}
func (b DurationBuilder) WithAll(labels ...Label) DurationBuilder {
b.addLabels(labels)
return b
}
func (b DurationBuilder) Record(v time.Duration) {
record(b.builderCommon, b.d, DurationOf(v))
}