2023-12-05 23:13:57 +03:00
package main
import (
"context"
"errors"
"fmt"
2024-02-08 16:49:01 +03:00
"log/slog"
2023-12-05 23:13:57 +03:00
"os"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/mjl-/mox/dane"
"github.com/mjl-/mox/dkim"
"github.com/mjl-/mox/dmarc"
"github.com/mjl-/mox/dns"
"github.com/mjl-/mox/dnsbl"
"github.com/mjl-/mox/iprev"
"github.com/mjl-/mox/metrics"
"github.com/mjl-/mox/mlog"
"github.com/mjl-/mox/mtasts"
"github.com/mjl-/mox/smtpclient"
"github.com/mjl-/mox/spf"
"github.com/mjl-/mox/subjectpass"
"github.com/mjl-/mox/tlsrpt"
"github.com/mjl-/mox/updates"
)
var metricHTTPClient = promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_httpclient_request_duration_seconds" ,
Help : "HTTP requests lookups." ,
Buckets : [ ] float64 { 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 , 30 } ,
} ,
[ ] string {
"pkg" ,
"method" ,
"code" ,
"result" ,
} ,
)
// httpClientObserve tracks the result of an HTTP transaction in a metric, and
// logs the result.
func httpClientObserve ( ctx context . Context , elog * slog . Logger , pkg , method string , statusCode int , err error , start time . Time ) {
log := mlog . New ( "metrics" , elog )
var result string
switch {
case err == nil :
switch statusCode / 100 {
case 2 :
result = "ok"
case 4 :
result = "usererror"
case 5 :
result = "servererror"
default :
result = "other"
}
case errors . Is ( err , os . ErrDeadlineExceeded ) || errors . Is ( err , context . DeadlineExceeded ) :
result = "timeout"
case errors . Is ( err , context . Canceled ) :
result = "canceled"
default :
result = "error"
}
metricHTTPClient . WithLabelValues ( pkg , method , result , fmt . Sprintf ( "%d" , statusCode ) ) . Observe ( float64 ( time . Since ( start ) ) / float64 ( time . Second ) )
log . Debugx ( "httpclient result" , err ,
slog . String ( "pkg" , pkg ) ,
slog . String ( "method" , method ) ,
slog . Int ( "code" , statusCode ) ,
slog . Duration ( "duration" , time . Since ( start ) ) )
}
func init ( ) {
dane . MetricVerify = promauto . NewCounter (
prometheus . CounterOpts {
Name : "mox_dane_verify_total" ,
Help : "Total number of DANE verification attempts, including mox_dane_verify_errors_total." ,
} ,
)
dane . MetricVerifyErrors = promauto . NewCounter (
prometheus . CounterOpts {
Name : "mox_dane_verify_errors_total" ,
Help : "Total number of DANE verification failures, causing connections to fail." ,
} ,
)
dkim . MetricSign = counterVec { promauto . NewCounterVec (
prometheus . CounterOpts {
Name : "mox_dkim_sign_total" ,
Help : "DKIM messages signings, label key is the type of key, rsa or ed25519." ,
} ,
[ ] string {
"key" ,
} ,
) }
dkim . MetricVerify = histogramVec {
promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_dkim_verify_duration_seconds" ,
Help : "DKIM verify, including lookup, duration and result." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 } ,
} ,
[ ] string {
"algorithm" ,
"status" ,
} ,
) ,
}
dmarc . MetricVerify = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_dmarc_verify_duration_seconds" ,
Help : "DMARC verify, including lookup, duration and result." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 } ,
} ,
[ ] string {
"status" ,
"reject" , // yes/no
"use" , // yes/no, if policy is used after random selection
} ,
) }
dns . MetricLookup = histogramVec {
promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_dns_lookup_duration_seconds" ,
Help : "DNS lookups." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 , 30 } ,
} ,
[ ] string {
"pkg" ,
"type" , // Lower-case Resolver method name without leading Lookup.
"result" , // ok, nxdomain, temporary, timeout, canceled, error
} ,
) ,
}
dnsbl . MetricLookup = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_dnsbl_lookup_duration_seconds" ,
Help : "DNSBL lookup" ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 } ,
} ,
[ ] string {
"zone" ,
"status" ,
} ,
) }
iprev . MetricIPRev = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_iprev_lookup_total" ,
Help : "Number of iprev lookups." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 , 30 } ,
} ,
[ ] string { "status" } ,
) }
mtasts . MetricGet = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_mtasts_get_duration_seconds" ,
Help : "MTA-STS get of policy, including lookup, duration and result." ,
Buckets : [ ] float64 { 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 } ,
} ,
[ ] string {
"result" , // ok, lookuperror, fetcherror
} ,
) }
mtasts . HTTPClientObserve = httpClientObserve
smtpclient . MetricCommands = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_smtpclient_command_duration_seconds" ,
Help : "SMTP client command duration and result codes in seconds." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 , 30 , 60 , 120 } ,
} ,
[ ] string {
"cmd" ,
"code" ,
"secode" ,
} ,
) }
smtpclient . MetricTLSRequiredNoIgnored = counterVec { promauto . NewCounterVec (
prometheus . CounterOpts {
Name : "mox_smtpclient_tlsrequiredno_ignored_total" ,
Help : "Connection attempts with TLS policy findings ignored due to message with TLS-Required: No header. Does not cover case where TLS certificate cannot be PKIX-verified." ,
} ,
[ ] string {
"ignored" , // daneverification (no matching tlsa record)
} ,
) }
smtpclient . MetricPanicInc = func ( ) {
metrics . PanicInc ( metrics . Smtpclient )
}
spf . MetricVerify = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_spf_verify_duration_seconds" ,
Help : "SPF verify, including lookup, duration and result." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 } ,
} ,
[ ] string {
"status" ,
} ,
) }
subjectpass . MetricGenerate = promauto . NewCounter (
prometheus . CounterOpts {
Name : "mox_subjectpass_generate_total" ,
Help : "Number of generated subjectpass challenges." ,
} ,
)
subjectpass . MetricVerify = counterVec { promauto . NewCounterVec (
prometheus . CounterOpts {
Name : "mox_subjectpass_verify_total" ,
Help : "Number of subjectpass verifications." ,
} ,
[ ] string {
"result" , // ok, fail
} ,
) }
tlsrpt . MetricLookup = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_tlsrpt_lookup_duration_seconds" ,
Help : "TLSRPT lookups with result." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 , 30 } ,
} ,
[ ] string { "result" } ,
) }
updates . MetricLookup = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_updates_lookup_duration_seconds" ,
Help : "Updates lookup with result." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 , 30 } ,
} ,
[ ] string { "result" } ,
) }
updates . MetricFetchChangelog = histogramVec { promauto . NewHistogramVec (
prometheus . HistogramOpts {
Name : "mox_updates_fetchchangelog_duration_seconds" ,
Help : "Fetch changelog with result." ,
Buckets : [ ] float64 { 0.001 , 0.005 , 0.01 , 0.05 , 0.100 , 0.5 , 1 , 5 , 10 , 20 , 30 } ,
} ,
[ ] string { "result" } ,
) }
}
type counterVec struct {
* prometheus . CounterVec
}
func ( m counterVec ) IncLabels ( labels ... string ) {
m . CounterVec . WithLabelValues ( labels ... ) . Inc ( )
}
type histogramVec struct {
* prometheus . HistogramVec
}
func ( m histogramVec ) ObserveLabels ( v float64 , labels ... string ) {
m . HistogramVec . WithLabelValues ( labels ... ) . Observe ( v )
}