Skip to content

Commit

Permalink
Merge pull request #9 from dolab/handler-metrics
Browse files Browse the repository at this point in the history
What: export internal metrics for handler develop
  • Loading branch information
mcspring committed Jul 1, 2019
2 parents 49156d3 + 44bc1c6 commit 13b58a9
Show file tree
Hide file tree
Showing 9 changed files with 404 additions and 64 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,45 @@ func main() {
}))
}
```

## Metrics

```go
package main

import (
"net/http"

"github.com/prometheus/client_golang/prometheus"

"github.com/dolab/redis-go"
)

func main() {
// Starts a new http server async for prom.
go http.ListenAndServe(":8080", http.HandlerFunc(redis.ServeMetrics))

// Starts a new counter for local usage.
counter := redis.NewCounterVec("handler", "custom handler counter", []string{"cmd"})

// Starts a new server speaking the redis protocol, the server automatically
// handle asynchronusly pipelining the requests and responses.

redis.ListenAndServe(":6380", redis.HandlerFunc(func(res redis.ResponseWriter, req *redis.Request) {
for _, cmd := range req.Cmds {
counter.With(prometheus.Labels{
"cmd": cmd.Cmd,
}).Inc()
}

// Put the response in streaming mode, will send 3 values.
res.WriteStream(3)

// The response writer automatically encodes Go values into their RESP
// representation.
res.Write(1)
res.Write(2)
res.Write(3)
}))
}
```
13 changes: 13 additions & 0 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ func NewClientConn(conn net.Conn) *Conn {
c.emitter.Reset(&c.wbuffer)
c.decoder = objconv.StreamDecoder{Parser: &c.parser}
c.encoder = objconv.StreamEncoder{Emitter: &c.emitter}

var (
localAddr, remoteAddr string
)
if local := c.LocalAddr(); local != nil {
localAddr = local.String()
}
if remote := c.RemoteAddr(); remote != nil {
remoteAddr = remote.String()
}

gometrics.Dialer(localAddr, remoteAddr)

return c
}

Expand Down
44 changes: 44 additions & 0 deletions metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package redis

import (
"net/http"
"sync"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/dolab/redis-go/metrics"
)

var (
gometrics *metrics.Metrics
gometricsOnce sync.Once
)

func init() {
gometricsOnce.Do(func() {
gometrics = metrics.NewMetrics(nil)

prometheus.MustRegister(gometrics)
})
}

// ServeMetrics exports prometheus metrics of internal server.
func ServeMetrics(w http.ResponseWriter, r *http.Request) {
promhttp.Handler().ServeHTTP(w, r)
}

// NewCounterVec returns a *prometheus.CounterVec for handler usage.
func NewCounterVec(name, help string, labels []string) *prometheus.CounterVec {
return gometrics.Counter(name, help, labels)
}

// NewGaugeVec returns a *prometheus.GaugeVec for handler usage.
func NewGaugeVec(name, help string, labels []string) *prometheus.GaugeVec {
return gometrics.Gauge(name, help, labels)
}

// NewHistogramVec returns a *prometheus.HistogramVec for handler usage.
func NewHistogramVec(name, help string, labels []string) *prometheus.HistogramVec {
return gometrics.Histogram(name, help, labels)
}
48 changes: 43 additions & 5 deletions metrics/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,64 @@ import (
"github.com/prometheus/client_golang/prometheus"
)

func (m *Metrics) Dialer(localAddr, remoteAddr string) {
localN := len(localAddr)
if n := strings.IndexByte(localAddr, ':'); n > 0 {
localN = n
}

remoteN := len(remoteAddr)
if n := strings.IndexByte(remoteAddr, ':'); n > 0 {
remoteN = n
}

labels := prometheus.Labels{
"local_addr": localAddr[:localN],
"remote_addr": remoteAddr[:remoteN],
}

m.monitor.dialer.With(labels).Inc()
}

func (m *Metrics) IncConnection(remoteAddr, localAddr string) {
localN := len(localAddr)
if n := strings.IndexByte(localAddr, ':'); n > 0 {
localN = n
}

remoteN := len(remoteAddr)
if n := strings.IndexByte(remoteAddr, ':'); n > 0 {
remoteN = n
}

labels := prometheus.Labels{
"remote_addr": remoteAddr,
"local_addr": localAddr,
"local_addr": localAddr[:localN],
"remote_addr": remoteAddr[:remoteN],
}

m.monitor.server.connections.With(labels).Inc()
}

func (m *Metrics) DecConnection(remoteAddr, localAddr string) {
localN := len(localAddr)
if n := strings.IndexByte(localAddr, ':'); n > 0 {
localN = n
}

remoteN := len(remoteAddr)
if n := strings.IndexByte(remoteAddr, ':'); n > 0 {
remoteN = n
}

labels := prometheus.Labels{
"remote_addr": remoteAddr,
"local_addr": localAddr,
"local_addr": localAddr[:localN],
"remote_addr": remoteAddr[:remoteN],
}

m.monitor.server.connections.With(labels).Dec()
}

func (m *Metrics) IncRequest(remoteAddr string) {

labels := prometheus.Labels{
"remote_addr": remoteAddr,
}
Expand Down
141 changes: 129 additions & 12 deletions metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,158 @@
package metrics

import (
"net"
"time"
"sync"

"github.com/prometheus/client_golang/prometheus"
)

// Metrics implements both prometheus Collector interface and
// grpc interceptor interface.
// Metrics implements prometheus Collector interface for server and handler.
type Metrics struct {
monitor *Monitor
trackPeers bool
monitor *Monitor

// for custom metrics with handler
mutex sync.RWMutex
counters map[string]*prometheus.CounterVec
gauges map[string]*prometheus.GaugeVec
histograms map[string]*prometheus.HistogramVec
}

// NewMetrics creates a new metrics of grpc interceptor for shared usage.
func NewMetrics(labels prometheus.Labels) *Metrics {
return &Metrics{
monitor: NewMonitor(labels),
monitor: NewMonitor(labels),
counters: make(map[string]*prometheus.CounterVec),
gauges: make(map[string]*prometheus.GaugeVec),
histograms: make(map[string]*prometheus.HistogramVec),
}
}

// Describe implements prometheus Collector interface.
func (m *Metrics) Describe(in chan<- *prometheus.Desc) {
m.monitor.Describe(in)

for _, vec := range m.counters {
vec.Describe(in)
}

for _, vec := range m.gauges {
vec.Describe(in)
}

for _, vec := range m.histograms {
vec.Describe(in)
}
}

// Collect implements prometheus Collector interface.
func (m *Metrics) Collect(in chan<- prometheus.Metric) {
m.monitor.Collect(in)

for _, vec := range m.counters {
vec.Collect(in)
}

for _, vec := range m.gauges {
vec.Collect(in)
}

for _, vec := range m.histograms {
vec.Collect(in)
}
}

// Counter ...
func (m *Metrics) Counter(name, help string, labels []string) *prometheus.CounterVec {
// find
m.mutex.RLock()
if vec, ok := m.counters[name]; ok {
m.mutex.RUnlock()

return vec
}
m.mutex.RUnlock()

// create
m.mutex.Lock()
defer m.mutex.Unlock()

if vec, ok := m.counters[name]; ok {
return vec
}

m.counters[name] = prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: "redis",
Subsystem: "handler",
Name: name,
Help: help,
},
labels,
)

return m.counters[name]
}

// Gauge ...
func (m *Metrics) Gauge(name, help string, labels []string) *prometheus.GaugeVec {
// find
m.mutex.RLock()
if vec, ok := m.gauges[name]; ok {
m.mutex.RUnlock()

return vec
}
m.mutex.RUnlock()

// create
m.mutex.Lock()
defer m.mutex.Unlock()

if vec, ok := m.gauges[name]; ok {
return vec
}

m.gauges[name] = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: "redis",
Subsystem: "handler",
Name: name,
Help: help,
},
labels,
)

return m.gauges[name]
}

// Dialer ...
func (m *Metrics) Dialer(f func(string, time.Duration) (net.Conn, error)) func(string, time.Duration) (net.Conn, error) {
return func(addr string, timeout time.Duration) (net.Conn, error) {
m.monitor.dialer.WithLabelValues(addr).Inc()
// Histogram ...
func (m *Metrics) Histogram(name, help string, labels []string) *prometheus.HistogramVec {
// find
m.mutex.RLock()
if vec, ok := m.histograms[name]; ok {
m.mutex.RUnlock()

return f(addr, timeout)
return vec
}
m.mutex.RUnlock()

// create
m.mutex.Lock()
defer m.mutex.Unlock()

if vec, ok := m.histograms[name]; ok {
return vec
}

m.histograms[name] = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "redis",
Subsystem: "handler",
Name: name,
Help: help,
},
labels,
)

return m.histograms[name]
}
Loading

0 comments on commit 13b58a9

Please sign in to comment.