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 runtime.Config struct with metric.Provider and WithMinimumReadMemStatsInterval() configuration options #224

Merged
merged 9 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions instrumentation/runtime/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"syscall"
"time"

"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/exporters/stdout"
"go.opentelemetry.io/otel/sdk/metric/controller/push"

Expand All @@ -42,7 +41,11 @@ func initMeter() *push.Controller {
func main() {
defer initMeter().Stop()

if err := runtime.Start(global.MeterProvider(), runtime.WithMinimumGCStatsInterval(time.Second)); err != nil {
if err := runtime.Start(
runtime.Configure(
runtime.WithMinimumReadMemStatsInterval(time.Second),
),
); err != nil {
panic(err)
}

Expand Down
2 changes: 1 addition & 1 deletion instrumentation/runtime/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ replace go.opentelemetry.io/contrib => ../..

require (
github.com/stretchr/testify v1.6.1
go.opentelemetry.io/contrib v0.0.0-00010101000000-000000000000
go.opentelemetry.io/contrib v0.10.1
go.opentelemetry.io/otel v0.10.0
go.opentelemetry.io/otel/exporters/stdout v0.10.0
go.opentelemetry.io/otel/sdk v0.10.0
Expand Down
100 changes: 76 additions & 24 deletions instrumentation/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,48 +20,100 @@ import (
"sync"
"time"

"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/unit"
)

// Runtime reports the work-in-progress conventional runtime metrics specified by OpenTelemetry
type runtime struct {
meter metric.Meter
minGCStatsInterval time.Duration
config Config
meter metric.Meter
}

// Config contains optional settings for reporting runtime metrics.
type Config struct {
// MinimumReadMemStatsInterval sets the mininum interval
// between calls to runtime.ReadMemStats(). Negative values
// are ignored.
MinimumReadMemStatsInterval time.Duration

// MeterProvider sets the metric.Provider. If nil, the global
// Provider will be used.
MeterProvider metric.Provider
}

// Option supports configuring optional settings for runtime metrics.
type Option func(*runtime)
type Option interface {
// ApplyRuntime updates *Config.
ApplyRuntime(*Config)
}

// DefaultMinimumGCStatsInterval is the default minimum interval
// DefaultMinimumReadMemStatsInterval is the default minimum interval
// between calls to runtime.ReadMemStats(). Use the
// WithMinimumGCStatsInterval() option to modify this setting in
// WithMinimumReadMemStatsInterval() option to modify this setting in
// Start().
const DefaultMinimumGCStatsInterval time.Duration = 15 * time.Second
const DefaultMinimumReadMemStatsInterval time.Duration = 15 * time.Second

// WithMinimumGCStatsInterval sets a minimum interval between calls to
// WithMinimumReadMemStatsInterval sets a minimum interval between calls to
// runtime.ReadMemStats(), which is a relatively expensive call to make
// frequently. `d` values less than 0 will be disregarded silently.
func WithMinimumGCStatsInterval(d time.Duration) Option {
return func(r *runtime) {
if d >= 0 {
r.minGCStatsInterval = d
}
// frequently. This setting is ignored when `d` is negative.
func WithMinimumReadMemStatsInterval(d time.Duration) Option {
return minimumReadMemStatsIntervalOption(d)
}

type minimumReadMemStatsIntervalOption time.Duration

// ApplyRuntime implements Option.
func (o minimumReadMemStatsIntervalOption) ApplyRuntime(c *Config) {
if o >= 0 {
c.MinimumReadMemStatsInterval = time.Duration(o)
}
}

// New returns Runtime, a structure for reporting Go runtime metrics
// interval is used to limit how often to invoke Go runtime.ReadMemStats() to obtain metric data.
// If the metric SDK attempts to observe MemStats-derived instruments more frequently than the
// interval, a cached value will be used.
func Start(provider metric.Provider, opts ...Option) error {
r := &runtime{
// TODO: How to set the instrumentation library version?
meter: provider.Meter("runtime"),
minGCStatsInterval: DefaultMinimumGCStatsInterval,
// WithMeterProvider sets the Metric implementation to use for
// reporting. If this option is not used, the global metric.Provider
// will be used. `provider` must be non-nil.
func WithMeterProvider(provider metric.Provider) Option {
return metricProviderOption{provider}
}

type metricProviderOption struct{ metric.Provider }

// ApplyRuntime implements Option.
func (o metricProviderOption) ApplyRuntime(c *Config) {
c.MeterProvider = o.Provider
}

// Configure computes a Config from the supplied Options.
func Configure(opts ...Option) Config {
c := Config{
MeterProvider: global.MeterProvider(),
MinimumReadMemStatsInterval: DefaultMinimumReadMemStatsInterval,
}
for _, opt := range opts {
opt(r)
opt.ApplyRuntime(&c)
}
return c
}

// Start initializes reporting of runtime metrics using the supplied Config.
func Start(c Config) error {
if c.MinimumReadMemStatsInterval < 0 {
c.MinimumReadMemStatsInterval = DefaultMinimumReadMemStatsInterval
}
if c.MeterProvider == nil {
c.MeterProvider = global.MeterProvider()
}
r := &runtime{
meter: c.MeterProvider.Meter(
// TODO: should library names be qualified?
// e.g., contrib/runtime?
"runtime",
MrAlias marked this conversation as resolved.
Show resolved Hide resolved
// TODO(#225): set the instrumentation library version
// metric.WithInstrumentationVersion(contrib.SemVersion())
jmacd marked this conversation as resolved.
Show resolved Hide resolved
),
config: c,
}
return r.register()
}
Expand Down Expand Up @@ -142,7 +194,7 @@ func (r *runtime) registerMemStats() error {
defer lock.Unlock()

now := time.Now()
if now.Sub(lastMemStats) >= r.minGCStatsInterval {
if now.Sub(lastMemStats) >= r.config.MinimumReadMemStatsInterval {
goruntime.ReadMemStats(&memStats)
lastMemStats = now
}
Expand Down
19 changes: 11 additions & 8 deletions instrumentation/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ import (

"go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/contrib/internal/metric"

"go.opentelemetry.io/otel/api/global"
)

func TestRuntime(t *testing.T) {
err := runtime.Start(
global.MeterProvider(),
runtime.WithMinimumGCStatsInterval(time.Second),
runtime.Configure(
runtime.WithMinimumReadMemStatsInterval(time.Second),
),
)
assert.NoError(t, err)
time.Sleep(time.Second)
Expand All @@ -58,8 +57,12 @@ func testMinimumInterval(t *testing.T, shouldHappen bool, opts ...runtime.Option
impl, provider := metric.NewProvider()

err := runtime.Start(
provider,
opts...,
runtime.Configure(
append(
opts,
runtime.WithMeterProvider(provider),
)...,
),
)
assert.NoError(t, err)

Expand Down Expand Up @@ -90,9 +93,9 @@ func TestDefaultMinimumInterval(t *testing.T) {
}

func TestNoMinimumInterval(t *testing.T) {
testMinimumInterval(t, true, runtime.WithMinimumGCStatsInterval(0))
testMinimumInterval(t, true, runtime.WithMinimumReadMemStatsInterval(0))
}

func TestExplicitMinimumInterval(t *testing.T) {
testMinimumInterval(t, false, runtime.WithMinimumGCStatsInterval(time.Hour))
testMinimumInterval(t, false, runtime.WithMinimumReadMemStatsInterval(time.Hour))
}