refactoring monitor code, support prometheus (#1668)

* refactoring monitor code, support prometheus
* remove vendor
This commit is contained in:
fatedier
2020-03-11 13:20:26 +08:00
committed by GitHub
parent 6d1af85e80
commit 495d999b6c
889 changed files with 682 additions and 333140 deletions

View File

@@ -48,31 +48,25 @@ type ServerCommonConf struct {
// ProxyBindAddr specifies the address that the proxy binds to. This value
// may be the same as BindAddr. By default, this value is "0.0.0.0".
ProxyBindAddr string `json:"proxy_bind_addr"`
// VhostHttpPort specifies the port that the server listens for HTTP Vhost
// requests. If this value is 0, the server will not listen for HTTP
// requests. By default, this value is 0.
VhostHttpPort int `json:"vhost_http_port"`
// VhostHttpsPort specifies the port that the server listens for HTTPS
// Vhost requests. If this value is 0, the server will not listen for HTTPS
// requests. By default, this value is 0.
VhostHttpsPort int `json:"vhost_https_port"`
// TcpMuxHttpConnectPort specifies the port that the server listens for TCP
// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
// requests on one single port. If it's not - it will listen on this value for
// HTTP CONNECT requests. By default, this value is 0.
TcpMuxHttpConnectPort int `json:"tcpmux_httpconnect_port"`
// VhostHttpTimeout specifies the response header timeout for the Vhost
// HTTP server, in seconds. By default, this value is 60.
VhostHttpTimeout int64 `json:"vhost_http_timeout"`
// DashboardAddr specifies the address that the dashboard binds to. By
// default, this value is "0.0.0.0".
DashboardAddr string `json:"dashboard_addr"`
// DashboardPort specifies the port that the dashboard listens on. If this
// value is 0, the dashboard will not be started. By default, this value is
// 0.
@@ -83,6 +77,9 @@ type ServerCommonConf struct {
// DashboardUser specifies the password that the dashboard will use for
// login. By default, this value is "admin".
DashboardPwd string `json:"dashboard_pwd"`
// EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port}
// in /metrics api.
EnablePrometheus bool `json:"enable_prometheus"`
// AssetsDir specifies the local directory that the dashboard will load
// resources from. If this value is "", assets will be loaded from the
// bundled executable using statik. By default, this value is "".
@@ -167,6 +164,7 @@ func GetDefaultServerConf() ServerCommonConf {
DashboardPort: 0,
DashboardUser: "admin",
DashboardPwd: "admin",
EnablePrometheus: false,
AssetsDir: "",
LogFile: "console",
LogWay: "console",
@@ -312,6 +310,10 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
cfg.DashboardPwd = tmpStr
}
if tmpStr, ok = conf.Get("common", "enable_prometheus"); ok && tmpStr == "true" {
cfg.EnablePrometheus = true
}
if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
cfg.AssetsDir = tmpStr
}

View File

@@ -0,0 +1,93 @@
// Copyright 2020 fatedier, fatedier@gmail.com
//
// Licensed 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 aggregate
import (
"github.com/fatedier/frp/models/metrics/mem"
"github.com/fatedier/frp/models/metrics/prometheus"
"github.com/fatedier/frp/server/metrics"
)
// EnableMem start to mark metrics to memory monitor system.
func EnableMem() {
sm.Add(mem.ServerMetrics)
}
// EnablePrometheus start to mark metrics to prometheus.
func EnablePrometheus() {
sm.Add(prometheus.ServerMetrics)
}
var sm *serverMetrics = &serverMetrics{}
func init() {
metrics.Register(sm)
}
type serverMetrics struct {
ms []metrics.ServerMetrics
}
func (m *serverMetrics) Add(sm metrics.ServerMetrics) {
m.ms = append(m.ms, sm)
}
func (m *serverMetrics) NewClient() {
for _, v := range m.ms {
v.NewClient()
}
}
func (m *serverMetrics) CloseClient() {
for _, v := range m.ms {
v.CloseClient()
}
}
func (m *serverMetrics) NewProxy(name string, proxyType string) {
for _, v := range m.ms {
v.NewProxy(name, proxyType)
}
}
func (m *serverMetrics) CloseProxy(name string, proxyType string) {
for _, v := range m.ms {
v.CloseProxy(name, proxyType)
}
}
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
for _, v := range m.ms {
v.OpenConnection(name, proxyType)
}
}
func (m *serverMetrics) CloseConnection(name string, proxyType string) {
for _, v := range m.ms {
v.CloseConnection(name, proxyType)
}
}
func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
for _, v := range m.ms {
v.AddTrafficIn(name, proxyType, trafficBytes)
}
}
func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
for _, v := range m.ms {
v.AddTrafficOut(name, proxyType, trafficBytes)
}
}

