Skip to content

Commit

Permalink
MQTT Consumer ServiceInput plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
sparrc committed Feb 4, 2016
1 parent f01da8f commit 6298d2a
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 16 deletions.
1 change: 1 addition & 0 deletions plugins/inputs/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/mailchimp"
_ "github.com/influxdata/telegraf/plugins/inputs/memcached"
_ "github.com/influxdata/telegraf/plugins/inputs/mongodb"
_ "github.com/influxdata/telegraf/plugins/inputs/mqtt_consumer"
_ "github.com/influxdata/telegraf/plugins/inputs/mysql"
_ "github.com/influxdata/telegraf/plugins/inputs/nginx"
_ "github.com/influxdata/telegraf/plugins/inputs/nsq"
Expand Down
31 changes: 18 additions & 13 deletions plugins/inputs/kafka_consumer/kafka_consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ type Kafka struct {
Topics []string
ZookeeperPeers []string
Consumer *consumergroup.ConsumerGroup
PointBuffer int
Offset string
MetricBuffer int
// TODO remove PointBuffer, legacy support
PointBuffer int
Offset string

sync.Mutex

// channel for all incoming kafka messages
in <-chan *sarama.ConsumerMessage
// channel for all kafka consumer errors
errs <-chan *sarama.ConsumerError
// channel for all incoming parsed kafka points
// channel for all incoming parsed kafka metrics
metricC chan telegraf.Metric
done chan struct{}

Expand All @@ -42,8 +44,8 @@ var sampleConfig = `
zookeeper_peers = ["localhost:2181"]
# the name of the consumer group
consumer_group = "telegraf_metrics_consumers"
# Maximum number of points to buffer between collection intervals
point_buffer = 100000
# Maximum number of metrics to buffer between collection intervals
metric_buffer = 100000
# Offset (must be either "oldest" or "newest")
offset = "oldest"
`
Expand Down Expand Up @@ -90,10 +92,13 @@ func (k *Kafka) Start() error {
}

k.done = make(chan struct{})
if k.PointBuffer == 0 {
k.PointBuffer = 100000
if k.PointBuffer == 0 && k.MetricBuffer == 0 {
k.MetricBuffer = 100000
} else if k.PointBuffer > 0 {
// Legacy support of PointBuffer field TODO remove
k.MetricBuffer = k.PointBuffer
}
k.metricC = make(chan telegraf.Metric, k.PointBuffer)
k.metricC = make(chan telegraf.Metric, k.MetricBuffer)

// Start the kafka message reader
go k.parser()
Expand Down Expand Up @@ -124,7 +129,7 @@ func (k *Kafka) parser() {
continue
default:
log.Printf("Kafka Consumer buffer is full, dropping a metric." +
" You may want to increase the point_buffer setting")
" You may want to increase the metric_buffer setting")
}
}

