| /* |
| * 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 "" |
| } |