blob: 761a7aae45686b0458d61200dc8bba4f02b71dd4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 metrics
import (
"context"
"strings"
"sync"
"time"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/prometheus"
mapset "github.com/deckarep/golang-set"
"github.com/go-chassis/foundation/gopool"
)
const prefixName = FamilyName + "_"
var families = mapset.NewSet(FamilyName)
// EmptyGather just active when metrics disabled
var EmptyGather = &Gather{
Records: NewMetrics(),
closed: false,
}
func NewGatherer(opts Options) *Gather {
return &Gather{
Interval: opts.Interval,
Records: NewMetrics(),
closed: true,
}
}
type Gather struct {
Records *Metrics
Interval time.Duration
lock sync.Mutex
closed bool
}
func (mm *Gather) Start() {
mm.lock.Lock()
if !mm.closed {
mm.lock.Unlock()
return
}
mm.closed = false
gopool.Go(mm.loop)
mm.lock.Unlock()
}
func (mm *Gather) loop(ctx context.Context) {
ticker := time.NewTicker(mm.Interval)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := mm.Collect(); err != nil {
log.Error("metrics collect failed", err)
return
}
Report()
}
}
}
func (mm *Gather) Collect() error {
mfs, err := prometheus.Gather()
if err != nil {
return err
}
records := NewMetrics()
for _, mf := range mfs {
name := RecordName(mf.GetName())
if len(name) == 0 {
continue
}
if d := Calculate(mf); d != nil {
records.put(name, d)
}
}
// clean the old cache here
mm.Records = records
return nil
}
func RecordName(metricName string) string {
_, isSys := SysMetrics.Get(metricName)
family := ParseFamily(metricName)
if !isSys && len(family) == 0 {
return ""
}
if strings.Index(metricName, prefixName) == 0 {
// just compatible with sc old metric name without familyName
metricName = strings.TrimPrefix(metricName, prefixName)
}
return metricName
}
func CollectFamily(familyName string) {
families.Add(familyName)
}
func ParseFamily(metricName string) string {
for family := range families.Iter() {
s := family.(string)
if strings.Index(metricName, s+"_") == 0 {
return s
}
}
return ""
}