// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you 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 monitoring import ( "encoding/json" "expvar" "math" "strconv" "sync" "github.com/elastic/beats/libbeat/common/atomic" ) // makeExpvar wraps a callback for registering a metrics with expvar.Publish. type makeExpvar func() string // Int is a 64 bit integer variable satisfying the Var interface. type Int struct{ i atomic.Int64 } // NewInt creates and registers a new integer variable. // // Note: If the registry is configured to publish variables to expvar, the // variable will be available via expvars package as well, but can not be removed // anymore. func NewInt(r *Registry, name string, opts ...Option) *Int { if r == nil { r = Default } v := &Int{} addVar(r, name, opts, v, makeExpvar(func() string { return strconv.FormatInt(v.Get(), 10) })) return v } func (v *Int) Get() int64 { return v.i.Load() } func (v *Int) Set(value int64) { v.i.Store(value) } func (v *Int) Add(delta int64) { v.i.Add(delta) } func (v *Int) Sub(delta int64) { v.i.Sub(delta) } func (v *Int) Inc() { v.i.Inc() } func (v *Int) Dec() { v.i.Dec() } func (v *Int) Visit(_ Mode, vs Visitor) { vs.OnInt(v.Get()) } // Uint is a 64bit unsigned integer variable satisfying the Var interface. type Uint struct{ u atomic.Uint64 } // NewUint creates and registers a new unsigned integer variable. // // Note: If the registry is configured to publish variables to expvar, the // variable will be available via expvars package as well, but can not be removed // anymore. func NewUint(r *Registry, name string, opts ...Option) *Uint { if r == nil { r = Default } v := &Uint{} addVar(r, name, opts, v, makeExpvar(func() string { return strconv.FormatUint(v.Get(), 10) })) return v } func (v *Uint) Get() uint64 { return v.u.Load() } func (v *Uint) Set(value uint64) { v.u.Store(value) } func (v *Uint) Add(delta uint64) { v.u.Add(delta) } func (v *Uint) Sub(delta uint64) { v.u.Sub(delta) } func (v *Uint) Inc() { v.u.Inc() } func (v *Uint) Dec() { v.u.Dec() } func (v *Uint) Visit(_ Mode, vs Visitor) { value := v.Get() & (^uint64(1 << 63)) vs.OnInt(int64(value)) } // Float is a 64 bit float variable satisfying the Var interface. type Float struct{ f atomic.Uint64 } // NewFloat creates and registers a new float variable. // // Note: If the registry is configured to publish variables to expvar, the // variable will be available via expvars package as well, but can not be removed // anymore. func NewFloat(r *Registry, name string, opts ...Option) *Float { if r == nil { r = Default } v := &Float{} addVar(r, name, opts, v, makeExpvar(func() string { return strconv.FormatFloat(v.Get(), 'g', -1, 64) })) return v } func (v *Float) Get() float64 { return math.Float64frombits(v.f.Load()) } func (v *Float) Set(value float64) { v.f.Store(math.Float64bits(value)) } func (v *Float) Sub(delta float64) { v.Add(-delta) } func (v *Float) Visit(_ Mode, vs Visitor) { vs.OnFloat(v.Get()) } func (v *Float) Add(delta float64) { for { cur := v.f.Load() next := math.Float64bits(math.Float64frombits(cur) + delta) if v.f.CAS(cur, next) { return } } } // Bool is a Bool variable satisfying the Var interface. type Bool struct{ f atomic.Bool } // NewBool creates and registers a new bool variable. // // Note: If the registry is configured to publish variables to expvar, the // variable will be available via expvars package as well, but can not be removed // anymore. func NewBool(r *Registry, name string, opts ...Option) *Bool { if r == nil { r = Default } v := &Bool{} addVar(r, name, opts, v, makeExpvar(func() string { return strconv.FormatBool(v.Get()) })) return v } func (v *Bool) Get() bool { return v.f.Load() } func (v *Bool) Set(value bool) { v.f.Store(value) } func (v *Bool) Visit(_ Mode, vs Visitor) { vs.OnBool(v.Get()) } // String is a string variable satisfying the Var interface. type String struct { mu sync.RWMutex s string } // NewString creates and registers a new string variable. // // Note: If the registry is configured to publish variables to expvar, the // variable will be available via expvars package as well, but can not be removed // anymore. func NewString(r *Registry, name string, opts ...Option) *String { if r == nil { r = Default } v := &String{} addVar(r, name, opts, v, makeExpvar(func() string { b, _ := json.Marshal(v.Get()) return string(b) })) return v } func (v *String) Visit(_ Mode, vs Visitor) { vs.OnString(v.Get()) } func (v *String) Get() string { v.mu.RLock() defer v.mu.RUnlock() return v.s } func (v *String) Set(s string) { v.mu.Lock() defer v.mu.Unlock() v.s = s } func (v *String) Clear() { v.Set("") } func (v *String) Fail(err error) { v.Set(err.Error()) } type FuncVar func(Mode, Visitor) func (f FuncVar) Visit(m Mode, vs Visitor) { f(m, vs) } type Func struct { f FuncVar } func NewFunc(r *Registry, name string, f func(Mode, Visitor), opts ...Option) *Func { if r == nil { r = Default } v := &Func{f} addVar(r, name, opts, v, nil) return v } func (f *Func) Visit(m Mode, vs Visitor) { f.f(m, vs) } func (m makeExpvar) String() string { return m() } func addVar(r *Registry, name string, opts []Option, v Var, ev expvar.Var) { O := varOpts(r.opts, opts) r.doAdd(name, v, O) if O.publishExpvar && ev != nil { expvar.Publish(fullName(r, name), ev) } } func fullName(r *Registry, name string) string { if r.name == "" { return name } return r.name + "." + name }