Expand All @@ -151,10 +156,10 @@ func (k *Kafka) Stop() {
func (k *Kafka) Gather(acc telegraf.Accumulator) error {
k.Lock()
defer k.Unlock()
npoints := len(k.metricC)
for i := 0; i < npoints; i++ {
point := <-k.metricC
acc.AddFields(point.Name(), point.Fields(), point.Tags(), point.Time())
nmetrics := len(k.metricC)
for i := 0; i < nmetrics; i++ {
metric := <-k.metricC
acc.AddFields(metric.Name(), metric.Fields(), metric.Tags(), metric.Time())
}
return nil
}
Expand Down
39 changes: 39 additions & 0 deletions plugins/inputs/mqtt_consumer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# mqtt_consumer Input Plugin

The example plugin gathers metrics about example things

### Configuration:

```
# Description
[[inputs.example]]
# SampleConfig
```

### Measurements & Fields:

<optional description>

- measurement1
- field1 (type, unit)
- field2 (float, percent)
- measurement2
- field3 (integer, bytes)

### Tags:

- All measurements have the following tags:
- tag1 (optional description)
- tag2
- measurement2 has the following tags:
- tag3

### Example Output:

Give an example `-test` output here

```
$ ./telegraf -config telegraf.conf -input-filter example -test
measurement1,tag1=foo,tag2=bar field1=1i,field2=2.1 1453831884664956455
measurement2,tag1=foo,tag2=bar,tag3=baz field3=1i 1453831884664956455
```
201 changes: 201 additions & 0 deletions plugins/inputs/mqtt_consumer/mqtt_consumer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package mqtt_consumer

import (
"fmt"
"log"
"sync"
"time"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"

"git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
)

type MQTTConsumer struct {
Servers []string
Topics []string
Username string
Password string
MetricBuffer int

// Path to CA file
SSLCA string `toml:"ssl_ca"`
// Path to host cert file
SSLCert string `toml:"ssl_cert"`
// Path to cert key file
SSLKey string `toml:"ssl_key"`
// Use SSL but skip chain & host verification
InsecureSkipVerify bool

sync.Mutex
client *mqtt.Client
// channel for all incoming parsed mqtt metrics
metricC chan telegraf.Metric
done chan struct{}
in chan []byte
}

var sampleConfig = `
servers = ["localhost:1883"]
### Topics to subscribe to
topics = [
"telegraf/host01/cpu",
"telegraf/host02/mem",
]
### Maximum number of metrics to buffer between collection intervals
metric_buffer = 100000
### username and password to connect MQTT server.
# username = "telegraf"
# password = "metricsmetricsmetricsmetrics"
### Optional SSL Config
# ssl_ca = "/etc/telegraf/ca.pem"
# ssl_cert = "/etc/telegraf/cert.pem"
# ssl_key = "/etc/telegraf/key.pem"
### Use SSL but skip chain & host verification
# insecure_skip_verify = false
`

func (m *MQTTConsumer) SampleConfig() string {
return sampleConfig
}

func (m *MQTTConsumer) Description() string {
return "Read line-protocol metrics from MQTT topic(s)"
}

func (m *MQTTConsumer) Start() error {
m.Lock()
defer m.Unlock()

opts, err := m.createOpts()
if err != nil {
return err
}

m.client = mqtt.NewClient(opts)
if token := m.client.Connect(); token.Wait() && token.Error() != nil {
return token.Error()
}

m.in = make(chan []byte, m.MetricBuffer)
m.done = make(chan struct{})
if m.MetricBuffer == 0 {
m.MetricBuffer = 100000
}
m.metricC = make(chan telegraf.Metric, m.MetricBuffer)

topics := make(map[string]byte)
for _, topic := range m.Topics {
topics[topic] = byte(0)
}
subscribeToken := m.client.SubscribeMultiple(topics, m.recvMessage)
subscribeToken.Wait()
if subscribeToken.Error() != nil {
return subscribeToken.Error()
}

go m.parser()

return nil
}

func (m *MQTTConsumer) parser() {
for {
select {
case <-m.done:
return
case msg := <-m.in:
metrics, err := telegraf.ParseMetrics(msg)
if err != nil {
log.Printf("Could not parse MQTT message: %s, error: %s",
string(msg), err.Error())
}

for _, metric := range metrics {
select {
case m.metricC <- metric:
continue
default:
log.Printf("MQTT Consumer buffer is full, dropping a metric." +
" You may want to increase the metric_buffer setting")
}
}
}
}
}

func (m *MQTTConsumer) recvMessage(_ *mqtt.Client, msg mqtt.Message) {
m.in <- msg.Payload()
}

func (m *MQTTConsumer) Stop() {
m.Lock()
defer m.Unlock()
close(m.done)
m.client.Disconnect(200)
}

func (m *MQTTConsumer) Gather(acc telegraf.Accumulator) error {
m.Lock()
defer m.Unlock()
nmetrics := len(m.metricC)
for i := 0; i < nmetrics; i++ {
metric := <-m.metricC
acc.AddFields(metric.Name(), metric.Fields(), metric.Tags(), metric.Time())
}
return nil
}

func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
opts := mqtt.NewClientOptions()

opts.SetClientID("Telegraf-Consumer-" + internal.RandomString(5))

tlsCfg, err := internal.GetTLSConfig(
m.SSLCert, m.SSLKey, m.SSLCA, m.InsecureSkipVerify)
if err != nil {
return nil, err
}

scheme := "tcp"
if tlsCfg != nil {
scheme = "ssl"
opts.SetTLSConfig(tlsCfg)
}

user := m.Username
if user == "" {
opts.SetUsername(user)
}
password := m.Password
if password != "" {
opts.SetPassword(password)
}

if len(m.Servers) == 0 {
return opts, fmt.Errorf("could not get host infomations")
}
for _, host := range m.Servers {
server := fmt.Sprintf("%s://%s", scheme, host)

opts.AddBroker(server)
}
opts.SetAutoReconnect(true)
// Setting KeepAlive to 0 disables it.
// TODO set KeepAlive to a real value (60s?) when this change is merged:
// https://git.eclipse.org/r/#/c/65850/
opts.SetKeepAlive(time.Duration(0))
return opts, nil
}

func init() {
inputs.Add("mqtt_consumer", func() telegraf.Input {
return &MQTTConsumer{}
})
}
1 change: 1 addition & 0 deletions plugins/inputs/mqtt_consumer/mqtt_consumer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package mqtt_consumer
3 changes: 0 additions & 3 deletions plugins/outputs/mqtt/mqtt.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import (
"github.com/influxdata/telegraf/plugins/outputs"
)

const MaxRetryCount = 3
const ClientIdPrefix = "telegraf"

type MQTT struct {
Servers []string `toml:"servers"`
Username string
Expand Down

0 comments on commit 6298d2a

Please sign in to comment.