View File

@@ -0,0 +1,262 @@
// Copyright 2019 fatedier, fatedier@gmail.com
//
// Licensed 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 mem
import (
"sync"
"time"
server "github.com/fatedier/frp/server/metrics"
"github.com/fatedier/frp/utils/log"
"github.com/fatedier/frp/utils/metric"
)
var sm *serverMetrics = newServerMetrics()
var ServerMetrics server.ServerMetrics
var StatsCollector Collector
func init() {
ServerMetrics = sm
StatsCollector = sm
sm.run()
}
type serverMetrics struct {
info *ServerStatistics
mu sync.Mutex
}
func newServerMetrics() *serverMetrics {
return &serverMetrics{
info: &ServerStatistics{
TotalTrafficIn: metric.NewDateCounter(ReserveDays),
TotalTrafficOut: metric.NewDateCounter(ReserveDays),
CurConns: metric.NewCounter(),
ClientCounts: metric.NewCounter(),
ProxyTypeCounts: make(map[string]metric.Counter),
ProxyStatistics: make(map[string]*ProxyStatistics),
},
}
}
func (m *serverMetrics) run() {
go func() {
for {
time.Sleep(12 * time.Hour)
log.Debug("start to clear useless proxy statistics data...")
m.clearUselessInfo()
log.Debug("finish to clear useless proxy statistics data")
}
}()
}
func (m *serverMetrics) clearUselessInfo() {
// To check if there are proxies that closed than 7 days and drop them.
m.mu.Lock()
defer m.mu.Unlock()
for name, data := range m.info.ProxyStatistics {
if !data.LastCloseTime.IsZero() && time.Since(data.LastCloseTime) > time.Duration(7*24)*time.Hour {
delete(m.info.ProxyStatistics, name)
log.Trace("clear proxy [%s]'s statistics data, lastCloseTime: [%s]", name, data.LastCloseTime.String())
}
}
}
func (m *serverMetrics) NewClient() {
m.info.ClientCounts.Inc(1)
}
func (m *serverMetrics) CloseClient() {
m.info.ClientCounts.Dec(1)
}
func (m *serverMetrics) NewProxy(name string, proxyType string) {
m.mu.Lock()
defer m.mu.Unlock()
counter, ok := m.info.ProxyTypeCounts[proxyType]
if !ok {
counter = metric.NewCounter()
}
counter.Inc(1)
m.info.ProxyTypeCounts[proxyType] = counter
proxyStats, ok := m.info.ProxyStatistics[name]
if !(ok && proxyStats.ProxyType == proxyType) {
proxyStats = &ProxyStatistics{
Name: name,
ProxyType: proxyType,
CurConns: metric.NewCounter(),
TrafficIn: metric.NewDateCounter(ReserveDays),
TrafficOut: metric.NewDateCounter(ReserveDays),
}
m.info.ProxyStatistics[name] = proxyStats
}
proxyStats.LastStartTime = time.Now()
}
func (m *serverMetrics) CloseProxy(name string, proxyType string) {
m.mu.Lock()
defer m.mu.Unlock()
if counter, ok := m.info.ProxyTypeCounts[proxyType]; ok {
counter.Dec(1)
}
if proxyStats, ok := m.info.ProxyStatistics[name]; ok {
proxyStats.LastCloseTime = time.Now()
}
}
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
m.info.CurConns.Inc(1)
m.mu.Lock()
defer m.mu.Unlock()
proxyStats, ok := m.info.ProxyStatistics[name]
if ok {
proxyStats.CurConns.Inc(1)
m.info.ProxyStatistics[name] = proxyStats
}
}
func (m *serverMetrics) CloseConnection(name string, proxyType string) {
m.info.CurConns.Dec(1)
m.mu.Lock()
defer m.mu.Unlock()
proxyStats, ok := m.info.ProxyStatistics[name]
if ok {
proxyStats.CurConns.Dec(1)
m.info.ProxyStatistics[name] = proxyStats
}
}
func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
m.info.TotalTrafficIn.Inc(trafficBytes)
m.mu.Lock()
defer m.mu.Unlock()
proxyStats, ok := m.info.ProxyStatistics[name]
if ok {
proxyStats.TrafficIn.Inc(trafficBytes)
m.info.ProxyStatistics[name] = proxyStats
}
}
func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
m.info.TotalTrafficOut.Inc(trafficBytes)
m.mu.Lock()
defer m.mu.Unlock()
proxyStats, ok := m.info.ProxyStatistics[name]
if ok {
proxyStats.TrafficOut.Inc(trafficBytes)
m.info.ProxyStatistics[name] = proxyStats
}
}
// Get stats data api.
func (m *serverMetrics) GetServer() *ServerStats {
m.mu.Lock()
defer m.mu.Unlock()
s := &ServerStats{
TotalTrafficIn: m.info.TotalTrafficIn.TodayCount(),
TotalTrafficOut: m.info.TotalTrafficOut.TodayCount(),
CurConns: m.info.CurConns.Count(),
ClientCounts: m.info.ClientCounts.Count(),
ProxyTypeCounts: make(map[string]int64),
}
for k, v := range m.info.ProxyTypeCounts {
s.ProxyTypeCounts[k] = v.Count()
}
return s
}
func (m *serverMetrics) GetProxiesByType(proxyType string) []*ProxyStats {
res := make([]*ProxyStats, 0)
m.mu.Lock()
defer m.mu.Unlock()
for name, proxyStats := range m.info.ProxyStatistics {
if proxyStats.ProxyType != proxyType {
continue
}
ps := &ProxyStats{
Name: name,
Type: proxyStats.ProxyType,
TodayTrafficIn: proxyStats.TrafficIn.TodayCount(),
TodayTrafficOut: proxyStats.TrafficOut.TodayCount(),
CurConns: proxyStats.CurConns.Count(),
}
if !proxyStats.LastStartTime.IsZero() {
ps.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05")
}
if !proxyStats.LastCloseTime.IsZero() {
ps.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05")
}
res = append(res, ps)
}
return res
}
func (m *serverMetrics) GetProxiesByTypeAndName(proxyType string, proxyName string) (res *ProxyStats) {
m.mu.Lock()
defer m.mu.Unlock()
for name, proxyStats := range m.info.ProxyStatistics {
if proxyStats.ProxyType != proxyType {
continue
}
if name != proxyName {
continue
}
res = &ProxyStats{
Name: name,
Type: proxyStats.ProxyType,
TodayTrafficIn: proxyStats.TrafficIn.TodayCount(),
TodayTrafficOut: proxyStats.TrafficOut.TodayCount(),
CurConns: proxyStats.CurConns.Count(),
}
if !proxyStats.LastStartTime.IsZero() {
res.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05")
}
if !proxyStats.LastCloseTime.IsZero() {
res.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05")
}
break
}
return
}
func (m *serverMetrics) GetProxyTraffic(name string) (res *ProxyTrafficInfo) {
m.mu.Lock()
defer m.mu.Unlock()
proxyStats, ok := m.info.ProxyStatistics[name]
if ok {
res = &ProxyTrafficInfo{
Name: name,
}
res.TrafficIn = proxyStats.TrafficIn.GetLastDaysCount(ReserveDays)
res.TrafficOut = proxyStats.TrafficOut.GetLastDaysCount(ReserveDays)
}
return
}

