Skip to content

Commit 2ae3559

Browse files
phemmerdanielnelson
authored andcommitted
Add postfix input plugin (#2553)
1 parent 2681be7 commit 2ae3559

File tree

5 files changed

+220
-0
lines changed

5 files changed

+220
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ configuration options.
180180
* [phpfpm](./plugins/inputs/phpfpm)
181181
* [phusion passenger](./plugins/inputs/passenger)
182182
* [ping](./plugins/inputs/ping)
183+
* [postfix](./plugins/inputs/postfix)
183184
* [postgresql](./plugins/inputs/postgresql)
184185
* [postgresql_extensible](./plugins/inputs/postgresql_extensible)
185186
* [powerdns](./plugins/inputs/powerdns)

plugins/inputs/all/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import (
6464
_ "github.com/influxdata/telegraf/plugins/inputs/passenger"
6565
_ "github.com/influxdata/telegraf/plugins/inputs/phpfpm"
6666
_ "github.com/influxdata/telegraf/plugins/inputs/ping"
67+
_ "github.com/influxdata/telegraf/plugins/inputs/postfix"
6768
_ "github.com/influxdata/telegraf/plugins/inputs/postgresql"
6869
_ "github.com/influxdata/telegraf/plugins/inputs/postgresql_extensible"
6970
_ "github.com/influxdata/telegraf/plugins/inputs/powerdns"

plugins/inputs/postfix/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Postfix Input Plugin
2+
3+
The postfix plugin reports metrics on the postfix queues.
4+
5+
For each of the active, hold, incoming, maildrop, and deferred queues (http://www.postfix.org/QSHAPE_README.html#queues), it will report the queue length (number of items), size (bytes used by items), and age (age of oldest item in seconds).
6+
7+
### Configuration
8+
9+
```toml
10+
[[inputs.postfix]]
11+
## Postfix queue directory. If not provided, telegraf will try to use
12+
## 'postconf -h queue_directory' to determine it.
13+
# queue_directory = "/var/spool/postfix"
14+
```
15+
16+
### Measurements & Fields:
17+
18+
- postfix_queue
19+
- length (integer)
20+
- size (integer, bytes)
21+
- age (integer, seconds)
22+
23+
### Tags:
24+
25+
- postfix_queue
26+
- queue
27+
28+
### Example Output
29+
30+
```
31+
postfix_queue,queue=active length=3,size=12345,age=9
32+
postfix_queue,queue=hold length=0,size=0,age=0
33+
postfix_queue,queue=maildrop length=1,size=2000,age=2
34+
postfix_queue,queue=incoming length=1,size=1020,age=0
35+
postfix_queue,queue=deferred length=400,size=76543210,age=3600
36+
```

plugins/inputs/postfix/postfix.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package postfix
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path"
8+
"strings"
9+
"time"
10+
11+
"github.com/influxdata/telegraf"
12+
"github.com/influxdata/telegraf/plugins/inputs"
13+
)
14+
15+
const sampleConfig = `
16+
## Postfix queue directory. If not provided, telegraf will try to use
17+
## 'postconf -h queue_directory' to determine it.
18+
# queue_directory = "/var/spool/postfix"
19+
`
20+
21+
const description = "Measure postfix queue statistics"
22+
23+
func getQueueDirectory() (string, error) {
24+
qd, err := exec.Command("postconf", "-h", "queue_directory").Output()
25+
if err != nil {
26+
return "", err
27+
}
28+
return strings.TrimSpace(string(qd)), nil
29+
}
30+
31+
func qScan(path string) (int64, int64, int64, error) {
32+
f, err := os.Open(path)
33+
if err != nil {
34+
return 0, 0, 0, err
35+
}
36+
37+
finfos, err := f.Readdir(-1)
38+
f.Close()
39+
if err != nil {
40+
return 0, 0, 0, err
41+
}
42+
43+
var length, size int64
44+
var oldest time.Time
45+
for _, finfo := range finfos {
46+
length++
47+
size += finfo.Size()
48+
if oldest.IsZero() || finfo.ModTime().Before(oldest) {
49+
oldest = finfo.ModTime()
50+
}
51+
}
52+
var age time.Duration
53+
if !oldest.IsZero() {
54+
age = time.Now().Sub(oldest) / time.Second
55+
}
56+
return length, size, int64(age), nil
57+
}
58+
59+
type Postfix struct {
60+
QueueDirectory string
61+
}
62+
63+
func (p *Postfix) Gather(acc telegraf.Accumulator) error {
64+
if p.QueueDirectory == "" {
65+
var err error
66+
p.QueueDirectory, err = getQueueDirectory()
67+
if err != nil {
68+
return fmt.Errorf("unable to determine queue directory: %s", err)
69+
}
70+
}
71+
72+
for _, q := range []string{"active", "hold", "incoming", "maildrop"} {
73+
length, size, age, err := qScan(path.Join(p.QueueDirectory, q))
74+
if err != nil {
75+
acc.AddError(fmt.Errorf("error scanning queue %s: %s", q, err))
76+
continue
77+
}
78+
fields := map[string]interface{}{"length": length, "size": size, "age": age}
79+
acc.AddFields("postfix_queue", fields, map[string]string{"queue": q})
80+
}
81+
82+
var dLength, dSize, dAge int64
83+
for _, q := range []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} {
84+
length, size, age, err := qScan(path.Join(p.QueueDirectory, "deferred", q))
85+
if err != nil {
86+
if os.IsNotExist(err) {
87+
// the directories are created on first use
88+
continue
89+
}
90+
acc.AddError(fmt.Errorf("error scanning queue deferred/%s: %s", q, err))
91+
return nil
92+
}
93+
dLength += length
94+
dSize += size
95+
if age > dAge {
96+
dAge = age
97+
}
98+
}
99+
fields := map[string]interface{}{"length": dLength, "size": dSize, "age": dAge}
100+
acc.AddFields("postfix_queue", fields, map[string]string{"queue": "deferred"})
101+
102+
return nil
103+
}
104+
105+
func (p *Postfix) SampleConfig() string {
106+
return sampleConfig
107+
}
108+
109+
func (p *Postfix) Description() string {
110+
return description
111+
}
112+
113+
func init() {
114+
inputs.Add("postfix", func() telegraf.Input {
115+
return &Postfix{
116+
QueueDirectory: "/var/spool/postfix",
117+
}
118+
})
119+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package postfix
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"path"
7+
"testing"
8+
"time"
9+
10+
"github.com/influxdata/telegraf/testutil"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestGather(t *testing.T) {
16+
td, err := ioutil.TempDir("", "")
17+
require.NoError(t, err)
18+
defer os.RemoveAll(td)
19+
20+
for _, q := range []string{"active", "hold", "incoming", "maildrop", "deferred"} {
21+
require.NoError(t, os.Mkdir(path.Join(td, q), 0755))
22+
}
23+
for _, q := range []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "F"} { // "E" deliberately left off
24+
require.NoError(t, os.Mkdir(path.Join(td, "deferred", q), 0755))
25+
}
26+
27+
require.NoError(t, ioutil.WriteFile(path.Join(td, "active", "01"), []byte("abc"), 0644))
28+
require.NoError(t, ioutil.WriteFile(path.Join(td, "active", "02"), []byte("defg"), 0644))
29+
require.NoError(t, os.Chtimes(path.Join(td, "active", "02"), time.Now(), time.Now().Add(-time.Hour)))
30+
require.NoError(t, ioutil.WriteFile(path.Join(td, "hold", "01"), []byte("abc"), 0644))
31+
require.NoError(t, ioutil.WriteFile(path.Join(td, "incoming", "01"), []byte("abcd"), 0644))
32+
require.NoError(t, ioutil.WriteFile(path.Join(td, "deferred", "0", "01"), []byte("abc"), 0644))
33+
require.NoError(t, ioutil.WriteFile(path.Join(td, "deferred", "F", "F1"), []byte("abc"), 0644))
34+
35+
p := Postfix{
36+
QueueDirectory: td,
37+
}
38+
39+
var acc testutil.Accumulator
40+
require.NoError(t, p.Gather(&acc))
41+
42+
metrics := map[string]*testutil.Metric{}
43+
for _, m := range acc.Metrics {
44+
metrics[m.Tags["queue"]] = m
45+
}
46+
47+
assert.Equal(t, int64(2), metrics["active"].Fields["length"])
48+
assert.Equal(t, int64(7), metrics["active"].Fields["size"])
49+
assert.InDelta(t, int64(time.Hour/time.Second), metrics["active"].Fields["age"], 10)
50+
51+
assert.Equal(t, int64(1), metrics["hold"].Fields["length"])
52+
assert.Equal(t, int64(3), metrics["hold"].Fields["size"])
53+
54+
assert.Equal(t, int64(1), metrics["incoming"].Fields["length"])
55+
assert.Equal(t, int64(4), metrics["incoming"].Fields["size"])
56+
57+
assert.Equal(t, int64(0), metrics["maildrop"].Fields["length"])
58+
assert.Equal(t, int64(0), metrics["maildrop"].Fields["size"])
59+
assert.Equal(t, int64(0), metrics["maildrop"].Fields["age"])
60+
61+
assert.Equal(t, int64(2), metrics["deferred"].Fields["length"])
62+
assert.Equal(t, int64(6), metrics["deferred"].Fields["size"])
63+
}

0 commit comments

Comments
 (0)