Skip to content

Commit

Permalink
Merge pull request #9 from Hsn723/dsn-metrics
Browse files Browse the repository at this point in the history
add metrics for enhanced status codes for deferred and bounced smtp mail
  • Loading branch information
Hsn723 authored Jan 29, 2024
2 parents 1bdefd5 + 4c867be commit 5577249
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 0 deletions.
29 changes: 29 additions & 0 deletions postfix_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ type PostfixExporter struct {
smtpTLSConnects *prometheus.CounterVec
smtpConnectionTimedOut prometheus.Counter
smtpProcesses *prometheus.CounterVec
smtpDeferredDSN *prometheus.CounterVec
smtpBouncedDSN *prometheus.CounterVec
// should be the same as smtpProcesses{status=deferred}, kept for compatibility, but this doesn't work !
smtpDeferreds prometheus.Counter
smtpdConnects prometheus.Counter
Expand Down Expand Up @@ -297,6 +299,7 @@ var (
qmgrInsertLine = regexp.MustCompile(`:.*, size=(\d+), nrcpt=(\d+) `)
qmgrExpiredLine = regexp.MustCompile(`:.*, status=(expired|force-expired), returned to sender`)
smtpStatusLine = regexp.MustCompile(`, status=(\w+) `)
smtpDSNLine = regexp.MustCompile(`, dsn=(\d\.\d+\.\d+)`)
smtpTLSLine = regexp.MustCompile(`^(\S+) TLS connection established to \S+: (\S+) with cipher (\S+) \((\d+)/(\d+) bits\)`)
smtpConnectionTimedOut = regexp.MustCompile(`^connect\s+to\s+(.*)\[(.*)\]:(\d+):\s+(Connection timed out)$`)
smtpdFCrDNSErrorsLine = regexp.MustCompile(`^warning: hostname \S+ does not resolve to address `)
Expand Down Expand Up @@ -372,8 +375,16 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
addToHistogramVec(e.smtpDelays, smtpMatches[5], "transmission", "")
if smtpStatusMatches := smtpStatusLine.FindStringSubmatch(remainder); smtpStatusMatches != nil {
e.smtpProcesses.WithLabelValues(smtpStatusMatches[1]).Inc()
dsnMatches := smtpDSNLine.FindStringSubmatch(remainder)
if smtpStatusMatches[1] == "deferred" {
e.smtpStatusDeferred.Inc()
if dsnMatches != nil {
e.smtpDeferredDSN.WithLabelValues(dsnMatches[1]).Inc()
}
} else if smtpStatusMatches[1] == "bounced" {
if dsnMatches != nil {
e.smtpBouncedDSN.WithLabelValues(dsnMatches[1]).Inc()
}
}
}
} else if smtpTLSMatches := smtpTLSLine.FindStringSubmatch(remainder); smtpTLSMatches != nil {
Expand Down Expand Up @@ -542,6 +553,20 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
Help: "Total number of messages that have been processed by the smtp process.",
},
[]string{"status"}),
smtpDeferredDSN: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: "postfix",
Name: "smtp_deferred_messages_by_dsn_total",
Help: "Total number of messages that have been deferred on SMTP by DSN.",
},
[]string{"dsn"}),
smtpBouncedDSN: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: "postfix",
Name: "smtp_bounced_messages_by_dsn_total",
Help: "Total number of messages that have been bounced on SMTP by DSN.",
},
[]string{"dsn"}),
smtpConnectionTimedOut: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "postfix",
Name: "smtp_connection_timed_out_total",
Expand Down Expand Up @@ -648,6 +673,8 @@ func (e *PostfixExporter) Describe(ch chan<- *prometheus.Desc) {
e.smtpTLSConnects.Describe(ch)
ch <- e.smtpDeferreds.Desc()
e.smtpProcesses.Describe(ch)
e.smtpDeferredDSN.Describe(ch)
e.smtpBouncedDSN.Describe(ch)
ch <- e.smtpdConnects.Desc()
ch <- e.smtpdDisconnects.Desc()
ch <- e.smtpdFCrDNSErrors.Desc()
Expand Down Expand Up @@ -732,6 +759,8 @@ func (e *PostfixExporter) Collect(ch chan<- prometheus.Metric) {
ch <- e.smtpdFCrDNSErrors
e.smtpdLostConnections.Collect(ch)
e.smtpdProcesses.Collect(ch)
e.smtpDeferredDSN.Collect(ch)
e.smtpBouncedDSN.Collect(ch)
e.smtpdRejects.Collect(ch)
ch <- e.smtpdSASLAuthenticationFailures
e.smtpdTLSConnects.Collect(ch)
Expand Down
12 changes: 12 additions & 0 deletions postfix_exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
smtpDeferreds prometheus.Counter
smtpStatusDeferred prometheus.Counter
smtpProcesses *prometheus.CounterVec
smtpDeferredDSN *prometheus.CounterVec
smtpBouncedDSN *prometheus.CounterVec
smtpdConnects prometheus.Counter
smtpdDisconnects prometheus.Counter
smtpdFCrDNSErrors prometheus.Counter
Expand All @@ -47,6 +49,8 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
outgoingTLS int
smtpdMessagesProcessed int
smtpMessagesProcessed int
smtpDeferred int
smtpBounced int
bounceNonDelivery int
virtualDelivered int
unsupportedLogEntries []string
Expand Down Expand Up @@ -206,13 +210,17 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
"Dec 29 03:03:48 mail postfix/bounce[9321]: 732BB407C3: sender non-delivery notification: 5DE184083C",
},
smtpMessagesProcessed: 2,
smtpDeferred: 1,
smtpBounced: 1,
bounceNonDelivery: 1,
},
fields: fields{
unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"service", "level"}),
smtpDelays: prometheus.NewHistogramVec(prometheus.HistogramOpts{}, []string{"stage"}),
smtpStatusDeferred: prometheus.NewCounter(prometheus.CounterOpts{}),
smtpProcesses: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"status"}),
smtpDeferredDSN: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"dsn"}),
smtpBouncedDSN: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"dsn"}),
bounceNonDelivery: prometheus.NewCounter(prometheus.CounterOpts{}),
},
},
Expand Down Expand Up @@ -267,6 +275,8 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
smtpDeferreds: tt.fields.smtpDeferreds,
smtpStatusDeferred: tt.fields.smtpStatusDeferred,
smtpProcesses: tt.fields.smtpProcesses,
smtpDeferredDSN: tt.fields.smtpDeferredDSN,
smtpBouncedDSN: tt.fields.smtpBouncedDSN,
smtpdConnects: tt.fields.smtpdConnects,
smtpdDisconnects: tt.fields.smtpdDisconnects,
smtpdFCrDNSErrors: tt.fields.smtpdFCrDNSErrors,
Expand All @@ -289,6 +299,8 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
assertCounterEquals(t, e.smtpTLSConnects, tt.args.outgoingTLS, "Wrong number of TLS connections counted")
assertCounterEquals(t, e.smtpdProcesses, tt.args.smtpdMessagesProcessed, "Wrong number of smtpd messages processed")
assertCounterEquals(t, e.smtpProcesses, tt.args.smtpMessagesProcessed, "Wrong number of smtp messages processed")
assertCounterEquals(t, e.smtpDeferredDSN, tt.args.smtpDeferred, "Wrong number of smtp deferred")
assertCounterEquals(t, e.smtpBouncedDSN, tt.args.smtpBounced, "Wrong number of smtp bounced")
assertCounterEquals(t, e.bounceNonDelivery, tt.args.bounceNonDelivery, "Wrong number of non delivery notifications")
assertCounterEquals(t, e.virtualDelivered, tt.args.virtualDelivered, "Wrong number of delivered mails")
assertVecMetricsEquals(t, e.unsupportedLogEntries, tt.args.unsupportedLogEntries, "Wrong number of unsupportedLogEntries")
Expand Down

0 comments on commit 5577249

Please sign in to comment.