mirror of
https://github.com/axllent/mailpit.git
synced 2025-08-13 20:04:49 +02:00
Feature: Add Prometheus exporter (#505)
This commit is contained in:
17
cmd/root.go
17
cmd/root.go
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/axllent/mailpit/config"
|
"github.com/axllent/mailpit/config"
|
||||||
"github.com/axllent/mailpit/internal/auth"
|
"github.com/axllent/mailpit/internal/auth"
|
||||||
"github.com/axllent/mailpit/internal/logger"
|
"github.com/axllent/mailpit/internal/logger"
|
||||||
|
"github.com/axllent/mailpit/internal/prometheus"
|
||||||
"github.com/axllent/mailpit/internal/smtpd"
|
"github.com/axllent/mailpit/internal/smtpd"
|
||||||
"github.com/axllent/mailpit/internal/smtpd/chaos"
|
"github.com/axllent/mailpit/internal/smtpd/chaos"
|
||||||
"github.com/axllent/mailpit/internal/storage"
|
"github.com/axllent/mailpit/internal/storage"
|
||||||
@@ -39,6 +40,14 @@ Documentation:
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start Prometheus metrics if enabled
|
||||||
|
switch prometheus.GetMode() {
|
||||||
|
case "integrated":
|
||||||
|
prometheus.StartUpdater()
|
||||||
|
case "separate":
|
||||||
|
go prometheus.StartSeparateServer()
|
||||||
|
}
|
||||||
|
|
||||||
go server.Listen()
|
go server.Listen()
|
||||||
|
|
||||||
if err := smtpd.Listen(); err != nil {
|
if err := smtpd.Listen(); err != nil {
|
||||||
@@ -112,6 +121,9 @@ func init() {
|
|||||||
rootCmd.Flags().StringVar(&config.SendAPIAuthFile, "send-api-auth-file", config.SendAPIAuthFile, "A password file for Send API authentication")
|
rootCmd.Flags().StringVar(&config.SendAPIAuthFile, "send-api-auth-file", config.SendAPIAuthFile, "A password file for Send API authentication")
|
||||||
rootCmd.Flags().BoolVar(&config.SendAPIAuthAcceptAny, "send-api-auth-accept-any", config.SendAPIAuthAcceptAny, "Accept any username and password for the Send API endpoint, including none")
|
rootCmd.Flags().BoolVar(&config.SendAPIAuthAcceptAny, "send-api-auth-accept-any", config.SendAPIAuthAcceptAny, "Accept any username and password for the Send API endpoint, including none")
|
||||||
|
|
||||||
|
// Prometheus Metrics
|
||||||
|
rootCmd.Flags().StringVar(&config.PrometheusListen, "enable-prometheus", config.PrometheusListen, "Enable Prometheus metrics: false=disabled, 'true'=use web port, address=separate server (':9090')")
|
||||||
|
|
||||||
// SMTP server
|
// SMTP server
|
||||||
rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port")
|
rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port")
|
||||||
rootCmd.Flags().StringVar(&config.SMTPAuthFile, "smtp-auth-file", config.SMTPAuthFile, "A password file for SMTP authentication")
|
rootCmd.Flags().StringVar(&config.SMTPAuthFile, "smtp-auth-file", config.SMTPAuthFile, "A password file for SMTP authentication")
|
||||||
@@ -262,6 +274,11 @@ func initConfigFromEnv() {
|
|||||||
config.SendAPIAuthAcceptAny = true
|
config.SendAPIAuthAcceptAny = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prometheus Metrics
|
||||||
|
if len(os.Getenv("MP_ENABLE_PROMETHEUS")) > 0 {
|
||||||
|
config.PrometheusListen = os.Getenv("MP_ENABLE_PROMETHEUS")
|
||||||
|
}
|
||||||
|
|
||||||
// SMTP server
|
// SMTP server
|
||||||
if len(os.Getenv("MP_SMTP_BIND_ADDR")) > 0 {
|
if len(os.Getenv("MP_SMTP_BIND_ADDR")) > 0 {
|
||||||
config.SMTPListen = os.Getenv("MP_SMTP_BIND_ADDR")
|
config.SMTPListen = os.Getenv("MP_SMTP_BIND_ADDR")
|
||||||
|
@@ -191,6 +191,10 @@ var (
|
|||||||
// AllowUntrustedTLS allows untrusted HTTPS connections link checking & screenshot generation
|
// AllowUntrustedTLS allows untrusted HTTPS connections link checking & screenshot generation
|
||||||
AllowUntrustedTLS bool
|
AllowUntrustedTLS bool
|
||||||
|
|
||||||
|
// PrometheusListen address for Prometheus metrics server
|
||||||
|
// Empty = disabled, "true"= use existing web server, address = separate server
|
||||||
|
PrometheusListen string
|
||||||
|
|
||||||
// Version is the default application version, updated on release
|
// Version is the default application version, updated on release
|
||||||
Version = "dev"
|
Version = "dev"
|
||||||
|
|
||||||
@@ -567,5 +571,17 @@ func VerifyConfig() error {
|
|||||||
logger.Log().Info("demo mode enabled")
|
logger.Log().Info("demo mode enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prometheus configuration validation
|
||||||
|
if PrometheusListen != "" {
|
||||||
|
mode := strings.ToLower(strings.TrimSpace(PrometheusListen))
|
||||||
|
if mode != "true" && mode != "false" {
|
||||||
|
// Validate as address for separate server mode
|
||||||
|
_, err := net.ResolveTCPAddr("tcp", PrometheusListen)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[prometheus] %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
8
go.mod
8
go.mod
@@ -21,6 +21,7 @@ require (
|
|||||||
github.com/leporo/sqlf v1.4.0
|
github.com/leporo/sqlf v1.4.0
|
||||||
github.com/lithammer/shortuuid/v4 v4.2.0
|
github.com/lithammer/shortuuid/v4 v4.2.0
|
||||||
github.com/mneis/go-telnet v0.0.0-20221017141824-6f643e477c62
|
github.com/mneis/go-telnet v0.0.0-20221017141824-6f643e477c62
|
||||||
|
github.com/prometheus/client_golang v1.22.0
|
||||||
github.com/rqlite/gorqlite v0.0.0-20250128004930-114c7828b55a
|
github.com/rqlite/gorqlite v0.0.0-20250128004930-114c7828b55a
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
@@ -36,7 +37,9 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 // indirect
|
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 // indirect
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
@@ -45,9 +48,13 @@ require (
|
|||||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
|
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/olekukonko/tablewriter v1.0.6 // indirect
|
github.com/olekukonko/tablewriter v1.0.6 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/reiver/go-oi v1.0.0 // indirect
|
github.com/reiver/go-oi v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
@@ -58,6 +65,7 @@ require (
|
|||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||||
golang.org/x/image v0.27.0 // indirect
|
golang.org/x/image v0.27.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
modernc.org/libc v1.65.8 // indirect
|
modernc.org/libc v1.65.8 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.11.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
|
20
go.sum
20
go.sum
@@ -8,8 +8,12 @@ github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhP
|
|||||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||||
github.com/axllent/semver v0.0.1 h1:QqF+KSGxgj8QZzSXAvKFqjGWE5792ksOnQhludToK8E=
|
github.com/axllent/semver v0.0.1 h1:QqF+KSGxgj8QZzSXAvKFqjGWE5792ksOnQhludToK8E=
|
||||||
github.com/axllent/semver v0.0.1/go.mod h1:2xSPzvG8n9mRfdtxSvWvfTfQGWfHsMsHO1iZnKATMSc=
|
github.com/axllent/semver v0.0.1/go.mod h1:2xSPzvG8n9mRfdtxSvWvfTfQGWfHsMsHO1iZnKATMSc=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -25,6 +29,8 @@ github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW
|
|||||||
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b h1:EY/KpStFl60qA17CptGXhwfZ+k1sFNJIUNR8DdbcuUk=
|
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b h1:EY/KpStFl60qA17CptGXhwfZ+k1sFNJIUNR8DdbcuUk=
|
||||||
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
@@ -45,6 +51,8 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt
|
|||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/kovidgoyal/imaging v1.6.4 h1:K0idhRPXnRrJBKnBYcTfI1HTWSNDeAn7hYDvf9I0dCk=
|
github.com/kovidgoyal/imaging v1.6.4 h1:K0idhRPXnRrJBKnBYcTfI1HTWSNDeAn7hYDvf9I0dCk=
|
||||||
github.com/kovidgoyal/imaging v1.6.4/go.mod h1:bEIgsaZmXlvFfkv/CUxr9rJook6AQkJnpB5EPosRfRY=
|
github.com/kovidgoyal/imaging v1.6.4/go.mod h1:bEIgsaZmXlvFfkv/CUxr9rJook6AQkJnpB5EPosRfRY=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/leporo/sqlf v1.4.0 h1:SyWnX/8GSGOzVmanG0Ub1c04mR9nNl6Tq3IeFKX2/4c=
|
github.com/leporo/sqlf v1.4.0 h1:SyWnX/8GSGOzVmanG0Ub1c04mR9nNl6Tq3IeFKX2/4c=
|
||||||
github.com/leporo/sqlf v1.4.0/go.mod h1:pgN9yKsAnQ+2ewhbZogr98RcasUjPsHF3oXwPPhHvBw=
|
github.com/leporo/sqlf v1.4.0/go.mod h1:pgN9yKsAnQ+2ewhbZogr98RcasUjPsHF3oXwPPhHvBw=
|
||||||
github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c=
|
github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c=
|
||||||
@@ -59,6 +67,8 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp
|
|||||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/mneis/go-telnet v0.0.0-20221017141824-6f643e477c62 h1:XMG5DklHoioVYysfYglOB7vRBg/LOUJZy2mq2QyedLg=
|
github.com/mneis/go-telnet v0.0.0-20221017141824-6f643e477c62 h1:XMG5DklHoioVYysfYglOB7vRBg/LOUJZy2mq2QyedLg=
|
||||||
github.com/mneis/go-telnet v0.0.0-20221017141824-6f643e477c62/go.mod h1:niAM5cni0I/47IFA995xQfeK58Mkbb7FHJjacY4OGQg=
|
github.com/mneis/go-telnet v0.0.0-20221017141824-6f643e477c62/go.mod h1:niAM5cni0I/47IFA995xQfeK58Mkbb7FHJjacY4OGQg=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
@@ -67,6 +77,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||||
|
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||||
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||||
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
|
github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
|
||||||
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
|
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
@@ -190,6 +208,8 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
|
|||||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
188
internal/prometheus/metrics.go
Normal file
188
internal/prometheus/metrics.go
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// Package prometheus provides Prometheus metrics for Mailpit
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/axllent/mailpit/config"
|
||||||
|
"github.com/axllent/mailpit/internal/logger"
|
||||||
|
"github.com/axllent/mailpit/internal/stats"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Registry is the Prometheus registry for Mailpit metrics
|
||||||
|
Registry = prometheus.NewRegistry()
|
||||||
|
|
||||||
|
// Metrics
|
||||||
|
totalMessages prometheus.Gauge
|
||||||
|
unreadMessages prometheus.Gauge
|
||||||
|
databaseSize prometheus.Gauge
|
||||||
|
messagesDeleted prometheus.Counter
|
||||||
|
smtpAccepted prometheus.Counter
|
||||||
|
smtpRejected prometheus.Counter
|
||||||
|
smtpIgnored prometheus.Counter
|
||||||
|
smtpAcceptedSize prometheus.Counter
|
||||||
|
uptime prometheus.Gauge
|
||||||
|
memoryUsage prometheus.Gauge
|
||||||
|
tagCounters *prometheus.GaugeVec
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitMetrics initializes all Prometheus metrics
|
||||||
|
func InitMetrics() {
|
||||||
|
// Create metrics
|
||||||
|
totalMessages = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "mailpit_messages",
|
||||||
|
Help: "Total number of messages in the database",
|
||||||
|
})
|
||||||
|
|
||||||
|
unreadMessages = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "mailpit_messages_unread",
|
||||||
|
Help: "Number of unread messages in the database",
|
||||||
|
})
|
||||||
|
|
||||||
|
databaseSize = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "mailpit_database_size_bytes",
|
||||||
|
Help: "Size of the database in bytes",
|
||||||
|
})
|
||||||
|
|
||||||
|
messagesDeleted = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "mailpit_messages_deleted_total",
|
||||||
|
Help: "Total number of messages deleted",
|
||||||
|
})
|
||||||
|
|
||||||
|
smtpAccepted = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "mailpit_smtp_accepted_total",
|
||||||
|
Help: "Total number of SMTP messages accepted",
|
||||||
|
})
|
||||||
|
|
||||||
|
smtpRejected = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "mailpit_smtp_rejected_total",
|
||||||
|
Help: "Total number of SMTP messages rejected",
|
||||||
|
})
|
||||||
|
|
||||||
|
smtpIgnored = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "mailpit_smtp_ignored_total",
|
||||||
|
Help: "Total number of SMTP messages ignored (duplicates)",
|
||||||
|
})
|
||||||
|
|
||||||
|
smtpAcceptedSize = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "mailpit_smtp_accepted_size_bytes_total",
|
||||||
|
Help: "Total size of accepted SMTP messages in bytes",
|
||||||
|
})
|
||||||
|
|
||||||
|
uptime = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "mailpit_uptime_seconds",
|
||||||
|
Help: "Uptime of Mailpit in seconds",
|
||||||
|
})
|
||||||
|
|
||||||
|
memoryUsage = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "mailpit_memory_usage_bytes",
|
||||||
|
Help: "Memory usage in bytes",
|
||||||
|
})
|
||||||
|
|
||||||
|
tagCounters = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "mailpit_tag_messages",
|
||||||
|
Help: "Number of messages per tag",
|
||||||
|
},
|
||||||
|
[]string{"tag"},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register metrics
|
||||||
|
Registry.MustRegister(totalMessages)
|
||||||
|
Registry.MustRegister(unreadMessages)
|
||||||
|
Registry.MustRegister(databaseSize)
|
||||||
|
Registry.MustRegister(messagesDeleted)
|
||||||
|
Registry.MustRegister(smtpAccepted)
|
||||||
|
Registry.MustRegister(smtpRejected)
|
||||||
|
Registry.MustRegister(smtpIgnored)
|
||||||
|
Registry.MustRegister(smtpAcceptedSize)
|
||||||
|
Registry.MustRegister(uptime)
|
||||||
|
Registry.MustRegister(memoryUsage)
|
||||||
|
Registry.MustRegister(tagCounters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMetrics updates all metrics with current values
|
||||||
|
func UpdateMetrics() {
|
||||||
|
info := stats.Load()
|
||||||
|
|
||||||
|
totalMessages.Set(float64(info.Messages))
|
||||||
|
unreadMessages.Set(float64(info.Unread))
|
||||||
|
databaseSize.Set(float64(info.DatabaseSize))
|
||||||
|
messagesDeleted.Add(float64(info.RuntimeStats.MessagesDeleted))
|
||||||
|
smtpAccepted.Add(float64(info.RuntimeStats.SMTPAccepted))
|
||||||
|
smtpRejected.Add(float64(info.RuntimeStats.SMTPRejected))
|
||||||
|
smtpIgnored.Add(float64(info.RuntimeStats.SMTPIgnored))
|
||||||
|
smtpAcceptedSize.Add(float64(info.RuntimeStats.SMTPAcceptedSize))
|
||||||
|
uptime.Set(float64(info.RuntimeStats.Uptime))
|
||||||
|
memoryUsage.Set(float64(info.RuntimeStats.Memory))
|
||||||
|
|
||||||
|
// Reset tag counters
|
||||||
|
tagCounters.Reset()
|
||||||
|
|
||||||
|
// Update tag counters
|
||||||
|
for tag, count := range info.Tags {
|
||||||
|
tagCounters.WithLabelValues(tag).Set(float64(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the Prometheus handler & disables double compression in middleware
|
||||||
|
func GetHandler() http.Handler {
|
||||||
|
return promhttp.HandlerFor(Registry, promhttp.HandlerOpts{
|
||||||
|
DisableCompression: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartUpdater starts the periodic metrics update routine
|
||||||
|
func StartUpdater() {
|
||||||
|
InitMetrics()
|
||||||
|
UpdateMetrics()
|
||||||
|
|
||||||
|
// Start periodic updates
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(15 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for range ticker.C {
|
||||||
|
UpdateMetrics()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartSeparateServer starts a separate HTTP server for Prometheus metrics
|
||||||
|
func StartSeparateServer() {
|
||||||
|
StartUpdater()
|
||||||
|
|
||||||
|
logger.Log().Infof("[prometheus] metrics server listening on %s", config.PrometheusListen)
|
||||||
|
|
||||||
|
// Create a dedicated mux for the metrics server
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/metrics", promhttp.HandlerFor(Registry, promhttp.HandlerOpts{}))
|
||||||
|
|
||||||
|
// Create a dedicated server instance
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: config.PrometheusListen,
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start HTTP server
|
||||||
|
if err := server.ListenAndServe(); err != nil {
|
||||||
|
logger.Log().Errorf("[prometheus] metrics server error: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMode returns the Prometheus run mode
|
||||||
|
func GetMode() string {
|
||||||
|
mode := strings.ToLower(strings.TrimSpace(config.PrometheusListen))
|
||||||
|
if mode == "false" {
|
||||||
|
return "disabled"
|
||||||
|
}
|
||||||
|
if mode == "true" {
|
||||||
|
return "integrated"
|
||||||
|
}
|
||||||
|
return "separate"
|
||||||
|
}
|
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/axllent/mailpit/internal/auth"
|
"github.com/axllent/mailpit/internal/auth"
|
||||||
"github.com/axllent/mailpit/internal/logger"
|
"github.com/axllent/mailpit/internal/logger"
|
||||||
"github.com/axllent/mailpit/internal/pop3"
|
"github.com/axllent/mailpit/internal/pop3"
|
||||||
|
"github.com/axllent/mailpit/internal/prometheus"
|
||||||
"github.com/axllent/mailpit/internal/stats"
|
"github.com/axllent/mailpit/internal/stats"
|
||||||
"github.com/axllent/mailpit/internal/storage"
|
"github.com/axllent/mailpit/internal/storage"
|
||||||
"github.com/axllent/mailpit/internal/tools"
|
"github.com/axllent/mailpit/internal/tools"
|
||||||
@@ -182,6 +183,13 @@ func apiRoutes() *mux.Router {
|
|||||||
r.HandleFunc(config.Webroot+"api/v1/chaos", middleWareFunc(apiv1.GetChaos)).Methods("GET")
|
r.HandleFunc(config.Webroot+"api/v1/chaos", middleWareFunc(apiv1.GetChaos)).Methods("GET")
|
||||||
r.HandleFunc(config.Webroot+"api/v1/chaos", middleWareFunc(apiv1.SetChaos)).Methods("PUT")
|
r.HandleFunc(config.Webroot+"api/v1/chaos", middleWareFunc(apiv1.SetChaos)).Methods("PUT")
|
||||||
|
|
||||||
|
// Prometheus metrics (if enabled and using existing server)
|
||||||
|
if prometheus.GetMode() == "integrated" {
|
||||||
|
r.HandleFunc(config.Webroot+"metrics", middleWareFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
prometheus.GetHandler().ServeHTTP(w, r)
|
||||||
|
})).Methods("GET")
|
||||||
|
}
|
||||||
|
|
||||||
// web UI websocket
|
// web UI websocket
|
||||||
r.HandleFunc(config.Webroot+"api/events", apiWebsocket).Methods("GET")
|
r.HandleFunc(config.Webroot+"api/events", apiWebsocket).Methods("GET")
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user