View File

@@ -0,0 +1,82 @@
// Copyright 2017 fatedier, fatedier@gmail.com
//
// Licensed 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 mem
import (
"time"
"github.com/fatedier/frp/utils/metric"
)
const (
ReserveDays = 7
)
type ServerStats struct {
TotalTrafficIn int64
TotalTrafficOut int64
CurConns int64
ClientCounts int64
ProxyTypeCounts map[string]int64
}
type ProxyStats struct {
Name string
Type string
TodayTrafficIn int64
TodayTrafficOut int64
LastStartTime string
LastCloseTime string
CurConns int64
}
type ProxyTrafficInfo struct {
Name string
TrafficIn []int64
TrafficOut []int64
}
type ProxyStatistics struct {
Name string
ProxyType string
TrafficIn metric.DateCounter
TrafficOut metric.DateCounter
CurConns metric.Counter
LastStartTime time.Time
LastCloseTime time.Time
}
type ServerStatistics struct {
TotalTrafficIn metric.DateCounter
TotalTrafficOut metric.DateCounter
CurConns metric.Counter
// counter for clients
ClientCounts metric.Counter
// counter for proxy types
ProxyTypeCounts map[string]metric.Counter
// statistics for different proxies
// key is proxy name
ProxyStatistics map[string]*ProxyStatistics
}
type Collector interface {
GetServer() *ServerStats
GetProxiesByType(proxyType string) []*ProxyStats
GetProxiesByTypeAndName(proxyType string, proxyName string) *ProxyStats
GetProxyTraffic(name string) *ProxyTrafficInfo
}

