Compare commits

..

1 Commits

Author SHA1 Message Date
Ian Fijolek
6c7c0a470f WIP: Begin adding prometheus metrics exporting 2019-11-15 11:25:21 -08:00
9 changed files with 30 additions and 223 deletions
-49
View File
@@ -1,4 +1,3 @@
---
kind: pipeline
name: test
@@ -12,51 +11,3 @@ steps:
image: golang:1.12
commands:
- make test
- name: check
image: python:3
commands:
- pip install pre-commit==1.20.0
- make check
- name: notify
image: drillster/drone-email
settings:
host:
from_secret: SMTP_HOST
username:
from_secret: SMTP_USER
password:
from_secret: SMTP_PASS
from: drone@iamthefij.com
when:
status: [changed, failure]
---
kind: pipeline
name: publish
depends_on:
- test
trigger:
event:
- push
- tag
refs:
- refs/heads/master
- refs/tags/v*
steps:
# Might consider moving this step into the previous pipeline
- name: push image
image: plugins/docker
settings:
repo: iamthefij/minitor-go
dockerfile: Dockerfile.multi-stage
auto_tag: true
username:
from_secret: docker_username
password:
from_secret: docker_password
-19
View File
@@ -1,19 +0,0 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
hooks:
- id: check-added-large-files
- id: check-yaml
args:
- --allow-multiple-documents
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-merge-conflict
- repo: git://github.com/dnephin/pre-commit-golang
rev: v0.3.5
hooks:
- id: go-fmt
- id: go-imports
# - id: gometalinter
# - id: golangci-lint
+1 -15
View File
@@ -1,7 +1,6 @@
.PHONY: all
DOCKER_TAG ?= minitor-go-${USER}
.PHONY: default
.PHONY: test
default: test
.PHONY: build
@@ -15,10 +14,6 @@ minitor-go:
run: minitor-go build
./minitor-go -debug
.PHONY: run-metrics
run-metrics: minitor-go build
./minitor-go -debug -metrics
.PHONY: test
test:
go test -coverprofile=coverage.out
@@ -29,15 +24,6 @@ test:
@go tool cover -func=coverage.out | awk -v target=80.0% \
'/^total:/ { print "Total coverage: " $$3 " Minimum coverage: " target; if ($$3+0.0 >= target+0.0) print "ok"; else { print "fail"; exit 1; } }'
# Installs pre-commit hooks
.PHONY: install-hooks
install-hooks:
pre-commit install --install-hooks
# Checks files for encryption
.PHONY: check
check:
pre-commit run --all-files
.PHONY: clean
clean:
+7 -9
View File
@@ -2,7 +2,7 @@
A reimplementation of [Minitor](https://git.iamthefij/iamthefij/minitor) in Go
Minitor is already a minimal monitoring tool. Python 3 was a quick way to get something live, but Python itself comes with a large footprint. Thus Go feels like a better fit for the project, longer term.
Minitor is already a very minimal monitoring tool. Python 3 was a quick way to get something live, but Python itself comes with a very large footprint.Thus Go feels like a better fit for the project, longer term.
Initial target is meant to be roughly compatible requiring only minor changes to configuration. Future iterations may diverge to take advantage of Go specific features.
@@ -30,7 +30,7 @@ monitors:
command_shell: echo 'test'
```
Second, templating for Alert messages has been updated. In the Python version, `str.format(...)` was used with certain keys passed in that could be used to format messages. In the Go version, we use a struct, `AlertNotice` defined in `alert.go` and the built in Go templating format. Eg.
Second, templating for Alert messages has been updated. In the Python version, `str.format(...)` was used with certain keys passed in that could be used to format messages. In the Go version, we use a struct containing Alert info and the built in Go templating format. Eg.
minitor-py:
```yaml
@@ -38,7 +38,7 @@ alerts:
log_command:
command: ['echo', '{monitor_name}']
log_shell:
command_shell: 'echo {monitor_name}'
command_shell: "echo {monitor_name}"
```
minitor-go:
@@ -47,7 +47,7 @@ alerts:
log_command:
command: ['echo', '{{.MonitorName}}']
log_shell:
command_shell: 'echo {{.MonitorName}}'
command_shell: "echo {{.MonitorName}}"
```
Finally, newlines in a shell command don't terminate a particular command. Semicolons must be used and continuations should not.
@@ -84,11 +84,10 @@ Pairity:
- [x] Run alert commands
- [x] Run alert commands in a shell
- [x] Allow templating of alert commands
- [x] Implement Prometheus client to export metrics
- [x] Test coverage
- [ ] Integration testing (manual or otherwise)
- [ ] Implement Prometheus client to export metrics
- [ ] Test coverage
Improvement (potentially breaking):
Improvement:
- [ ] Implement leveled logging (maybe glog or logrus)
- [ ] Consider switching from YAML to TOML
@@ -96,4 +95,3 @@ Improvement (potentially breaking):
- [ ] Consider dropping `alert_up` and `alert_down` in favor of using Go templates that offer more control of messaging
- [ ] Async checking
- [ ] Use durations rather than seconds checked in event loop
- [ ] Revisit metrics and see if they all make sense
+2 -5
View File
@@ -2,11 +2,10 @@ package main
import (
"errors"
"gopkg.in/yaml.v2"
"io/ioutil"
"log"
"os"
"gopkg.in/yaml.v2"
)
// Config type is contains all provided user configuration
@@ -85,9 +84,7 @@ func LoadConfig(filePath string) (config Config, err error) {
return
}
if LogDebug {
log.Printf("DEBUG: Config values:\n%v\n", config)
}
log.Printf("config:\n%v\n", config)
if !config.IsValid() {
err = errors.New("Invalid configuration")
+9 -15
View File
@@ -3,7 +3,9 @@ package main
import (
"flag"
"fmt"
"github.com/prometheus/client_golang/prometheus/promhttp"
"log"
"net/http"
"time"
)
@@ -13,10 +15,6 @@ var (
// ExportMetrics will track whether or not we want to export metrics to prometheus
ExportMetrics = false
// MetricsPort is the port to expose metrics on
MetricsPort = 8080
// Metrics contains all active metrics
Metrics = NewMetrics()
// version of minitor being run
version = "dev"
@@ -25,13 +23,7 @@ var (
func checkMonitors(config *Config) error {
for _, monitor := range config.Monitors {
if monitor.ShouldCheck() {
success, alertNotice := monitor.Check()
hasAlert := alertNotice != nil
// Track status metrics
Metrics.SetMonitorStatus(monitor.Name, success)
Metrics.CountCheck(monitor.Name, success, hasAlert)
_, alertNotice := monitor.Check()
// Should probably consider refactoring everything below here
if alertNotice != nil {
@@ -63,9 +55,6 @@ func checkMonitors(config *Config) error {
err,
)
}
// Count alert metrics
Metrics.CountAlert(monitor.Name, alert.Name)
} else {
// This case should never actually happen since we validate against it
log.Printf("ERROR: Unknown alert for monitor %s: %s", alertNotice.MonitorName, alertName)
@@ -79,6 +68,11 @@ func checkMonitors(config *Config) error {
return nil
}
func serveMetrics() {
http.Handle("/metrics", promhttp.Handler())
_ = http.ListenAndServe(":8080", nil)
}
func main() {
// Get debug flag
flag.BoolVar(&LogDebug, "debug", false, "Enables debug logs (default: false)")
@@ -101,7 +95,7 @@ func main() {
// Serve metrics exporter, if specified
if ExportMetrics {
log.Println("INFO: Exporting metrics to Prometheus")
go ServeMetrics()
go serveMetrics()
}
// Start main loop
-101
View File
@@ -1,101 +0,0 @@
package main
import (
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// TODO: Not sure if this is the best way to handle. A global instance for
// metrics isn't bad, but it might be nice to curry versions of the metrics
// for each monitor. Especially since every monitor has it's own. Perhaps
// another new function that essentially curries each metric for a given
// monitor name would do. This could be run when validating monitors and
// initializing alert templates.
// MinitorMetrics contains all counters and metrics that Minitor will need to access
type MinitorMetrics struct {
alertCount *prometheus.CounterVec
checkCount *prometheus.CounterVec
monitorStatus *prometheus.GaugeVec
}
// NewMetrics creates and initializes all metrics
func NewMetrics() *MinitorMetrics {
// Initialize all metrics
metrics := &MinitorMetrics{
alertCount: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "minitor_alert_total",
Help: "Number of Minitor alerts",
},
[]string{"alert", "monitor"},
),
checkCount: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "minitor_check_total",
Help: "Number of Minitor checks",
},
[]string{"monitor", "status", "is_alert"},
),
monitorStatus: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "minitor_monitor_up_count",
Help: "Status of currently responsive monitors",
},
[]string{"monitor"},
),
}
// Register newly created metrics
prometheus.MustRegister(metrics.alertCount)
prometheus.MustRegister(metrics.checkCount)
prometheus.MustRegister(metrics.monitorStatus)
return metrics
}
// SetMonitorStatus sets the current status of Monitor
func (metrics *MinitorMetrics) SetMonitorStatus(monitor string, isUp bool) {
val := 0.0
if isUp {
val = 1.0
}
metrics.monitorStatus.With(prometheus.Labels{"monitor": monitor}).Set(val)
}
// CountCheck counts the result of a particular Monitor check
func (metrics *MinitorMetrics) CountCheck(monitor string, isSuccess bool, isAlert bool) {
status := "failure"
if isSuccess {
status = "success"
}
alertVal := "false"
if isAlert {
alertVal = "true"
}
metrics.checkCount.With(
prometheus.Labels{"monitor": monitor, "status": status, "is_alert": alertVal},
).Inc()
}
// CountAlert counts an alert
func (metrics *MinitorMetrics) CountAlert(monitor string, alert string) {
metrics.alertCount.With(
prometheus.Labels{
"alert": alert,
"monitor": monitor,
},
).Inc()
}
// ServeMetrics starts an http server with a Prometheus metrics handler
func ServeMetrics() {
http.Handle("/metrics", promhttp.Handler())
host := fmt.Sprintf(":%d", MetricsPort)
_ = http.ListenAndServe(host, nil)
}
+1
View File
@@ -6,3 +6,4 @@ monitors:
alert_down: [ 'alert_down', 'log_shell', 'log_command' ]
# alert_every: -1
alert_every: 0