forked from influxdata/telegraf
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add postfix input plugin (influxdata#2553)
- Loading branch information
1 parent
06e22ee
commit 76240b9
Showing
5 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Postfix Input Plugin | ||
|
||
The postfix plugin reports metrics on the postfix queues. | ||
|
||
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). | ||
|
||
### Configuration | ||
|
||
```toml | ||
[[inputs.postfix]] | ||
## Postfix queue directory. If not provided, telegraf will try to use | ||
## 'postconf -h queue_directory' to determine it. | ||
# queue_directory = "/var/spool/postfix" | ||
``` | ||
|
||
### Measurements & Fields: | ||
|
||
- postfix_queue | ||
- length (integer) | ||
- size (integer, bytes) | ||
- age (integer, seconds) | ||
|
||
### Tags: | ||
|
||
- postfix_queue | ||
- queue | ||
|
||
### Example Output | ||
|
||
``` | ||
postfix_queue,queue=active length=3,size=12345,age=9 | ||
postfix_queue,queue=hold length=0,size=0,age=0 | ||
postfix_queue,queue=maildrop length=1,size=2000,age=2 | ||
postfix_queue,queue=incoming length=1,size=1020,age=0 | ||
postfix_queue,queue=deferred length=400,size=76543210,age=3600 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package postfix | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path" | ||
"strings" | ||
"time" | ||
|
||
"github.com/influxdata/telegraf" | ||
"github.com/influxdata/telegraf/plugins/inputs" | ||
) | ||
|
||
const sampleConfig = ` | ||
## Postfix queue directory. If not provided, telegraf will try to use | ||
## 'postconf -h queue_directory' to determine it. | ||
# queue_directory = "/var/spool/postfix" | ||
` | ||
|
||
const description = "Measure postfix queue statistics" | ||
|
||
func getQueueDirectory() (string, error) { | ||
qd, err := exec.Command("postconf", "-h", "queue_directory").Output() | ||
if err != nil { | ||
return "", err | ||
} | ||
return strings.TrimSpace(string(qd)), nil | ||
} | ||
|
||
func qScan(path string) (int64, int64, int64, error) { | ||
f, err := os.Open(path) | ||
if err != nil { | ||
return 0, 0, 0, err | ||
} | ||
|
||
finfos, err := f.Readdir(-1) | ||
f.Close() | ||
if err != nil { | ||
return 0, 0, 0, err | ||
} | ||
|
||
var length, size int64 | ||
var oldest time.Time | ||
for _, finfo := range finfos { | ||
length++ | ||
size += finfo.Size() | ||
if oldest.IsZero() || finfo.ModTime().Before(oldest) { | ||
oldest = finfo.ModTime() | ||
} | ||
} | ||
var age time.Duration | ||
if !oldest.IsZero() { | ||
age = time.Now().Sub(oldest) / time.Second | ||
} | ||
return length, size, int64(age), nil | ||
} | ||
|
||
type Postfix struct { | ||
QueueDirectory string | ||
} | ||
|
||
func (p *Postfix) Gather(acc telegraf.Accumulator) error { | ||
if p.QueueDirectory == "" { | ||
var err error | ||
p.QueueDirectory, err = getQueueDirectory() | ||
if err != nil { | ||
return fmt.Errorf("unable to determine queue directory: %s", err) | ||
} | ||
} | ||
|
||
for _, q := range []string{"active", "hold", "incoming", "maildrop"} { | ||
length, size, age, err := qScan(path.Join(p.QueueDirectory, q)) | ||
if err != nil { | ||
acc.AddError(fmt.Errorf("error scanning queue %s: %s", q, err)) | ||
continue | ||
} | ||
fields := map[string]interface{}{"length": length, "size": size, "age": age} | ||
acc.AddFields("postfix_queue", fields, map[string]string{"queue": q}) | ||
} | ||
|
||
var dLength, dSize, dAge int64 | ||
for _, q := range []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} { | ||
length, size, age, err := qScan(path.Join(p.QueueDirectory, "deferred", q)) | ||
if err != nil { | ||
if os.IsNotExist(err) { | ||
// the directories are created on first use | ||
continue | ||
} | ||
acc.AddError(fmt.Errorf("error scanning queue deferred/%s: %s", q, err)) | ||
return nil | ||
} | ||
dLength += length | ||
dSize += size | ||
if age > dAge { | ||
dAge = age | ||
} | ||
} | ||
fields := map[string]interface{}{"length": dLength, "size": dSize, "age": dAge} | ||
acc.AddFields("postfix_queue", fields, map[string]string{"queue": "deferred"}) | ||
|
||
return nil | ||
} | ||
|
||
func (p *Postfix) SampleConfig() string { | ||
return sampleConfig | ||
} | ||
|
||
func (p *Postfix) Description() string { | ||
return description | ||
} | ||
|
||
func init() { | ||
inputs.Add("postfix", func() telegraf.Input { | ||
return &Postfix{ | ||
QueueDirectory: "/var/spool/postfix", | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package postfix | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"path" | ||
"testing" | ||
"time" | ||
|
||
"github.com/influxdata/telegraf/testutil" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestGather(t *testing.T) { | ||
td, err := ioutil.TempDir("", "") | ||
require.NoError(t, err) | ||
defer os.RemoveAll(td) | ||
|
||
for _, q := range []string{"active", "hold", "incoming", "maildrop", "deferred"} { | ||
require.NoError(t, os.Mkdir(path.Join(td, q), 0755)) | ||
} | ||
for _, q := range []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "F"} { // "E" deliberately left off | ||
require.NoError(t, os.Mkdir(path.Join(td, "deferred", q), 0755)) | ||
} | ||
|
||
require.NoError(t, ioutil.WriteFile(path.Join(td, "active", "01"), []byte("abc"), 0644)) | ||
require.NoError(t, ioutil.WriteFile(path.Join(td, "active", "02"), []byte("defg"), 0644)) | ||
require.NoError(t, os.Chtimes(path.Join(td, "active", "02"), time.Now(), time.Now().Add(-time.Hour))) | ||
require.NoError(t, ioutil.WriteFile(path.Join(td, "hold", "01"), []byte("abc"), 0644)) | ||
require.NoError(t, ioutil.WriteFile(path.Join(td, "incoming", "01"), []byte("abcd"), 0644)) | ||
require.NoError(t, ioutil.WriteFile(path.Join(td, "deferred", "0", "01"), []byte("abc"), 0644)) | ||
require.NoError(t, ioutil.WriteFile(path.Join(td, "deferred", "F", "F1"), []byte("abc"), 0644)) | ||
|
||
p := Postfix{ | ||
QueueDirectory: td, | ||
} | ||
|
||
var acc testutil.Accumulator | ||
require.NoError(t, p.Gather(&acc)) | ||
|
||
metrics := map[string]*testutil.Metric{} | ||
for _, m := range acc.Metrics { | ||
metrics[m.Tags["queue"]] = m | ||
} | ||
|
||
assert.Equal(t, int64(2), metrics["active"].Fields["length"]) | ||
assert.Equal(t, int64(7), metrics["active"].Fields["size"]) | ||
assert.InDelta(t, int64(time.Hour/time.Second), metrics["active"].Fields["age"], 10) | ||
|
||
assert.Equal(t, int64(1), metrics["hold"].Fields["length"]) | ||
assert.Equal(t, int64(3), metrics["hold"].Fields["size"]) | ||
|
||
assert.Equal(t, int64(1), metrics["incoming"].Fields["length"]) | ||
assert.Equal(t, int64(4), metrics["incoming"].Fields["size"]) | ||
|
||
assert.Equal(t, int64(0), metrics["maildrop"].Fields["length"]) | ||
assert.Equal(t, int64(0), metrics["maildrop"].Fields["size"]) | ||
assert.Equal(t, int64(0), metrics["maildrop"].Fields["age"]) | ||
|
||
assert.Equal(t, int64(2), metrics["deferred"].Fields["length"]) | ||
assert.Equal(t, int64(6), metrics["deferred"].Fields["size"]) | ||
} |