- Added flag for logging lines that were not processed (mainly for debugging/development)

- Added Counter for smtp Connection Timed Out events
- Added counter for OpenDKIM signatures added
This commit is contained in:
Per Abich 2020-02-18 16:54:25 +01:00
parent e416d73974
commit 3af4390187

View File

@ -45,9 +45,10 @@ var (
// PostfixExporter holds the state that should be preserved by the // PostfixExporter holds the state that should be preserved by the
// Postfix Prometheus metrics exporter across scrapes. // Postfix Prometheus metrics exporter across scrapes.
type PostfixExporter struct { type PostfixExporter struct {
showqPath string showqPath string
journal *Journal journal *Journal
tailer *tail.Tail tailer *tail.Tail
logUnsupportedLines bool
// Metrics that should persist after refreshes, based on logs. // Metrics that should persist after refreshes, based on logs.
cleanupProcesses prometheus.Counter cleanupProcesses prometheus.Counter
@ -60,6 +61,7 @@ type PostfixExporter struct {
qmgrRemoves prometheus.Counter qmgrRemoves prometheus.Counter
smtpDelays *prometheus.HistogramVec smtpDelays *prometheus.HistogramVec
smtpTLSConnects *prometheus.CounterVec smtpTLSConnects *prometheus.CounterVec
smtpConnectionTimedOut prometheus.Counter
smtpDeferreds prometheus.Counter smtpDeferreds prometheus.Counter
smtpdConnects prometheus.Counter smtpdConnects prometheus.Counter
smtpdDisconnects prometheus.Counter smtpdDisconnects prometheus.Counter
@ -71,6 +73,7 @@ type PostfixExporter struct {
smtpdTLSConnects *prometheus.CounterVec smtpdTLSConnects *prometheus.CounterVec
unsupportedLogEntries *prometheus.CounterVec unsupportedLogEntries *prometheus.CounterVec
smtpStatusDeferred prometheus.Counter smtpStatusDeferred prometheus.Counter
opendkimSignatureAdded *prometheus.CounterVec
} }
// CollectShowqFromReader parses the output of Postfix's 'showq' command // CollectShowqFromReader parses the output of Postfix's 'showq' command
@ -279,160 +282,205 @@ func CollectShowqFromSocket(path string, ch chan<- prometheus.Metric) error {
// Patterns for parsing log messages. // Patterns for parsing log messages.
var ( var (
logLine = regexp.MustCompile(` ?postfix/(\w+)\[\d+\]: (.*)`) logLine = regexp.MustCompile(` ?(postfix|opendkim)(/(\w+))?\[\d+\]: (.*)`)
lmtpPipeSMTPLine = regexp.MustCompile(`, relay=(\S+), .*, delays=([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+), `) lmtpPipeSMTPLine = regexp.MustCompile(`, relay=(\S+), .*, delays=([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+), `)
qmgrInsertLine = regexp.MustCompile(`:.*, size=(\d+), nrcpt=(\d+) `) qmgrInsertLine = regexp.MustCompile(`:.*, size=(\d+), nrcpt=(\d+) `)
smtpStatusDeferredLine = regexp.MustCompile(`, status=deferred`) smtpStatusDeferredLine = regexp.MustCompile(`, status=deferred`)
smtpTLSLine = regexp.MustCompile(`^(\S+) TLS connection established to \S+: (\S+) with cipher (\S+) \((\d+)/(\d+) bits\)$`) 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 `) smtpdFCrDNSErrorsLine = regexp.MustCompile(`^warning: hostname \S+ does not resolve to address `)
smtpdProcessesSASLLine = regexp.MustCompile(`: client=.*, sasl_username=(\S+)`) smtpdProcessesSASLLine = regexp.MustCompile(`: client=.*, sasl_username=(\S+)`)
smtpdRejectsLine = regexp.MustCompile(`^NOQUEUE: reject: RCPT from \S+: ([0-9]+) `) smtpdRejectsLine = regexp.MustCompile(`^NOQUEUE: reject: RCPT from \S+: ([0-9]+) `)
smtpdLostConnectionLine = regexp.MustCompile(`^lost connection after (\w+) from `) smtpdLostConnectionLine = regexp.MustCompile(`^lost connection after (\w+) from `)
smtpdSASLAuthenticationFailuresLine = regexp.MustCompile(`^warning: \S+: SASL \S+ authentication failed: `) 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\)$`) 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=(.*)\)$`)
) )
// CollectFromLogline collects metrict from a Postfix log line. // CollectFromLogline collects metrict from a Postfix log line.
func (e *PostfixExporter) CollectFromLogLine(line string) { func (e *PostfixExporter) CollectFromLogLine(line string) {
// Strip off timestamp, hostname, etc. // Strip off timestamp, hostname, etc.
if logMatches := logLine.FindStringSubmatch(line); logMatches != nil { if logMatches := logLine.FindStringSubmatch(line); logMatches != nil {
// Group patterns to check by Postfix service. process := logMatches[1]
if logMatches[1] == "cleanup" { subprocess := logMatches[3]
if strings.Contains(logMatches[2], ": message-id=<") { remainder := logMatches[4]
e.cleanupProcesses.Inc() switch process {
} else if strings.Contains(logMatches[2], ": reject: ") { case "postfix":
e.cleanupRejects.Inc() // Group patterns to check by Postfix service.
} else if strings.Contains(logMatches[2], "message not accepted") { if subprocess == "cleanup" {
e.cleanupNotAccepted.Inc() if strings.Contains(remainder, ": message-id=<") {
} else { e.cleanupProcesses.Inc()
e.unsupportedLogEntries.WithLabelValues(logMatches[1]).Inc() } else if strings.Contains(remainder, ": reject: ") {
} e.cleanupRejects.Inc()
} else if logMatches[1] == "lmtp" { } else if strings.Contains(remainder, "message not accepted") {
if lmtpMatches := lmtpPipeSMTPLine.FindStringSubmatch(logMatches[2]); lmtpMatches != nil { e.cleanupNotAccepted.Inc()
pdelay, err := strconv.ParseFloat(lmtpMatches[2], 64) } else {
if err != nil { if e.logUnsupportedLines {
log.Printf("Couldn't convert LMTP pdelay: %v", err) log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
} }
e.lmtpDelays.WithLabelValues("before_queue_manager").Observe(pdelay) } else if subprocess == "lmtp" {
adelay, err := strconv.ParseFloat(lmtpMatches[3], 64) if lmtpMatches := lmtpPipeSMTPLine.FindStringSubmatch(remainder); lmtpMatches != nil {
if err != nil { pdelay, err := strconv.ParseFloat(lmtpMatches[2], 64)
log.Printf("Couldn't convert LMTP adelay: %v", err) if err != nil {
log.Printf("Couldn't convert LMTP pdelay: %v", err)
}
e.lmtpDelays.WithLabelValues("before_queue_manager").Observe(pdelay)
adelay, err := strconv.ParseFloat(lmtpMatches[3], 64)
if err != nil {
log.Printf("Couldn't convert LMTP adelay: %v", err)
}
e.lmtpDelays.WithLabelValues("queue_manager").Observe(adelay)
sdelay, err := strconv.ParseFloat(lmtpMatches[4], 64)
if err != nil {
log.Printf("Couldn't convert LMTP adelay: %v", err)
}
e.lmtpDelays.WithLabelValues("connection_setup").Observe(sdelay)
xdelay, err := strconv.ParseFloat(lmtpMatches[5], 64)
if err != nil {
log.Printf("Couldn't convert LMTP xdelay: %v", err)
}
e.lmtpDelays.WithLabelValues("transmission").Observe(xdelay)
} else {
if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
} }
e.lmtpDelays.WithLabelValues("queue_manager").Observe(adelay) } else if subprocess == "pipe" {
sdelay, err := strconv.ParseFloat(lmtpMatches[4], 64) if pipeMatches := lmtpPipeSMTPLine.FindStringSubmatch(remainder); pipeMatches != nil {
if err != nil { pdelay, err := strconv.ParseFloat(pipeMatches[2], 64)
log.Printf("Couldn't convert LMTP adelay: %v", err) if err != nil {
log.Printf("Couldn't convert PIPE pdelay: %v", err)
}
e.pipeDelays.WithLabelValues(pipeMatches[1], "before_queue_manager").Observe(pdelay)
adelay, err := strconv.ParseFloat(pipeMatches[3], 64)
if err != nil {
log.Printf("Couldn't convert PIPE adelay: %v", err)
}
e.pipeDelays.WithLabelValues(pipeMatches[1], "queue_manager").Observe(adelay)
sdelay, err := strconv.ParseFloat(pipeMatches[4], 64)
if err != nil {
log.Printf("Couldn't convert PIPE sdelay: %v", err)
}
e.pipeDelays.WithLabelValues(pipeMatches[1], "connection_setup").Observe(sdelay)
xdelay, err := strconv.ParseFloat(pipeMatches[5], 64)
if err != nil {
log.Printf("Couldn't convert PIPE xdelay: %v", err)
}
e.pipeDelays.WithLabelValues(pipeMatches[1], "transmission").Observe(xdelay)
} else {
if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
} }
e.lmtpDelays.WithLabelValues("connection_setup").Observe(sdelay) } else if subprocess == "qmgr" {
xdelay, err := strconv.ParseFloat(lmtpMatches[5], 64) if qmgrInsertMatches := qmgrInsertLine.FindStringSubmatch(remainder); qmgrInsertMatches != nil {
if err != nil { size, err := strconv.ParseFloat(qmgrInsertMatches[1], 64)
log.Printf("Couldn't convert LMTP xdelay: %v", err) if err != nil {
log.Printf("Couldn't convert QMGR size: %v", err)
}
e.qmgrInsertsSize.Observe(size)
nrcpt, err := strconv.ParseFloat(qmgrInsertMatches[2], 64)
if err != nil {
log.Printf("Couldn't convert QMGR nrcpt: %v", err)
}
e.qmgrInsertsNrcpt.Observe(nrcpt)
} else if strings.HasSuffix(remainder, ": removed") {
e.qmgrRemoves.Inc()
} else {
if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
} }
e.lmtpDelays.WithLabelValues("transmission").Observe(xdelay) } else if subprocess == "smtp" {
} else { if smtpMatches := lmtpPipeSMTPLine.FindStringSubmatch(remainder); smtpMatches != nil {
e.unsupportedLogEntries.WithLabelValues(logMatches[1]).Inc() pdelay, err := strconv.ParseFloat(smtpMatches[2], 64)
} if err != nil {
} else if logMatches[1] == "pipe" { log.Printf("Couldn't convert SMTP pdelay: %v", err)
if pipeMatches := lmtpPipeSMTPLine.FindStringSubmatch(logMatches[2]); pipeMatches != nil { }
pdelay, err := strconv.ParseFloat(pipeMatches[2], 64) e.smtpDelays.WithLabelValues("before_queue_manager").Observe(pdelay)
if err != nil { adelay, err := strconv.ParseFloat(smtpMatches[3], 64)
log.Printf("Couldn't convert PIPE pdelay: %v", err) if err != nil {
} log.Printf("Couldn't convert SMTP adelay: %v", err)
e.pipeDelays.WithLabelValues(pipeMatches[1], "before_queue_manager").Observe(pdelay) }
adelay, err := strconv.ParseFloat(pipeMatches[3], 64) e.smtpDelays.WithLabelValues("queue_manager").Observe(adelay)
if err != nil { sdelay, err := strconv.ParseFloat(smtpMatches[4], 64)
log.Printf("Couldn't convert PIPE adelay: %v", err) if err != nil {
} log.Printf("Couldn't convert SMTP sdelay: %v", err)
e.pipeDelays.WithLabelValues(pipeMatches[1], "queue_manager").Observe(adelay) }
sdelay, err := strconv.ParseFloat(pipeMatches[4], 64) e.smtpDelays.WithLabelValues("connection_setup").Observe(sdelay)
if err != nil { xdelay, err := strconv.ParseFloat(smtpMatches[5], 64)
log.Printf("Couldn't convert PIPE sdelay: %v", err) if err != nil {
} log.Printf("Couldn't convert SMTP xdelay: %v", err)
e.pipeDelays.WithLabelValues(pipeMatches[1], "connection_setup").Observe(sdelay) }
xdelay, err := strconv.ParseFloat(pipeMatches[5], 64) e.smtpDelays.WithLabelValues("transmission").Observe(xdelay)
if err != nil {
log.Printf("Couldn't convert PIPE xdelay: %v", err)
}
e.pipeDelays.WithLabelValues(pipeMatches[1], "transmission").Observe(xdelay)
} else {
e.unsupportedLogEntries.WithLabelValues(logMatches[1]).Inc()
}
} else if logMatches[1] == "qmgr" {
if qmgrInsertMatches := qmgrInsertLine.FindStringSubmatch(logMatches[2]); qmgrInsertMatches != nil {
size, err := strconv.ParseFloat(qmgrInsertMatches[1], 64)
if err != nil {
log.Printf("Couldn't convert QMGR size: %v", err)
}
e.qmgrInsertsSize.Observe(size)
nrcpt, err := strconv.ParseFloat(qmgrInsertMatches[2], 64)
if err != nil {
log.Printf("Couldn't convert QMGR nrcpt: %v", err)
}
e.qmgrInsertsNrcpt.Observe(nrcpt)
} else if strings.HasSuffix(logMatches[2], ": removed") {
e.qmgrRemoves.Inc()
} else {
e.unsupportedLogEntries.WithLabelValues(logMatches[1]).Inc()
}
} else if logMatches[1] == "smtp" {
if smtpMatches := lmtpPipeSMTPLine.FindStringSubmatch(logMatches[2]); smtpMatches != nil {
pdelay, err := strconv.ParseFloat(smtpMatches[2], 64)
if err != nil {
log.Printf("Couldn't convert SMTP pdelay: %v", err)
}
e.smtpDelays.WithLabelValues("before_queue_manager").Observe(pdelay)
adelay, err := strconv.ParseFloat(smtpMatches[3], 64)
if err != nil {
log.Printf("Couldn't convert SMTP adelay: %v", err)
}
e.smtpDelays.WithLabelValues("queue_manager").Observe(adelay)
sdelay, err := strconv.ParseFloat(smtpMatches[4], 64)
if err != nil {
log.Printf("Couldn't convert SMTP sdelay: %v", err)
}
e.smtpDelays.WithLabelValues("connection_setup").Observe(sdelay)
xdelay, err := strconv.ParseFloat(smtpMatches[5], 64)
if err != nil {
log.Printf("Couldn't convert SMTP xdelay: %v", err)
}
e.smtpDelays.WithLabelValues("transmission").Observe(xdelay)
if smtpMatches := smtpStatusDeferredLine.FindStringSubmatch(logMatches[2]); smtpMatches != nil { if smtpMatches := smtpStatusDeferredLine.FindStringSubmatch(remainder); smtpMatches != nil {
e.smtpStatusDeferred.Inc() 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 {
if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
}
} else if subprocess == "smtpd" {
if strings.HasPrefix(remainder, "connect from ") {
e.smtpdConnects.Inc()
} else if strings.HasPrefix(remainder, "disconnect from ") {
e.smtpdDisconnects.Inc()
} else if smtpdFCrDNSErrorsLine.MatchString(remainder) {
e.smtpdFCrDNSErrors.Inc()
} else if smtpdLostConnectionMatches := smtpdLostConnectionLine.FindStringSubmatch(remainder); smtpdLostConnectionMatches != nil {
e.smtpdLostConnections.WithLabelValues(smtpdLostConnectionMatches[1]).Inc()
} else if smtpdProcessesSASLMatches := smtpdProcessesSASLLine.FindStringSubmatch(remainder); smtpdProcessesSASLMatches != nil {
e.smtpdProcesses.WithLabelValues(smtpdProcessesSASLMatches[1]).Inc()
} else if strings.Contains(remainder, ": client=") {
e.smtpdProcesses.WithLabelValues("").Inc()
} else if smtpdRejectsMatches := smtpdRejectsLine.FindStringSubmatch(remainder); smtpdRejectsMatches != nil {
e.smtpdRejects.WithLabelValues(smtpdRejectsMatches[1]).Inc()
} else if smtpdSASLAuthenticationFailuresLine.MatchString(remainder) {
e.smtpdSASLAuthenticationFailures.Inc()
} else if smtpdTLSMatches := smtpdTLSLine.FindStringSubmatch(remainder); smtpdTLSMatches != nil {
e.smtpdTLSConnects.WithLabelValues(smtpdTLSMatches[1:]...).Inc()
} else {
if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
} }
} else if smtpTLSMatches := smtpTLSLine.FindStringSubmatch(logMatches[2]); smtpTLSMatches != nil {
e.smtpTLSConnects.WithLabelValues(smtpTLSMatches[1:]...).Inc()
} else { } else {
e.unsupportedLogEntries.WithLabelValues(logMatches[1]).Inc() // Unknown Postfix service.
if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
} }
} else if logMatches[1] == "smtpd" { case "opendkim":
if strings.HasPrefix(logMatches[2], "connect from ") { if opendkimMatches := opendkimSignatureAdded.FindStringSubmatch(remainder); opendkimMatches != nil {
e.smtpdConnects.Inc() e.opendkimSignatureAdded.WithLabelValues(opendkimMatches[1], opendkimMatches[2]).Inc()
} else if strings.HasPrefix(logMatches[2], "disconnect from ") {
e.smtpdDisconnects.Inc()
} else if smtpdFCrDNSErrorsLine.MatchString(logMatches[2]) {
e.smtpdFCrDNSErrors.Inc()
} else if smtpdLostConnectionMatches := smtpdLostConnectionLine.FindStringSubmatch(logMatches[2]); smtpdLostConnectionMatches != nil {
e.smtpdLostConnections.WithLabelValues(smtpdLostConnectionMatches[1]).Inc()
} else if smtpdProcessesSASLMatches := smtpdProcessesSASLLine.FindStringSubmatch(logMatches[2]); smtpdProcessesSASLMatches != nil {
e.smtpdProcesses.WithLabelValues(smtpdProcessesSASLMatches[1]).Inc()
} else if strings.Contains(logMatches[2], ": client=") {
e.smtpdProcesses.WithLabelValues("").Inc()
} else if smtpdRejectsMatches := smtpdRejectsLine.FindStringSubmatch(logMatches[2]); smtpdRejectsMatches != nil {
e.smtpdRejects.WithLabelValues(smtpdRejectsMatches[1]).Inc()
} else if smtpdSASLAuthenticationFailuresLine.MatchString(logMatches[2]) {
e.smtpdSASLAuthenticationFailures.Inc()
} else if smtpdTLSMatches := smtpdTLSLine.FindStringSubmatch(logMatches[2]); smtpdTLSMatches != nil {
e.smtpdTLSConnects.WithLabelValues(smtpdTLSMatches[1:]...).Inc()
} else { } else {
e.unsupportedLogEntries.WithLabelValues(logMatches[1]).Inc() if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues(subprocess).Inc()
} }
} else { default:
// Unknown Postfix service.
e.unsupportedLogEntries.WithLabelValues(logMatches[1]).Inc()
} }
} else { } else {
// Unknown log entry format. // Unknown log entry format.
if e.logUnsupportedLines {
log.Printf("Unsupported Line: %v", line)
}
e.unsupportedLogEntries.WithLabelValues("").Inc() e.unsupportedLogEntries.WithLabelValues("").Inc()
} }
} }
@ -447,7 +495,7 @@ func (e *PostfixExporter) CollectLogfileFromFile(ctx context.Context) {
Help: "Whether scraping Postfix's metrics was successful.", Help: "Whether scraping Postfix's metrics was successful.",
}, },
[]string{"path"}) []string{"path"})
gauge := gaugeVec.With(prometheus.Labels{"path": e.tailer.Filename}) gauge := gaugeVec.WithLabelValues(e.tailer.Filename)
for { for {
select { select {
case line := <-e.tailer.Lines: case line := <-e.tailer.Lines:
@ -461,7 +509,7 @@ func (e *PostfixExporter) CollectLogfileFromFile(ctx context.Context) {
} }
// NewPostfixExporter creates a new Postfix exporter instance. // NewPostfixExporter creates a new Postfix exporter instance.
func NewPostfixExporter(showqPath string, logfilePath string, journal *Journal) (*PostfixExporter, error) { func NewPostfixExporter(showqPath string, logfilePath string, journal *Journal, logUnsupportedLines bool) (*PostfixExporter, error) {
var tailer *tail.Tail var tailer *tail.Tail
if logfilePath != "" { if logfilePath != "" {
var err error var err error
@ -475,9 +523,10 @@ func NewPostfixExporter(showqPath string, logfilePath string, journal *Journal)
} }
} }
return &PostfixExporter{ return &PostfixExporter{
showqPath: showqPath, logUnsupportedLines: logUnsupportedLines,
tailer: tailer, showqPath: showqPath,
journal: journal, tailer: tailer,
journal: journal,
cleanupProcesses: prometheus.NewCounter(prometheus.CounterOpts{ cleanupProcesses: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "postfix", Namespace: "postfix",
@ -547,6 +596,11 @@ func NewPostfixExporter(showqPath string, logfilePath string, journal *Journal)
Name: "smtp_deferred_messages_total", Name: "smtp_deferred_messages_total",
Help: "Total number of messages that have been deferred on SMTP.", Help: "Total number of messages that have been deferred on SMTP.",
}), }),
smtpConnectionTimedOut: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "postfix",
Name: "smtp_connection_timed_out_total",
Help: "Total number of messages that have been deferred on SMTP.",
}),
smtpdConnects: prometheus.NewCounter(prometheus.CounterOpts{ smtpdConnects: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "postfix", Namespace: "postfix",
Name: "smtpd_connects_total", Name: "smtpd_connects_total",
@ -607,6 +661,14 @@ func NewPostfixExporter(showqPath string, logfilePath string, journal *Journal)
Name: "smtp_status_deferred", Name: "smtp_status_deferred",
Help: "Total number of messages deferred.", Help: "Total number of messages deferred.",
}), }),
opendkimSignatureAdded: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: "opendkim",
Name: "signatures_added_total",
Help: "Total number of messages signed.",
},
[]string{"subject", "domain"},
),
}, nil }, nil
} }
@ -645,7 +707,7 @@ func (e *PostfixExporter) foreverCollectFromJournal(ctx context.Context) {
Name: "up", Name: "up",
Help: "Whether scraping Postfix's metrics was successful.", Help: "Whether scraping Postfix's metrics was successful.",
}, },
[]string{"path"}).With(prometheus.Labels{"path": e.journal.Path}) []string{"path"}).WithLabelValues(e.journal.Path)
select { select {
case <-ctx.Done(): case <-ctx.Done():
gauge.Set(0) gauge.Set(0)
@ -718,6 +780,8 @@ func (e *PostfixExporter) Collect(ch chan<- prometheus.Metric) {
e.smtpdTLSConnects.Collect(ch) e.smtpdTLSConnects.Collect(ch)
ch <- e.smtpStatusDeferred ch <- e.smtpStatusDeferred
e.unsupportedLogEntries.Collect(ch) e.unsupportedLogEntries.Collect(ch)
ch <- e.smtpConnectionTimedOut
e.opendkimSignatureAdded.Collect(ch)
} }
func main() { func main() {
@ -727,6 +791,7 @@ func main() {
metricsPath = app.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String() metricsPath = app.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String()
postfixShowqPath = app.Flag("postfix.showq_path", "Path at which Postfix places its showq socket.").Default("/var/spool/postfix/public/showq").String() postfixShowqPath = app.Flag("postfix.showq_path", "Path at which Postfix places its showq socket.").Default("/var/spool/postfix/public/showq").String()
postfixLogfilePath = app.Flag("postfix.logfile_path", "Path where Postfix writes log entries. This file will be truncated by this exporter.").Default("/var/log/postfix_exporter_input.log").String() postfixLogfilePath = app.Flag("postfix.logfile_path", "Path where Postfix writes log entries. This file will be truncated by this exporter.").Default("/var/log/postfix_exporter_input.log").String()
logUnsupportedLines = app.Flag("log.unsupported", "Log all unsupported lines.").Bool()
systemdEnable bool systemdEnable bool
systemdUnit, systemdSlice, systemdJournalPath string systemdUnit, systemdSlice, systemdJournalPath string
) )
@ -751,6 +816,7 @@ func main() {
*postfixShowqPath, *postfixShowqPath,
*postfixLogfilePath, *postfixLogfilePath,
journal, journal,
*logUnsupportedLines,
) )
if err != nil { if err != nil {
log.Fatalf("Failed to create PostfixExporter: %s", err) log.Fatalf("Failed to create PostfixExporter: %s", err)