Add the name of the message queue as a label.

Instead of providing stats for all messages collectively, it would make
more sense to group them by queue. That way it's possible to alert on
the number of active, deferred, etc. messages.

For Postfix 2.x there isn't a lot we can do, as there only is a single
ASCII character distinguishing between two queues specifically.
This commit is contained in:
Ed Schouten 2017-04-18 16:03:53 +02:00
parent 1df13fe2c1
commit c3262ed850

View File

@ -62,30 +62,40 @@ func CollectTextualShowqFromReader(file io.Reader, ch chan<- prometheus.Metric)
// Regular expression for matching postqueue's output. Example: // Regular expression for matching postqueue's output. Example:
// "A07A81514 5156 Tue Feb 14 13:13:54 MAILER-DAEMON" // "A07A81514 5156 Tue Feb 14 13:13:54 MAILER-DAEMON"
messageLine := regexp.MustCompile("^[0-9A-F]+ +(\\d+) (\\w{3} \\w{3} +\\d+ +\\d+:\\d{2}:\\d{2}) +") messageLine := regexp.MustCompile("^[0-9A-F]+([\\*!]?) +(\\d+) (\\w{3} \\w{3} +\\d+ +\\d+:\\d{2}:\\d{2}) +")
// Histograms tracking the messages by size and age. // Histograms tracking the messages by size and age.
sizeHistogram := prometheus.NewHistogram( sizeHistogram := prometheus.NewHistogramVec(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Namespace: "postfix", Namespace: "postfix",
Name: "queue_message_size_bytes", Name: "queue_message_size_bytes",
Help: "Size of messages in Postfix's message queue, in bytes", Help: "Size of messages in Postfix's message queue, in bytes",
Buckets: []float64{1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9}, Buckets: []float64{1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9},
}) },
ageHistogram := prometheus.NewHistogram( []string{"queue"})
ageHistogram := prometheus.NewHistogramVec(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Namespace: "postfix", Namespace: "postfix",
Name: "queue_message_age_seconds", Name: "queue_message_age_seconds",
Help: "Age of messages in Postfix's message queue, in seconds", Help: "Age of messages in Postfix's message queue, in seconds",
Buckets: []float64{1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8}, Buckets: []float64{1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8},
}) },
[]string{"queue"})
now := time.Now() now := time.Now()
for scanner.Scan() { for scanner.Scan() {
matches := messageLine.FindStringSubmatch(scanner.Text()) matches := messageLine.FindStringSubmatch(scanner.Text())
if matches != nil { if matches != nil {
// Derive the name of the message queue.
queue := "other"
if matches[1] == "*" {
queue = "active"
} else if matches[1] == "!" {
queue = "hold"
}
// Parse the message size. // Parse the message size.
size, err := strconv.ParseFloat(matches[1], 64) size, err := strconv.ParseFloat(matches[2], 64)
if err != nil { if err != nil {
return err return err
} }
@ -94,7 +104,7 @@ func CollectTextualShowqFromReader(file io.Reader, ch chan<- prometheus.Metric)
// output contains no year number. Assume it // output contains no year number. Assume it
// applies to the last year for which the // applies to the last year for which the
// message date doesn't exceed time.Now(). // message date doesn't exceed time.Now().
date, err := time.Parse("Mon Jan 2 15:04:05", matches[2]) date, err := time.Parse("Mon Jan 2 15:04:05", matches[3])
if err != nil { if err != nil {
return err return err
} }
@ -103,13 +113,13 @@ func CollectTextualShowqFromReader(file io.Reader, ch chan<- prometheus.Metric)
date = date.AddDate(-1, 0, 0) date = date.AddDate(-1, 0, 0)
} }
sizeHistogram.Observe(size) sizeHistogram.WithLabelValues(queue).Observe(size)
ageHistogram.Observe(now.Sub(date).Seconds()) ageHistogram.WithLabelValues(queue).Observe(now.Sub(date).Seconds())
} }
} }
ch <- sizeHistogram sizeHistogram.Collect(ch)
ch <- ageHistogram ageHistogram.Collect(ch)
return scanner.Err() return scanner.Err()
} }
@ -133,27 +143,31 @@ func CollectBinaryShowqFromReader(file io.Reader, ch chan<- prometheus.Metric) e
scanner.Split(ScanNullTerminatedEntries) scanner.Split(ScanNullTerminatedEntries)
// Histograms tracking the messages by size and age. // Histograms tracking the messages by size and age.
sizeHistogram := prometheus.NewHistogram( sizeHistogram := prometheus.NewHistogramVec(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Namespace: "postfix", Namespace: "postfix",
Name: "queue_message_size_bytes", Name: "queue_message_size_bytes",
Help: "Size of messages in Postfix's message queue, in bytes", Help: "Size of messages in Postfix's message queue, in bytes",
Buckets: []float64{1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9}, Buckets: []float64{1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9},
}) },
ageHistogram := prometheus.NewHistogram( []string{"queue"})
ageHistogram := prometheus.NewHistogramVec(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Namespace: "postfix", Namespace: "postfix",
Name: "queue_message_age_seconds", Name: "queue_message_age_seconds",
Help: "Age of messages in Postfix's message queue, in seconds", Help: "Age of messages in Postfix's message queue, in seconds",
Buckets: []float64{1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8}, Buckets: []float64{1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8},
}) },
[]string{"queue"})
now := float64(time.Now().UnixNano()) / 1e9 now := float64(time.Now().UnixNano()) / 1e9
queue := "unknown"
for scanner.Scan() { for scanner.Scan() {
// Parse a key/value entry. // Parse a key/value entry.
key := scanner.Text() key := scanner.Text()
if len(key) == 0 { if len(key) == 0 {
// Empty key means a record separator. We don't care. // Empty key means a record separator.
queue := "unknown"
continue continue
} }
if !scanner.Scan() { if !scanner.Scan() {
@ -161,25 +175,28 @@ func CollectBinaryShowqFromReader(file io.Reader, ch chan<- prometheus.Metric) e
} }
value := scanner.Text() value := scanner.Text()
if key == "size" { if key == "queue_name" {
// The name of the message queue.
queue = value
} else if key == "size" {
// Message size in bytes. // Message size in bytes.
size, err := strconv.ParseFloat(value, 64) size, err := strconv.ParseFloat(value, 64)
if err != nil { if err != nil {
return err return err
} }
sizeHistogram.Observe(size) sizeHistogram.WithLabelValues(queue).Observe(size)
} else if key == "time" { } else if key == "time" {
// Message time as a UNIX timestamp. // Message time as a UNIX timestamp.
time, err := strconv.ParseFloat(value, 64) time, err := strconv.ParseFloat(value, 64)
if err != nil { if err != nil {
return err return err
} }
ageHistogram.Observe(now - time) ageHistogram.WithLabelValues(queue).Observe(now - time)
} }
} }
ch <- sizeHistogram sizeHistogram.Collect(ch)
ch <- ageHistogram ageHistogram.Collect(ch)
return scanner.Err() return scanner.Err()
} }