metric 등록 방법 변경
This commit is contained in:
@ -15,18 +15,7 @@ import (
|
|||||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const metric_value_line_size = 19
|
||||||
METRIC_HEAD_INLINE = byte(14)
|
|
||||||
METRIC_TAIL_INLINE = byte(15)
|
|
||||||
)
|
|
||||||
|
|
||||||
type MetricType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
MetricCounter = MetricType(1)
|
|
||||||
MetricGuage = MetricType(2)
|
|
||||||
metric_key_size = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
type MetricDescription struct {
|
type MetricDescription struct {
|
||||||
Key string
|
Key string
|
||||||
@ -36,17 +25,9 @@ type MetricDescription struct {
|
|||||||
ConstLabels map[string]string `json:",omitempty"`
|
ConstLabels map[string]string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type writeRequest struct {
|
type Exporter interface {
|
||||||
key string
|
RegisterMetric(*MetricDescription)
|
||||||
valfunc func() float64
|
UpdateMetric(string, float64)
|
||||||
}
|
|
||||||
|
|
||||||
type metricCollection struct {
|
|
||||||
writerChan chan *writeRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
var mc = metricCollection{
|
|
||||||
writerChan: make(chan *writeRequest, 100),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetricWriter interface {
|
type MetricWriter interface {
|
||||||
@ -56,92 +37,36 @@ type MetricWriter interface {
|
|||||||
|
|
||||||
type metric_empty struct{}
|
type metric_empty struct{}
|
||||||
|
|
||||||
func (mw *metric_empty) Set(newval int64) {}
|
func (mw *metric_empty) Set(int64) {}
|
||||||
func (mw *metric_empty) Add(inc int64) {}
|
func (mw *metric_empty) Add(int64) {}
|
||||||
|
|
||||||
var MetricWriterNil = MetricWriter(&metric_empty{})
|
var MetricWriterNil = MetricWriter(&metric_empty{})
|
||||||
|
|
||||||
type metric_int64 struct {
|
type metric_int64 struct {
|
||||||
valptr *int64
|
valptr *int64
|
||||||
key string
|
buff [metric_value_line_size]byte
|
||||||
writerChan chan *writeRequest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mw *metric_int64) requestMetricWrite() {
|
func (mw *metric_int64) printOut() {
|
||||||
mw.writerChan <- &writeRequest{
|
binary.LittleEndian.PutUint64(mw.buff[9:], math.Float64bits(float64(atomic.LoadInt64(mw.valptr))))
|
||||||
key: mw.key,
|
os.Stdout.Write(mw.buff[:])
|
||||||
valfunc: func() float64 { return float64(atomic.LoadInt64(mw.valptr)) },
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mw *metric_int64) Set(newval int64) {
|
func (mw *metric_int64) Set(newval int64) {
|
||||||
atomic.StoreInt64(mw.valptr, newval)
|
atomic.StoreInt64(mw.valptr, newval)
|
||||||
mw.requestMetricWrite()
|
mw.printOut()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mw *metric_int64) Add(inc int64) {
|
func (mw *metric_int64) Add(inc int64) {
|
||||||
atomic.AddInt64(mw.valptr, inc)
|
atomic.AddInt64(mw.valptr, inc)
|
||||||
mw.requestMetricWrite()
|
mw.printOut()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *metricCollection) metricWriter() {
|
func NewMetric(mt MetricType, name string, help string, constLabels map[string]string) (writer MetricWriter) {
|
||||||
defer func() {
|
if !metricEnabled {
|
||||||
r := recover()
|
return MetricWriterNil
|
||||||
if r != nil {
|
|
||||||
logger.Error(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// head + metric_key_size + 8byte + tail + cr = 19
|
|
||||||
var buff [19]byte
|
|
||||||
buff[0] = METRIC_HEAD_INLINE
|
|
||||||
buff[17] = METRIC_TAIL_INLINE
|
|
||||||
buff[18] = '\n'
|
|
||||||
|
|
||||||
for req := range mc.writerChan {
|
|
||||||
copy(buff[1:], []byte(req.key))
|
|
||||||
binary.LittleEndian.PutUint64(buff[9:], math.Float64bits(req.valfunc()))
|
|
||||||
os.Stdout.Write(buff[:])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var NewMetric func(MetricType, string, string, map[string]string) MetricWriter
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
NewMetric = func(MetricType, string, string, map[string]string) MetricWriter {
|
|
||||||
return &metric_empty{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.Base(os.Args[0]) == "houston" {
|
|
||||||
logger.Println("metrics are going to be generated for myself(houston)")
|
|
||||||
go mc.metricWriter()
|
|
||||||
NewMetric = newMetricImpl
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ppid := os.Getppid()
|
|
||||||
if parent, _ := os.FindProcess(ppid); parent != nil {
|
|
||||||
filename := fmt.Sprintf(`/proc/%d/stat`, os.Getppid())
|
|
||||||
if fn, err := os.ReadFile(filename); err == nil {
|
|
||||||
stats := strings.SplitN(string(fn), " ", 3)
|
|
||||||
parentname := strings.Trim(stats[1], "()")
|
|
||||||
|
|
||||||
if path.Base(parentname) == "houston" {
|
|
||||||
logger.Println("metrics are going to be generated for houston")
|
|
||||||
go mc.metricWriter()
|
|
||||||
NewMetric = newMetricImpl
|
|
||||||
} else {
|
|
||||||
logger.Println("metrics are NOT going to be generated. parent is not houston :", filename, string(fn))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Println("metrics are NOT going to be generated. ppid proc is missing :", filename)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Println("metrics are NOT going to be generated. parent process is missing. ppid :", ppid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMetricImpl(mt MetricType, name string, help string, constLabels map[string]string) (writer MetricWriter) {
|
|
||||||
hash := md5.New()
|
hash := md5.New()
|
||||||
hash.Write([]byte(name))
|
hash.Write([]byte(name))
|
||||||
for k, v := range constLabels {
|
for k, v := range constLabels {
|
||||||
@ -158,15 +83,62 @@ func newMetricImpl(mt MetricType, name string, help string, constLabels map[stri
|
|||||||
ConstLabels: constLabels,
|
ConstLabels: constLabels,
|
||||||
})
|
})
|
||||||
|
|
||||||
writer = &metric_int64{
|
impl := &metric_int64{
|
||||||
valptr: new(int64),
|
valptr: new(int64),
|
||||||
key: key,
|
|
||||||
writerChan: mc.writerChan,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl.buff[0] = METRIC_HEAD_INLINE
|
||||||
|
impl.buff[17] = METRIC_TAIL_INLINE
|
||||||
|
impl.buff[18] = '\n'
|
||||||
|
copy(impl.buff[1:], []byte(key))
|
||||||
|
|
||||||
output := append([]byte{METRIC_HEAD_INLINE}, temp...)
|
output := append([]byte{METRIC_HEAD_INLINE}, temp...)
|
||||||
output = append(output, METRIC_TAIL_INLINE, '\n')
|
output = append(output, METRIC_TAIL_INLINE, '\n')
|
||||||
os.Stdout.Write(output)
|
os.Stdout.Write(output)
|
||||||
|
|
||||||
|
// writer
|
||||||
|
|
||||||
|
return impl
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadMetricValue(line []byte) (string, float64) {
|
||||||
|
if len(line) < 16 {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
|
||||||
|
key := string(line[0:8])
|
||||||
|
valbits := binary.LittleEndian.Uint64(line[8:])
|
||||||
|
val := math.Float64frombits(valbits)
|
||||||
|
|
||||||
|
return key, val
|
||||||
|
}
|
||||||
|
|
||||||
|
var metricEnabled = false
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if path.Base(os.Args[0]) == "houston" {
|
||||||
|
logger.Println("metrics are going to be generated for myself(houston)")
|
||||||
|
metricEnabled = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ppid := os.Getppid()
|
||||||
|
if parent, _ := os.FindProcess(ppid); parent != nil {
|
||||||
|
filename := fmt.Sprintf(`/proc/%d/stat`, os.Getppid())
|
||||||
|
if fn, err := os.ReadFile(filename); err == nil {
|
||||||
|
stats := strings.SplitN(string(fn), " ", 3)
|
||||||
|
parentname := strings.Trim(stats[1], "()")
|
||||||
|
|
||||||
|
if path.Base(parentname) == "houston" {
|
||||||
|
logger.Println("metrics are going to be generated for houston")
|
||||||
|
metricEnabled = true
|
||||||
|
} else {
|
||||||
|
logger.Println("metrics are NOT going to be generated. parent is not houston :", filename, string(fn))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Println("metrics are NOT going to be generated. ppid proc is missing :", filename)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Println("metrics are NOT going to be generated. parent process is missing. ppid :", ppid)
|
||||||
|
}
|
||||||
|
}
|
||||||
135
metric/prometheus.go
Normal file
135
metric/prometheus.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package metric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
METRIC_HEAD_INLINE = byte(14)
|
||||||
|
METRIC_TAIL_INLINE = byte(15)
|
||||||
|
)
|
||||||
|
|
||||||
|
type MetricType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetricCounter = MetricType(1)
|
||||||
|
MetricGuage = MetricType(2)
|
||||||
|
metric_key_size = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
func convertValueType(in MetricType) prometheus.ValueType {
|
||||||
|
switch in {
|
||||||
|
case MetricCounter:
|
||||||
|
return prometheus.CounterValue
|
||||||
|
|
||||||
|
case MetricGuage:
|
||||||
|
return prometheus.GaugeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return prometheus.UntypedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeRequest struct {
|
||||||
|
key string
|
||||||
|
val float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type prometheusMetricDesc struct {
|
||||||
|
*prometheus.Desc
|
||||||
|
valueType prometheus.ValueType
|
||||||
|
valptr *uint64
|
||||||
|
key string
|
||||||
|
}
|
||||||
|
|
||||||
|
type prometheusExporter struct {
|
||||||
|
writerChan chan *writeRequest
|
||||||
|
registerChan chan *prometheusMetricDesc
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *prometheusExporter) RegisterMetric(nm *MetricDescription) {
|
||||||
|
pe.registerChan <- &prometheusMetricDesc{
|
||||||
|
Desc: prometheus.NewDesc(prometheus.BuildFQName(pe.namespace, "", nm.Name), nm.Help, nil, nm.ConstLabels),
|
||||||
|
valueType: convertValueType(nm.Type),
|
||||||
|
valptr: new(uint64),
|
||||||
|
key: nm.Key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *prometheusExporter) UpdateMetric(key string, val float64) {
|
||||||
|
pe.writerChan <- &writeRequest{key: key, val: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
type prometheusCollector struct {
|
||||||
|
metrics map[string]*prometheusMetricDesc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *prometheusCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
for _, v := range pc.metrics {
|
||||||
|
ch <- v.Desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *prometheusCollector) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
for _, v := range pc.metrics {
|
||||||
|
cm, err := prometheus.NewConstMetric(v.Desc, v.valueType, math.Float64frombits(atomic.LoadUint64(v.valptr)))
|
||||||
|
if err == nil {
|
||||||
|
ch <- cm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *prometheusExporter) loop() {
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r != nil {
|
||||||
|
logger.Error(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var collector *prometheusCollector
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case req := <-pe.writerChan:
|
||||||
|
if m := collector.metrics[req.key]; m != nil {
|
||||||
|
atomic.StoreUint64(m.valptr, math.Float64bits(req.val))
|
||||||
|
}
|
||||||
|
|
||||||
|
case nm := <-pe.registerChan:
|
||||||
|
var nextmetrics map[string]*prometheusMetricDesc
|
||||||
|
if collector != nil {
|
||||||
|
nextmetrics = collector.metrics
|
||||||
|
prometheus.Unregister(collector)
|
||||||
|
nextmetrics[nm.key] = nm
|
||||||
|
} else {
|
||||||
|
nextmetrics = map[string]*prometheusMetricDesc{
|
||||||
|
nm.key: nm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collector = &prometheusCollector{
|
||||||
|
metrics: nextmetrics,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := prometheus.Register(collector); err != nil {
|
||||||
|
logger.Error("prometheus register err :", *nm, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrometheusExport(namespace string) Exporter {
|
||||||
|
exp := &prometheusExporter{
|
||||||
|
registerChan: make(chan *prometheusMetricDesc, 10),
|
||||||
|
writerChan: make(chan *writeRequest, 100),
|
||||||
|
namespace: namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
go exp.loop()
|
||||||
|
return exp
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user