| // Licensed to 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. Apache Software Foundation (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 observability |
| |
| import ( |
| "context" |
| "strings" |
| "sync" |
| |
| "github.com/shirou/gopsutil/v3/cpu" |
| "github.com/shirou/gopsutil/v3/mem" |
| "github.com/shirou/gopsutil/v3/net" |
| |
| "github.com/apache/skywalking-banyandb/pkg/logger" |
| "github.com/apache/skywalking-banyandb/pkg/meter" |
| ) |
| |
| var log = logger.GetLogger("observability", "metrics", "system") |
| |
| var ( |
| cpuCount = 0 |
| once4CpuCount sync.Once |
| cpuCountsFunc = cpu.Counts |
| cpuTimesFunc = cpu.Times |
| ) |
| |
| var ( |
| // RootScope is the root scope for all metrics. |
| RootScope = meter.NewHierarchicalScope("banyandb", "_") |
| |
| systemScope = RootScope.SubScope("system") |
| |
| systemProvider = NewMeterProvider(systemScope) |
| cpuStateGauge = systemProvider.Gauge("cpu_state", "kind") |
| cpuNumGauge = systemProvider.Gauge("cpu_num") |
| memorySateGauge = systemProvider.Gauge("memory_state", "kind") |
| netStateGauge = systemProvider.Gauge("net_state", "kind", "name") |
| ) |
| |
| func init() { |
| MetricsCollector.Register("cpu", collectCPU) |
| MetricsCollector.Register("memory", collectMemory) |
| MetricsCollector.Register("net", collectNet) |
| } |
| |
| func collectCPU() { |
| once4CpuCount.Do(func() { |
| if c, err := cpuCountsFunc(false); err != nil { |
| log.Error().Err(err).Msg("cannot get cpu count") |
| } else { |
| cpuCount = c |
| } |
| }) |
| cpuNumGauge.Set(float64(cpuCount)) |
| s, err := cpuTimesFunc(false) |
| if err != nil { |
| log.Error().Err(err).Msg("cannot get cpu stat") |
| } |
| if len(s) == 0 { |
| log.Error().Msg("cannot get cpu stat") |
| } |
| allStat := s[0] |
| total := allStat.User + allStat.System + allStat.Idle + allStat.Nice + allStat.Iowait + allStat.Irq + |
| allStat.Softirq + allStat.Steal + allStat.Guest + allStat.GuestNice |
| cpuStateGauge.Set(allStat.User/total, "user") |
| cpuStateGauge.Set(allStat.System/total, "system") |
| cpuStateGauge.Set(allStat.Idle/total, "idle") |
| cpuStateGauge.Set(allStat.Nice/total, "nice") |
| cpuStateGauge.Set(allStat.Iowait/total, "iowait") |
| cpuStateGauge.Set(allStat.Irq/total, "irq") |
| cpuStateGauge.Set(allStat.Softirq/total, "softirq") |
| cpuStateGauge.Set(allStat.Steal/total, "steal") |
| } |
| |
| func collectMemory() { |
| m, err := mem.VirtualMemory() |
| if err != nil { |
| log.Error().Err(err).Msg("cannot get memory stat") |
| } |
| memorySateGauge.Set(m.UsedPercent/100, "used_percent") |
| memorySateGauge.Set(float64(m.Used)/float64(m.Total), "used") |
| } |
| |
| func collectNet() { |
| stats, err := getNetStat(context.Background()) |
| if err != nil { |
| log.Error().Err(err).Msg("cannot get net stat") |
| } |
| for _, stat := range stats { |
| netStateGauge.Set(float64(stat.BytesRecv), "bytes_recv", stat.Name) |
| netStateGauge.Set(float64(stat.BytesSent), "bytes_sent", stat.Name) |
| netStateGauge.Set(float64(stat.PacketsRecv), "packets_recv", stat.Name) |
| netStateGauge.Set(float64(stat.PacketsSent), "packets_sent", stat.Name) |
| netStateGauge.Set(float64(stat.Errin), "errin", stat.Name) |
| netStateGauge.Set(float64(stat.Errout), "errout", stat.Name) |
| netStateGauge.Set(float64(stat.Dropin), "dropin", stat.Name) |
| netStateGauge.Set(float64(stat.Dropout), "dropout", stat.Name) |
| netStateGauge.Set(float64(stat.Fifoin), "fifoin", stat.Name) |
| netStateGauge.Set(float64(stat.Fifoout), "fifoout", stat.Name) |
| } |
| } |
| |
| func getNetStat(ctx context.Context) ([]net.IOCountersStat, error) { |
| stats, err := net.IOCountersWithContext(ctx, true) |
| if err != nil { |
| return nil, err |
| } |
| var availableStats []net.IOCountersStat |
| for _, stat := range stats { |
| switch { |
| // OS X |
| case strings.HasPrefix(stat.Name, "en"): |
| // Linux |
| case strings.HasPrefix(stat.Name, "eth"): |
| default: |
| continue |
| } |
| // ignore empty interface |
| if stat.BytesRecv == 0 || stat.BytesSent == 0 { |
| continue |
| } |
| availableStats = append(availableStats, stat) |
| } |
| return availableStats, nil |
| } |