New Upstream Snapshot - prometheus-postfix-exporter

Ready changes

Summary

Merged new upstream version: 0.3.0+git20221013.1.a6f58e9 (was: 0.3.0).

Diff

diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 7dd92f4..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-# Editor files
-*~
-.idea/
-
-# Test binary, build with `go test -c`
-*.test
-
-# Binaries
-postfix_exporter
-
-*.iml
-vendor/
diff --git a/debian/changelog b/debian/changelog
index f91c00f..3b80adf 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+prometheus-postfix-exporter (0.3.0+git20221013.1.a6f58e9-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 10 Feb 2023 12:32:39 -0000
+
 prometheus-postfix-exporter (0.3.0-1) unstable; urgency=medium
 
   [ Aloïs Micard ]
diff --git a/debian/patches/01-Defaults.patch b/debian/patches/01-Defaults.patch
index 1b4ba1c..2058070 100644
--- a/debian/patches/01-Defaults.patch
+++ b/debian/patches/01-Defaults.patch
@@ -1,8 +1,10 @@
 Description: Adjust default parameters for Debian.
 Author: Daniel Swarbrick <dswarbrick@debian.org>
 Forwarded: not-needed
---- a/logsource_file.go
-+++ b/logsource_file.go
+Index: prometheus-postfix-exporter.git/logsource_file.go
+===================================================================
+--- prometheus-postfix-exporter.git.orig/logsource_file.go
++++ prometheus-postfix-exporter.git/logsource_file.go
 @@ -66,7 +66,7 @@ type fileLogSourceFactory struct {
  }
  
@@ -12,8 +14,10 @@ Forwarded: not-needed
  }
  
  func (f *fileLogSourceFactory) New(ctx context.Context) (LogSourceCloser, error) {
---- a/logsource_systemd.go
-+++ b/logsource_systemd.go
+Index: prometheus-postfix-exporter.git/logsource_systemd.go
+===================================================================
+--- prometheus-postfix-exporter.git.orig/logsource_systemd.go
++++ prometheus-postfix-exporter.git/logsource_systemd.go
 @@ -107,7 +107,7 @@ type systemdLogSourceFactory struct {
  
  func (f *systemdLogSourceFactory) Init(app *kingpin.Application) {
diff --git a/postfix_exporter.go b/postfix_exporter.go
index 226542d..56691cc 100644
--- a/postfix_exporter.go
+++ b/postfix_exporter.go
@@ -53,9 +53,12 @@ type PostfixExporter struct {
 	qmgrInsertsNrcpt                prometheus.Histogram
 	qmgrInsertsSize                 prometheus.Histogram
 	qmgrRemoves                     prometheus.Counter
+	qmgrExpires                     prometheus.Counter
 	smtpDelays                      *prometheus.HistogramVec
 	smtpTLSConnects                 *prometheus.CounterVec
 	smtpConnectionTimedOut          prometheus.Counter
+	smtpProcesses                    *prometheus.CounterVec
+	// should be the same as smtpProcesses{status=deferred}, kept for compatibility, but this doesn't work !
 	smtpDeferreds                   prometheus.Counter
 	smtpdConnects                   prometheus.Counter
 	smtpdDisconnects                prometheus.Counter
@@ -66,8 +69,11 @@ type PostfixExporter struct {
 	smtpdSASLAuthenticationFailures prometheus.Counter
 	smtpdTLSConnects                *prometheus.CounterVec
 	unsupportedLogEntries           *prometheus.CounterVec
+	// same as smtpProcesses{status=deferred}, kept for compatibility
 	smtpStatusDeferred              prometheus.Counter
 	opendkimSignatureAdded          *prometheus.CounterVec
+	bounceNonDelivery               prometheus.Counter
+	virtualDelivered                prometheus.Counter
 }
 
 // A LogSource is an interface to read log lines.
@@ -286,10 +292,11 @@ func CollectShowqFromSocket(path string, ch chan<- prometheus.Metric) error {
 
 // Patterns for parsing log messages.
 var (
-	logLine                             = regexp.MustCompile(` ?(postfix|opendkim)(/(\w+))?\[\d+\]: (.*)`)
+	logLine                             = regexp.MustCompile(` ?(postfix|opendkim)(/(\w+))?\[\d+\]: ((?:(warning|error|fatal|panic): )?.*)`)
 	lmtpPipeSMTPLine                    = regexp.MustCompile(`, relay=(\S+), .*, delays=([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+), `)
 	qmgrInsertLine                      = regexp.MustCompile(`:.*, size=(\d+), nrcpt=(\d+) `)
-	smtpStatusDeferredLine              = regexp.MustCompile(`, status=deferred`)
+	qmgrExpiredLine                     = regexp.MustCompile(`:.*, status=(expired|force-expired), returned to sender`)
+	smtpStatusLine                      = regexp.MustCompile(`, status=(\w+) `)
 	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 `)
@@ -299,6 +306,7 @@ var (
 	smtpdSASLAuthenticationFailuresLine = regexp.MustCompile(`^warning: \S+: SASL \S+ authentication failed: `)
 	smtpdTLSLine                        = regexp.MustCompile(`^(\S+) TLS connection established from \S+: (\S+) with cipher (\S+) \((\d+)/(\d+) bits\)`)
 	opendkimSignatureAdded              = regexp.MustCompile(`^[\w\d]+: DKIM-Signature field added \(s=(\w+), d=(.*)\)$`)
+	bounceNonDeliveryLine               = regexp.MustCompile(`: sender non-delivery notification: `)
 )
 
 // CollectFromLogline collects metrict from a Postfix log line.
@@ -308,10 +316,11 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
 
 	if logMatches == nil {
 		// Unknown log entry format.
-		e.addToUnsupportedLine(line, "")
+		e.addToUnsupportedLine(line, "", "")
 		return
 	}
 	process := logMatches[1]
+	level := logMatches[5]
 	remainder := logMatches[4]
 	switch process {
 	case "postfix":
@@ -324,7 +333,7 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
 			} else if strings.Contains(remainder, ": reject: ") {
 				e.cleanupRejects.Inc()
 			} else {
-				e.addToUnsupportedLine(line, subprocess)
+				e.addToUnsupportedLine(line, subprocess, level)
 			}
 		case "lmtp":
 			if lmtpMatches := lmtpPipeSMTPLine.FindStringSubmatch(remainder); lmtpMatches != nil {
@@ -333,7 +342,7 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
 				addToHistogramVec(e.lmtpDelays, lmtpMatches[4], "LMTP sdelay", "connection_setup")
 				addToHistogramVec(e.lmtpDelays, lmtpMatches[5], "LMTP xdelay", "transmission")
 			} else {
-				e.addToUnsupportedLine(line, subprocess)
+				e.addToUnsupportedLine(line, subprocess, level)
 			}
 		case "pipe":
 			if pipeMatches := lmtpPipeSMTPLine.FindStringSubmatch(remainder); pipeMatches != nil {
@@ -342,7 +351,7 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
 				addToHistogramVec(e.pipeDelays, pipeMatches[4], "PIPE sdelay", pipeMatches[1], "connection_setup")
 				addToHistogramVec(e.pipeDelays, pipeMatches[5], "PIPE xdelay", pipeMatches[1], "transmission")
 			} else {
-				e.addToUnsupportedLine(line, subprocess)
+				e.addToUnsupportedLine(line, subprocess, level)
 			}
 		case "qmgr":
 			if qmgrInsertMatches := qmgrInsertLine.FindStringSubmatch(remainder); qmgrInsertMatches != nil {
@@ -350,8 +359,10 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
 				addToHistogram(e.qmgrInsertsNrcpt, qmgrInsertMatches[2], "QMGR nrcpt")
 			} else if strings.HasSuffix(remainder, ": removed") {
 				e.qmgrRemoves.Inc()
+			} else if qmgrExpired := qmgrExpiredLine.FindStringSubmatch(remainder); qmgrExpired != nil {
+				e.qmgrExpires.Inc()
 			} else {
-				e.addToUnsupportedLine(line, subprocess)
+				e.addToUnsupportedLine(line, subprocess, level)
 			}
 		case "smtp":
 			if smtpMatches := lmtpPipeSMTPLine.FindStringSubmatch(remainder); smtpMatches != nil {
@@ -359,15 +370,18 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
 				addToHistogramVec(e.smtpDelays, smtpMatches[3], "queue_manager", "")
 				addToHistogramVec(e.smtpDelays, smtpMatches[4], "connection_setup", "")
 				addToHistogramVec(e.smtpDelays, smtpMatches[5], "transmission", "")
-				if smtpMatches := smtpStatusDeferredLine.FindStringSubmatch(remainder); smtpMatches != nil {
-					e.smtpStatusDeferred.Inc()
+				if smtpStatusMatches := smtpStatusLine.FindStringSubmatch(remainder); smtpStatusMatches != nil {
+					e.smtpProcesses.WithLabelValues(smtpStatusMatches[1]).Inc()
+					if smtpStatusMatches[1] == "deferred" {
+						e.smtpStatusDeferred.Inc()
+					}
 				}
 			} else if smtpTLSMatches := smtpTLSLine.FindStringSubmatch(remainder); smtpTLSMatches != nil {
 				e.smtpTLSConnects.WithLabelValues(smtpTLSMatches[1:]...).Inc()
 			} else if smtpMatches := smtpConnectionTimedOut.FindStringSubmatch(remainder); smtpMatches != nil {
 				e.smtpConnectionTimedOut.Inc()
 			} else {
-				e.addToUnsupportedLine(line, subprocess)
+				e.addToUnsupportedLine(line, subprocess, level)
 			}
 		case "smtpd":
 			if strings.HasPrefix(remainder, "connect from ") {
@@ -389,28 +403,40 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
 			} else if smtpdTLSMatches := smtpdTLSLine.FindStringSubmatch(remainder); smtpdTLSMatches != nil {
 				e.smtpdTLSConnects.WithLabelValues(smtpdTLSMatches[1:]...).Inc()
 			} else {
-				e.addToUnsupportedLine(line, subprocess)
+				e.addToUnsupportedLine(line, subprocess, level)
+			}
+		case "bounce":
+			if bounceMatches := bounceNonDeliveryLine.FindStringSubmatch(remainder); bounceMatches != nil {
+				e.bounceNonDelivery.Inc()
+			} else {
+				e.addToUnsupportedLine(line, process, level)
+			}
+		case "virtual":
+			if strings.HasSuffix(remainder, ", status=sent (delivered to maildir)") {
+				e.virtualDelivered.Inc()
+			} else {
+				e.addToUnsupportedLine(line, process, level)
 			}
 		default:
-			e.addToUnsupportedLine(line, subprocess)
+			e.addToUnsupportedLine(line, subprocess, level)
 		}
 	case "opendkim":
 		if opendkimMatches := opendkimSignatureAdded.FindStringSubmatch(remainder); opendkimMatches != nil {
 			e.opendkimSignatureAdded.WithLabelValues(opendkimMatches[1], opendkimMatches[2]).Inc()
 		} else {
-			e.addToUnsupportedLine(line, process)
+			e.addToUnsupportedLine(line, process, level)
 		}
 	default:
 		// Unknown log entry format.
-		e.addToUnsupportedLine(line, "")
+		e.addToUnsupportedLine(line, process, level)
 	}
 }
 
-func (e *PostfixExporter) addToUnsupportedLine(line string, subprocess string) {
+func (e *PostfixExporter) addToUnsupportedLine(line string, subprocess string, level string) {
 	if e.logUnsupportedLines {
 		log.Printf("Unsupported Line: %v", line)
 	}
-	e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
+	e.unsupportedLogEntries.WithLabelValues(subprocess, level).Inc()
 }
 
 func addToHistogram(h prometheus.Histogram, value, fieldName string) {
@@ -484,6 +510,11 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
 			Name:      "qmgr_messages_removed_total",
 			Help:      "Total number of messages removed from mail queues.",
 		}),
+		qmgrExpires: prometheus.NewCounter(prometheus.CounterOpts{
+			Namespace: "postfix",
+			Name:      "qmgr_messages_expired_total",
+			Help:      "Total number of messages expired from mail queues.",
+		}),
 		smtpDelays: prometheus.NewHistogramVec(
 			prometheus.HistogramOpts{
 				Namespace: "postfix",
@@ -504,6 +535,13 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
 			Name:      "smtp_deferred_messages_total",
 			Help:      "Total number of messages that have been deferred on SMTP.",
 		}),
+		smtpProcesses: prometheus.NewCounterVec(
+			prometheus.CounterOpts{
+				Namespace: "postfix",
+				Name:      "smtp_messages_processed_total",
+				Help:      "Total number of messages that have been processed by the smtp process.",
+			},
+			[]string{"status"}),
 		smtpConnectionTimedOut: prometheus.NewCounter(prometheus.CounterOpts{
 			Namespace: "postfix",
 			Name:      "smtp_connection_timed_out_total",
@@ -563,7 +601,7 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
 				Name:      "unsupported_log_entries_total",
 				Help:      "Log entries that could not be processed.",
 			},
-			[]string{"service"}),
+			[]string{"service", "level"}),
 		smtpStatusDeferred: prometheus.NewCounter(prometheus.CounterOpts{
 			Namespace: "postfix",
 			Name:      "smtp_status_deferred",
@@ -577,6 +615,16 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
 			},
 			[]string{"subject", "domain"},
 		),
+		bounceNonDelivery: prometheus.NewCounter(prometheus.CounterOpts{
+			Namespace: "postfix",
+			Name:      "bounce_non_delivery_notification_total",
+			Help:      "Total number of non delivery notification sent by bounce.",
+		}),
+		virtualDelivered: prometheus.NewCounter(prometheus.CounterOpts{
+			Namespace: "postfix",
+			Name:      "virtual_delivered_total",
+			Help:      "Total number of mail delivered to a virtual mailbox.",
+		}),
 	}, nil
 }
 
@@ -595,9 +643,11 @@ func (e *PostfixExporter) Describe(ch chan<- *prometheus.Desc) {
 	ch <- e.qmgrInsertsNrcpt.Desc()
 	ch <- e.qmgrInsertsSize.Desc()
 	ch <- e.qmgrRemoves.Desc()
+	ch <- e.qmgrExpires.Desc()
 	e.smtpDelays.Describe(ch)
 	e.smtpTLSConnects.Describe(ch)
 	ch <- e.smtpDeferreds.Desc()
+	e.smtpProcesses.Describe(ch)
 	ch <- e.smtpdConnects.Desc()
 	ch <- e.smtpdDisconnects.Desc()
 	ch <- e.smtpdFCrDNSErrors.Desc()
@@ -610,6 +660,8 @@ func (e *PostfixExporter) Describe(ch chan<- *prometheus.Desc) {
 	e.unsupportedLogEntries.Describe(ch)
 	e.smtpConnectionTimedOut.Describe(ch)
 	e.opendkimSignatureAdded.Describe(ch)
+	ch <- e.bounceNonDelivery.Desc()
+	ch <- e.virtualDelivered.Desc()
 }
 
 func (e *PostfixExporter) StartMetricCollection(ctx context.Context) {
@@ -670,9 +722,11 @@ func (e *PostfixExporter) Collect(ch chan<- prometheus.Metric) {
 	ch <- e.qmgrInsertsNrcpt
 	ch <- e.qmgrInsertsSize
 	ch <- e.qmgrRemoves
+	ch <- e.qmgrExpires
 	e.smtpDelays.Collect(ch)
 	e.smtpTLSConnects.Collect(ch)
 	ch <- e.smtpDeferreds
+	e.smtpProcesses.Collect(ch)
 	ch <- e.smtpdConnects
 	ch <- e.smtpdDisconnects
 	ch <- e.smtpdFCrDNSErrors
@@ -685,4 +739,6 @@ func (e *PostfixExporter) Collect(ch chan<- prometheus.Metric) {
 	e.unsupportedLogEntries.Collect(ch)
 	ch <- e.smtpConnectionTimedOut
 	e.opendkimSignatureAdded.Collect(ch)
+	ch <- e.bounceNonDelivery
+	ch <- e.virtualDelivered
 }
diff --git a/postfix_exporter_test.go b/postfix_exporter_test.go
index a3426a4..8f057a8 100644
--- a/postfix_exporter_test.go
+++ b/postfix_exporter_test.go
@@ -20,9 +20,12 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 		qmgrInsertsNrcpt                prometheus.Histogram
 		qmgrInsertsSize                 prometheus.Histogram
 		qmgrRemoves                     prometheus.Counter
+		qmgrExpires                     prometheus.Counter
 		smtpDelays                      *prometheus.HistogramVec
 		smtpTLSConnects                 *prometheus.CounterVec
 		smtpDeferreds                   prometheus.Counter
+		smtpStatusDeferred              prometheus.Counter
+		smtpProcesses                   *prometheus.CounterVec
 		smtpdConnects                   prometheus.Counter
 		smtpdDisconnects                prometheus.Counter
 		smtpdFCrDNSErrors               prometheus.Counter
@@ -31,14 +34,21 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 		smtpdRejects                    *prometheus.CounterVec
 		smtpdSASLAuthenticationFailures prometheus.Counter
 		smtpdTLSConnects                *prometheus.CounterVec
+		bounceNonDelivery               prometheus.Counter
+		virtualDelivered                prometheus.Counter
 		unsupportedLogEntries           *prometheus.CounterVec
 	}
 	type args struct {
 		line                   []string
 		removedCount           int
+		expiredCount           int
 		saslFailedCount        int
 		outgoingTLS            int
 		smtpdMessagesProcessed int
+		smtpMessagesProcessed  int
+		bounceNonDelivery  int
+		virtualDelivered       int
+		unsupportedLogEntries  []string
 	}
 	tests := []struct {
 		name   string
@@ -56,7 +66,7 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 			},
 			fields: fields{
 				qmgrRemoves:           prometheus.NewCounter(prometheus.CounterOpts{}),
-				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"process"}),
+				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"service", "level"}),
 			},
 		},
 		{
@@ -100,7 +110,20 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 			},
 			fields: fields{
 				qmgrRemoves:           prometheus.NewCounter(prometheus.CounterOpts{}),
-				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"process"}),
+				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"service", "level"}),
+			},
+		},
+		{
+			name: "qmgr expired",
+			args: args{
+				line: []string{
+					"Apr 10 14:50:16 mail postfix/qmgr[3663]: BACE842E72: from=<noreply@domain.com>, status=expired, returned to sender",
+					"Apr 10 14:50:16 mail postfix/qmgr[3663]: BACE842E73: from=<noreply@domain.com>, status=force-expired, returned to sender",
+				},
+				expiredCount:    2,
+			},
+			fields: fields{
+				qmgrExpires:           prometheus.NewCounter(prometheus.CounterOpts{}),
 			},
 		},
 		{
@@ -116,7 +139,8 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 			},
 			fields: fields{
 				smtpdSASLAuthenticationFailures: prometheus.NewCounter(prometheus.CounterOpts{}),
-				unsupportedLogEntries:           prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"process"}),
+				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"service", "level"}),
+				smtpProcesses:                   prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"status"}),
 			},
 		},
 		{
@@ -132,7 +156,7 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 				smtpdMessagesProcessed: 2,
 			},
 			fields: fields{
-				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"process"}),
+				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"service", "level"}),
 				smtpdProcesses:        prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"sasl_method"}),
 			},
 		},
@@ -146,10 +170,12 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 				removedCount:    0,
 				saslFailedCount: 0,
 				outgoingTLS:     2,
+				smtpdMessagesProcessed: 0,
 			},
 			fields: fields{
-				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"process"}),
+				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"service", "level"}),
 				smtpTLSConnects:       prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"Verified", "TLSv1.2", "ECDHE-RSA-AES256-GCM-SHA384", "256", "256"}),
+				smtpProcesses:         prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"status"}),
 			},
 		},
 		{
@@ -162,9 +188,62 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 				saslFailedCount:        0,
 				outgoingTLS:            0,
 				smtpdMessagesProcessed: 0,
+				smtpMessagesProcessed:  1,
 			},
 			fields: fields{
 				smtpDelays: prometheus.NewHistogramVec(prometheus.HistogramOpts{}, []string{"stage"}),
+				smtpProcesses: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"status"}),
+			},
+		},
+		{
+			name: "Testing different smtp statuses",
+			args: args{
+				line: []string{
+					"Dec 29 02:54:09 mail postfix/smtp[7648]: 732BB407C3: host mail.domain.com[1.1.1.1] said: 451 DT:SPM 163 mx13,P8CowECpNVM_oEVaenoEAQ--.23796S3 1514512449, please try again 15min later (in reply to end of DATA command)",
+					"Dec 29 02:54:12 mail postfix/smtp[7648]: 732BB407C3: to=<redacted@domain.com>, relay=mail.domain.com[1.1.1.1]:25, delay=6.2, delays=0.1/0/5.2/0.87, dsn=4.0.0, status=deferred (host mail.domain.com[1.1.1.1] said: 451 DT:SPM 163 mx40,WsCowAAnEhlCoEVa5GjcAA--.20089S3 1514512452, please try again 15min later (in reply to end of DATA command))",
+					"Dec 29 03:03:48 mail postfix/smtp[8492]: 732BB407C3: to=<redacted@domain.com>, relay=mail.domain.com[1.1.1.1]:25, delay=582, delays=563/16/1.7/0.81, dsn=5.0.0, status=bounced (host mail.domain.com[1.1.1.1] said: 554 DT:SPM 163 mx9,O8CowEDJVFKCokVaRhz+AA--.26016S3 1514513028,please see http://mail.domain.com/help/help_spam.htm?ip= (in reply to end of DATA command))",
+					"Dec 29 03:03:48 mail postfix/bounce[9321]: 732BB407C3: sender non-delivery notification: 5DE184083C",
+				},
+				smtpMessagesProcessed:  2,
+				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"}),
+				bounceNonDelivery: prometheus.NewCounter(prometheus.CounterOpts{}),
+			},
+		},
+		{
+			name: "Testing virtual delivered",
+			args: args{
+				line: []string{
+					"Apr  7 15:35:20 123-mail postfix/virtual[20235]: 199041033BE: to=<me@domain.fr>, relay=virtual, delay=0.08, delays=0.08/0/0/0, dsn=2.0.0, status=sent (delivered to maildir)",
+				},
+				virtualDelivered: 1,
+			},
+			fields: fields{
+				virtualDelivered: prometheus.NewCounter(prometheus.CounterOpts{}),
+			},
+		},
+		{
+			name: "Testing levels of unsupported entries",
+			args: args{
+				line: []string{
+					"Feb 14 19:05:25 123-mail postfix/smtpd[1517]: table hash:/etc/postfix/virtual_mailbox_maps(0,lock|fold_fix) has changed -- restarting",
+				    "Mar 16 12:28:02 123-mail postfix/smtpd[16268]: fatal: file /etc/postfix/main.cf: parameter default_privs: unknown user name value: nobody",
+					"Mar 16 23:30:44 123-mail postfix/qmgr[29980]: warning: please avoid flushing the whole queue when you have",
+					"Mar 16 23:30:44 123-mail postfix/qmgr[29980]: warning: lots of deferred mail, that is bad for performance",
+				},
+				unsupportedLogEntries: []string{
+					`label:<name:"level" value:"" > label:<name:"service" value:"smtpd" > counter:<value:1 > `,
+					`label:<name:"level" value:"fatal" > label:<name:"service" value:"smtpd" > counter:<value:1 > `,
+					`label:<name:"level" value:"warning" > label:<name:"service" value:"qmgr" > counter:<value:2 > `,
+				},
+			},
+			fields: fields{
+				unsupportedLogEntries: prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"service", "level"}),
 			},
 		},
 	}
@@ -181,9 +260,12 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 				qmgrInsertsNrcpt:                tt.fields.qmgrInsertsNrcpt,
 				qmgrInsertsSize:                 tt.fields.qmgrInsertsSize,
 				qmgrRemoves:                     tt.fields.qmgrRemoves,
+				qmgrExpires:                     tt.fields.qmgrExpires,
 				smtpDelays:                      tt.fields.smtpDelays,
 				smtpTLSConnects:                 tt.fields.smtpTLSConnects,
 				smtpDeferreds:                   tt.fields.smtpDeferreds,
+				smtpStatusDeferred:              tt.fields.smtpStatusDeferred,
+				smtpProcesses:                   tt.fields.smtpProcesses,
 				smtpdConnects:                   tt.fields.smtpdConnects,
 				smtpdDisconnects:                tt.fields.smtpdDisconnects,
 				smtpdFCrDNSErrors:               tt.fields.smtpdFCrDNSErrors,
@@ -192,6 +274,8 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 				smtpdRejects:                    tt.fields.smtpdRejects,
 				smtpdSASLAuthenticationFailures: tt.fields.smtpdSASLAuthenticationFailures,
 				smtpdTLSConnects:                tt.fields.smtpdTLSConnects,
+				bounceNonDelivery:               tt.fields.bounceNonDelivery,
+				virtualDelivered:                tt.fields.virtualDelivered,
 				unsupportedLogEntries:           tt.fields.unsupportedLogEntries,
 				logUnsupportedLines:             true,
 			}
@@ -199,9 +283,14 @@ func TestPostfixExporter_CollectFromLogline(t *testing.T) {
 				e.CollectFromLogLine(line)
 			}
 			assertCounterEquals(t, e.qmgrRemoves, tt.args.removedCount, "Wrong number of lines counted")
+			assertCounterEquals(t, e.qmgrExpires, tt.args.expiredCount, "Wrong number of qmgr expired lines counted")
 			assertCounterEquals(t, e.smtpdSASLAuthenticationFailures, tt.args.saslFailedCount, "Wrong number of Sasl counter counted")
 			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.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")
 		})
 	}
 }
@@ -241,3 +330,19 @@ func assertCounterEquals(t *testing.T, counter prometheus.Collector, expected in
 		}
 	}
 }
+func assertVecMetricsEquals(t *testing.T, counter *prometheus.CounterVec, expected []string, message string) {
+	if expected != nil {
+		metricsChan := make(chan prometheus.Metric)
+		go func() {
+			counter.Collect(metricsChan)
+			close(metricsChan)
+		}()
+		var res []string
+		for metric := range metricsChan {
+			metricDto := io_prometheus_client.Metric{}
+			metric.Write(&metricDto)
+			res = append(res, metricDto.String())
+		}
+		assert.Equal(t, expected, res, message)
+	}
+}

More details

Full run details

Historical runs