Skip to content

Commit a6f58e9

Browse files
authored
Merge pull request kumina#75 from paradis/master
Add some new metrics when parsing postfix logs
2 parents 580003c + 0523537 commit a6f58e9

File tree

2 files changed

+183
-22
lines changed

2 files changed

+183
-22
lines changed

postfix_exporter.go

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
288294
var (
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

416442
func 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

615667
func (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

Comments
 (0)