@@ -53,9 +53,12 @@ type PostfixExporter struct {
5353 qmgrInsertsNrcpt prometheus.Histogram
5454 qmgrInsertsSize prometheus.Histogram
5555 qmgrRemoves prometheus.Counter
56+ qmgrExpires prometheus.Counter
5657 smtpDelays * prometheus.HistogramVec
5758 smtpTLSConnects * prometheus.CounterVec
5859 smtpConnectionTimedOut prometheus.Counter
60+ smtpProcesses * prometheus.CounterVec
61+ // should be the same as smtpProcesses{status=deferred}, kept for compatibility, but this doesn't work !
5962 smtpDeferreds prometheus.Counter
6063 smtpdConnects prometheus.Counter
6164 smtpdDisconnects prometheus.Counter
@@ -66,8 +69,11 @@ type PostfixExporter struct {
6669 smtpdSASLAuthenticationFailures prometheus.Counter
6770 smtpdTLSConnects * prometheus.CounterVec
6871 unsupportedLogEntries * prometheus.CounterVec
72+ // same as smtpProcesses{status=deferred}, kept for compatibility
6973 smtpStatusDeferred prometheus.Counter
7074 opendkimSignatureAdded * prometheus.CounterVec
75+ bounceNonDelivery prometheus.Counter
76+ virtualDelivered prometheus.Counter
7177}
7278
7379// A LogSource is an interface to read log lines.
@@ -286,10 +292,11 @@ func CollectShowqFromSocket(path string, ch chan<- prometheus.Metric) error {
286292
287293// Patterns for parsing log messages.
288294var (
289- logLine = regexp .MustCompile (` ?(postfix|opendkim)(/(\w+))?\[\d+\]: (.*)` )
295+ logLine = regexp .MustCompile (` ?(postfix|opendkim)(/(\w+))?\[\d+\]: ((?:(warning|error|fatal|panic): )? .*)` )
290296 lmtpPipeSMTPLine = regexp .MustCompile (`, relay=(\S+), .*, delays=([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+), ` )
291297 qmgrInsertLine = regexp .MustCompile (`:.*, size=(\d+), nrcpt=(\d+) ` )
292- smtpStatusDeferredLine = regexp .MustCompile (`, status=deferred` )
298+ qmgrExpiredLine = regexp .MustCompile (`:.*, status=(expired|force-expired), returned to sender` )
299+ smtpStatusLine = regexp .MustCompile (`, status=(\w+) ` )
293300 smtpTLSLine = regexp .MustCompile (`^(\S+) TLS connection established to \S+: (\S+) with cipher (\S+) \((\d+)/(\d+) bits\)` )
294301 smtpConnectionTimedOut = regexp .MustCompile (`^connect\s+to\s+(.*)\[(.*)\]:(\d+):\s+(Connection timed out)$` )
295302 smtpdFCrDNSErrorsLine = regexp .MustCompile (`^warning: hostname \S+ does not resolve to address ` )
@@ -299,6 +306,7 @@ var (
299306 smtpdSASLAuthenticationFailuresLine = regexp .MustCompile (`^warning: \S+: SASL \S+ authentication failed: ` )
300307 smtpdTLSLine = regexp .MustCompile (`^(\S+) TLS connection established from \S+: (\S+) with cipher (\S+) \((\d+)/(\d+) bits\)` )
301308 opendkimSignatureAdded = regexp .MustCompile (`^[\w\d]+: DKIM-Signature field added \(s=(\w+), d=(.*)\)$` )
309+ bounceNonDeliveryLine = regexp .MustCompile (`: sender non-delivery notification: ` )
302310)
303311
304312// CollectFromLogline collects metrict from a Postfix log line.
@@ -308,10 +316,11 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
308316
309317 if logMatches == nil {
310318 // Unknown log entry format.
311- e .addToUnsupportedLine (line , "" )
319+ e .addToUnsupportedLine (line , "" , "" )
312320 return
313321 }
314322 process := logMatches [1 ]
323+ level := logMatches [5 ]
315324 remainder := logMatches [4 ]
316325 switch process {
317326 case "postfix" :
@@ -324,7 +333,7 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
324333 } else if strings .Contains (remainder , ": reject: " ) {
325334 e .cleanupRejects .Inc ()
326335 } else {
327- e .addToUnsupportedLine (line , subprocess )
336+ e .addToUnsupportedLine (line , subprocess , level )
328337 }
329338 case "lmtp" :
330339 if lmtpMatches := lmtpPipeSMTPLine .FindStringSubmatch (remainder ); lmtpMatches != nil {
@@ -333,7 +342,7 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
333342 addToHistogramVec (e .lmtpDelays , lmtpMatches [4 ], "LMTP sdelay" , "connection_setup" )
334343 addToHistogramVec (e .lmtpDelays , lmtpMatches [5 ], "LMTP xdelay" , "transmission" )
335344 } else {
336- e .addToUnsupportedLine (line , subprocess )
345+ e .addToUnsupportedLine (line , subprocess , level )
337346 }
338347 case "pipe" :
339348 if pipeMatches := lmtpPipeSMTPLine .FindStringSubmatch (remainder ); pipeMatches != nil {
@@ -342,32 +351,37 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
342351 addToHistogramVec (e .pipeDelays , pipeMatches [4 ], "PIPE sdelay" , pipeMatches [1 ], "connection_setup" )
343352 addToHistogramVec (e .pipeDelays , pipeMatches [5 ], "PIPE xdelay" , pipeMatches [1 ], "transmission" )
344353 } else {
345- e .addToUnsupportedLine (line , subprocess )
354+ e .addToUnsupportedLine (line , subprocess , level )
346355 }
347356 case "qmgr" :
348357 if qmgrInsertMatches := qmgrInsertLine .FindStringSubmatch (remainder ); qmgrInsertMatches != nil {
349358 addToHistogram (e .qmgrInsertsSize , qmgrInsertMatches [1 ], "QMGR size" )
350359 addToHistogram (e .qmgrInsertsNrcpt , qmgrInsertMatches [2 ], "QMGR nrcpt" )
351360 } else if strings .HasSuffix (remainder , ": removed" ) {
352361 e .qmgrRemoves .Inc ()
362+ } else if qmgrExpired := qmgrExpiredLine .FindStringSubmatch (remainder ); qmgrExpired != nil {
363+ e .qmgrExpires .Inc ()
353364 } else {
354- e .addToUnsupportedLine (line , subprocess )
365+ e .addToUnsupportedLine (line , subprocess , level )
355366 }
356367 case "smtp" :
357368 if smtpMatches := lmtpPipeSMTPLine .FindStringSubmatch (remainder ); smtpMatches != nil {
358369 addToHistogramVec (e .smtpDelays , smtpMatches [2 ], "before_queue_manager" , "" )
359370 addToHistogramVec (e .smtpDelays , smtpMatches [3 ], "queue_manager" , "" )
360371 addToHistogramVec (e .smtpDelays , smtpMatches [4 ], "connection_setup" , "" )
361372 addToHistogramVec (e .smtpDelays , smtpMatches [5 ], "transmission" , "" )
362- if smtpMatches := smtpStatusDeferredLine .FindStringSubmatch (remainder ); smtpMatches != nil {
363- e .smtpStatusDeferred .Inc ()
373+ if smtpStatusMatches := smtpStatusLine .FindStringSubmatch (remainder ); smtpStatusMatches != nil {
374+ e .smtpProcesses .WithLabelValues (smtpStatusMatches [1 ]).Inc ()
375+ if smtpStatusMatches [1 ] == "deferred" {
376+ e .smtpStatusDeferred .Inc ()
377+ }
364378 }
365379 } else if smtpTLSMatches := smtpTLSLine .FindStringSubmatch (remainder ); smtpTLSMatches != nil {
366380 e .smtpTLSConnects .WithLabelValues (smtpTLSMatches [1 :]... ).Inc ()
367381 } else if smtpMatches := smtpConnectionTimedOut .FindStringSubmatch (remainder ); smtpMatches != nil {
368382 e .smtpConnectionTimedOut .Inc ()
369383 } else {
370- e .addToUnsupportedLine (line , subprocess )
384+ e .addToUnsupportedLine (line , subprocess , level )
371385 }
372386 case "smtpd" :
373387 if strings .HasPrefix (remainder , "connect from " ) {
@@ -389,28 +403,40 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
389403 } else if smtpdTLSMatches := smtpdTLSLine .FindStringSubmatch (remainder ); smtpdTLSMatches != nil {
390404 e .smtpdTLSConnects .WithLabelValues (smtpdTLSMatches [1 :]... ).Inc ()
391405 } else {
392- e .addToUnsupportedLine (line , subprocess )
406+ e .addToUnsupportedLine (line , subprocess , level )
407+ }
408+ case "bounce" :
409+ if bounceMatches := bounceNonDeliveryLine .FindStringSubmatch (remainder ); bounceMatches != nil {
410+ e .bounceNonDelivery .Inc ()
411+ } else {
412+ e .addToUnsupportedLine (line , process , level )
413+ }
414+ case "virtual" :
415+ if strings .HasSuffix (remainder , ", status=sent (delivered to maildir)" ) {
416+ e .virtualDelivered .Inc ()
417+ } else {
418+ e .addToUnsupportedLine (line , process , level )
393419 }
394420 default :
395- e .addToUnsupportedLine (line , subprocess )
421+ e .addToUnsupportedLine (line , subprocess , level )
396422 }
397423 case "opendkim" :
398424 if opendkimMatches := opendkimSignatureAdded .FindStringSubmatch (remainder ); opendkimMatches != nil {
399425 e .opendkimSignatureAdded .WithLabelValues (opendkimMatches [1 ], opendkimMatches [2 ]).Inc ()
400426 } else {
401- e .addToUnsupportedLine (line , process )
427+ e .addToUnsupportedLine (line , process , level )
402428 }
403429 default :
404430 // Unknown log entry format.
405- e .addToUnsupportedLine (line , "" )
431+ e .addToUnsupportedLine (line , process , level )
406432 }
407433}
408434
409- func (e * PostfixExporter ) addToUnsupportedLine (line string , subprocess string ) {
435+ func (e * PostfixExporter ) addToUnsupportedLine (line string , subprocess string , level string ) {
410436 if e .logUnsupportedLines {
411437 log .Printf ("Unsupported Line: %v" , line )
412438 }
413- e .unsupportedLogEntries .WithLabelValues (subprocess ).Inc ()
439+ e .unsupportedLogEntries .WithLabelValues (subprocess , level ).Inc ()
414440}
415441
416442func addToHistogram (h prometheus.Histogram , value , fieldName string ) {
@@ -484,6 +510,11 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
484510 Name : "qmgr_messages_removed_total" ,
485511 Help : "Total number of messages removed from mail queues." ,
486512 }),
513+ qmgrExpires : prometheus .NewCounter (prometheus.CounterOpts {
514+ Namespace : "postfix" ,
515+ Name : "qmgr_messages_expired_total" ,
516+ Help : "Total number of messages expired from mail queues." ,
517+ }),
487518 smtpDelays : prometheus .NewHistogramVec (
488519 prometheus.HistogramOpts {
489520 Namespace : "postfix" ,
@@ -504,6 +535,13 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
504535 Name : "smtp_deferred_messages_total" ,
505536 Help : "Total number of messages that have been deferred on SMTP." ,
506537 }),
538+ smtpProcesses : prometheus .NewCounterVec (
539+ prometheus.CounterOpts {
540+ Namespace : "postfix" ,
541+ Name : "smtp_messages_processed_total" ,
542+ Help : "Total number of messages that have been processed by the smtp process." ,
543+ },
544+ []string {"status" }),
507545 smtpConnectionTimedOut : prometheus .NewCounter (prometheus.CounterOpts {
508546 Namespace : "postfix" ,
509547 Name : "smtp_connection_timed_out_total" ,
@@ -563,7 +601,7 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
563601 Name : "unsupported_log_entries_total" ,
564602 Help : "Log entries that could not be processed." ,
565603 },
566- []string {"service" }),
604+ []string {"service" , "level" }),
567605 smtpStatusDeferred : prometheus .NewCounter (prometheus.CounterOpts {
568606 Namespace : "postfix" ,
569607 Name : "smtp_status_deferred" ,
@@ -577,6 +615,16 @@ func NewPostfixExporter(showqPath string, logSrc LogSource, logUnsupportedLines
577615 },
578616 []string {"subject" , "domain" },
579617 ),
618+ bounceNonDelivery : prometheus .NewCounter (prometheus.CounterOpts {
619+ Namespace : "postfix" ,
620+ Name : "bounce_non_delivery_notification_total" ,
621+ Help : "Total number of non delivery notification sent by bounce." ,
622+ }),
623+ virtualDelivered : prometheus .NewCounter (prometheus.CounterOpts {
624+ Namespace : "postfix" ,
625+ Name : "virtual_delivered_total" ,
626+ Help : "Total number of mail delivered to a virtual mailbox." ,
627+ }),
580628 }, nil
581629}
582630
@@ -595,9 +643,11 @@ func (e *PostfixExporter) Describe(ch chan<- *prometheus.Desc) {
595643 ch <- e .qmgrInsertsNrcpt .Desc ()
596644 ch <- e .qmgrInsertsSize .Desc ()
597645 ch <- e .qmgrRemoves .Desc ()
646+ ch <- e .qmgrExpires .Desc ()
598647 e .smtpDelays .Describe (ch )
599648 e .smtpTLSConnects .Describe (ch )
600649 ch <- e .smtpDeferreds .Desc ()
650+ e .smtpProcesses .Describe (ch )
601651 ch <- e .smtpdConnects .Desc ()
602652 ch <- e .smtpdDisconnects .Desc ()
603653 ch <- e .smtpdFCrDNSErrors .Desc ()
@@ -610,6 +660,8 @@ func (e *PostfixExporter) Describe(ch chan<- *prometheus.Desc) {
610660 e .unsupportedLogEntries .Describe (ch )
611661 e .smtpConnectionTimedOut .Describe (ch )
612662 e .opendkimSignatureAdded .Describe (ch )
663+ ch <- e .bounceNonDelivery .Desc ()
664+ ch <- e .virtualDelivered .Desc ()
613665}
614666
615667func (e * PostfixExporter ) StartMetricCollection (ctx context.Context ) {
@@ -670,9 +722,11 @@ func (e *PostfixExporter) Collect(ch chan<- prometheus.Metric) {
670722 ch <- e .qmgrInsertsNrcpt
671723 ch <- e .qmgrInsertsSize
672724 ch <- e .qmgrRemoves
725+ ch <- e .qmgrExpires
673726 e .smtpDelays .Collect (ch )
674727 e .smtpTLSConnects .Collect (ch )
675728 ch <- e .smtpDeferreds
729+ e .smtpProcesses .Collect (ch )
676730 ch <- e .smtpdConnects
677731 ch <- e .smtpdDisconnects
678732 ch <- e .smtpdFCrDNSErrors
@@ -685,4 +739,6 @@ func (e *PostfixExporter) Collect(ch chan<- prometheus.Metric) {
685739 e .unsupportedLogEntries .Collect (ch )
686740 ch <- e .smtpConnectionTimedOut
687741 e .opendkimSignatureAdded .Collect (ch )
742+ ch <- e .bounceNonDelivery
743+ ch <- e .virtualDelivered
688744}
0 commit comments