View File

@@ -0,0 +1,8 @@
package metrics
import (
"github.com/fatedier/frp/models/metrics/aggregate"
)
var EnableMem = aggregate.EnableMem
var EnablePrometheus = aggregate.EnablePrometheus

View File

@@ -0,0 +1,95 @@
package prometheus
import (
"github.com/fatedier/frp/server/metrics"
"github.com/prometheus/client_golang/prometheus"
)
const (
namespace = "frp"
serverSubsystem = "server"
)
var ServerMetrics metrics.ServerMetrics = newServerMetrics()
type serverMetrics struct {
clientCount prometheus.Gauge
proxyCount *prometheus.GaugeVec
connectionCount *prometheus.GaugeVec
trafficIn *prometheus.CounterVec
trafficOut *prometheus.CounterVec
}
func (m *serverMetrics) NewClient() {
m.clientCount.Inc()
}
func (m *serverMetrics) CloseClient() {
m.clientCount.Dec()
}
func (m *serverMetrics) NewProxy(name string, proxyType string) {
m.proxyCount.WithLabelValues(proxyType).Inc()
}
func (m *serverMetrics) CloseProxy(name string, proxyType string) {
m.proxyCount.WithLabelValues(proxyType).Dec()
}
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
m.connectionCount.WithLabelValues(name, proxyType).Inc()
}
func (m *serverMetrics) CloseConnection(name string, proxyType string) {
m.connectionCount.WithLabelValues(name, proxyType).Dec()
}
func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
m.trafficIn.WithLabelValues(name, proxyType).Add(float64(trafficBytes))
}
func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
m.trafficOut.WithLabelValues(name, proxyType).Add(float64(trafficBytes))
}
func newServerMetrics() *serverMetrics {
m := &serverMetrics{
clientCount: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: serverSubsystem,
Name: "client_counts",
Help: "The current client counts of frps",
}),
proxyCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: serverSubsystem,
Name: "proxy_counts",
Help: "The current proxy counts",
}, []string{"type"}),
connectionCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: serverSubsystem,
Name: "connection_counts",
Help: "The current connection counts",
}, []string{"name", "type"}),
trafficIn: prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: serverSubsystem,
Name: "traffic_in",
Help: "The total in traffic",
}, []string{"name", "type"}),
trafficOut: prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: serverSubsystem,
Name: "traffic_out",
Help: "The total out traffic",
}, []string{"name", "type"}),
}
prometheus.MustRegister(m.clientCount)
prometheus.MustRegister(m.proxyCount)
prometheus.MustRegister(m.connectionCount)
prometheus.MustRegister(m.trafficIn)
prometheus.MustRegister(m.trafficOut)
return m
}