telegraf/plugins/inputs/apache/apache.go

236 lines
5.0 KiB
Go
Raw Normal View History

2015-08-27 09:24:26 +00:00
package apache
import (
"bufio"
"fmt"
"net"
"net/http"
"net/url"
"strconv"
"strings"
2017-04-24 18:13:26 +00:00
"sync"
2015-08-27 09:24:26 +00:00
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
2016-01-20 18:57:35 +00:00
"github.com/influxdata/telegraf/plugins/inputs"
2015-08-27 09:24:26 +00:00
)
type Apache struct {
Urls []string
Username string
Password string
ResponseTimeout internal.Duration
// 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
client *http.Client
2015-08-27 09:24:26 +00:00
}
var sampleConfig = `
2017-08-12 00:50:51 +00:00
## An array of URLs to gather from, must be directed at the machine
## readable version of the mod_status page including the auto query string.
## Default is "http://localhost/server-status?auto".
urls = ["http://localhost/server-status?auto"]
2017-08-12 00:50:51 +00:00
## Credentials for basic HTTP authentication.
# username = "myuser"
# password = "mypassword"
## Maximum time to receive response.
# response_timeout = "5s"
## 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
2015-08-27 09:24:26 +00:00
`
func (n *Apache) SampleConfig() string {
return sampleConfig
}
func (n *Apache) Description() string {
return "Read Apache status information (mod_status)"
}
func (n *Apache) Gather(acc telegraf.Accumulator) error {
if len(n.Urls) == 0 {
n.Urls = []string{"http://localhost/server-status?auto"}
}
if n.ResponseTimeout.Duration < time.Second {
n.ResponseTimeout.Duration = time.Second * 5
}
if n.client == nil {
client, err := n.createHttpClient()
if err != nil {
return err
}
n.client = client
}
2017-04-24 18:13:26 +00:00
var wg sync.WaitGroup
wg.Add(len(n.Urls))
2015-08-27 09:24:26 +00:00
for _, u := range n.Urls {
addr, err := url.Parse(u)
if err != nil {
2017-04-24 18:13:26 +00:00
acc.AddError(fmt.Errorf("Unable to parse address '%s': %s", u, err))
continue
2015-08-27 09:24:26 +00:00
}
go func(addr *url.URL) {
2017-04-24 18:13:26 +00:00
defer wg.Done()
acc.AddError(n.gatherUrl(addr, acc))
2015-08-27 09:24:26 +00:00
}(addr)
}
2017-04-24 18:13:26 +00:00
wg.Wait()
return nil
2015-08-27 09:24:26 +00:00
}
func (n *Apache) createHttpClient() (*http.Client, error) {
tlsCfg, err := internal.GetTLSConfig(
n.SSLCert, n.SSLKey, n.SSLCA, n.InsecureSkipVerify)
if err != nil {
return nil, err
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsCfg,
},
Timeout: n.ResponseTimeout.Duration,
}
return client, nil
}
func (n *Apache) gatherUrl(addr *url.URL, acc telegraf.Accumulator) error {
req, err := http.NewRequest("GET", addr.String(), nil)
if err != nil {
return fmt.Errorf("error on new request to %s : %s\n", addr.String(), err)
}
if len(n.Username) != 0 && len(n.Password) != 0 {
req.SetBasicAuth(n.Username, n.Password)
}
resp, err := n.client.Do(req)
2015-08-27 09:24:26 +00:00
if err != nil {
return fmt.Errorf("error on request to %s : %s\n", addr.String(), err)
2015-08-27 09:24:26 +00:00
}
defer resp.Body.Close()
2015-08-27 09:24:26 +00:00
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%s returned HTTP status %s", addr.String(), resp.Status)
}
2015-08-27 09:24:26 +00:00
tags := getTags(addr)
2015-08-27 09:24:26 +00:00
sc := bufio.NewScanner(resp.Body)
fields := make(map[string]interface{})
2015-08-27 09:24:26 +00:00
for sc.Scan() {
line := sc.Text()
if strings.Contains(line, ":") {
parts := strings.SplitN(line, ":", 2)
key, part := strings.Replace(parts[0], " ", "", -1), strings.TrimSpace(parts[1])
switch key {
case "Scoreboard":
for field, value := range n.gatherScores(part) {
fields[field] = value
}
default:
2015-11-13 19:51:37 +00:00
value, err := strconv.ParseFloat(part, 64)
if err != nil {
continue
}
fields[key] = value
}
}
2015-08-27 09:24:26 +00:00
}
acc.AddFields("apache", fields, tags)
2015-08-27 09:24:26 +00:00
return nil
}
func (n *Apache) gatherScores(data string) map[string]interface{} {
var waiting, open int = 0, 0
var S, R, W, K, D, C, L, G, I int = 0, 0, 0, 0, 0, 0, 0, 0, 0
for _, s := range strings.Split(data, "") {
switch s {
case "_":
waiting++
case "S":
S++
case "R":
R++
case "W":
W++
case "K":
K++
case "D":
D++
case "C":
C++
case "L":
L++
case "G":
G++
case "I":
I++
case ".":
open++
}
}
fields := map[string]interface{}{
"scboard_waiting": float64(waiting),
"scboard_starting": float64(S),
"scboard_reading": float64(R),
"scboard_sending": float64(W),
"scboard_keepalive": float64(K),
"scboard_dnslookup": float64(D),
"scboard_closing": float64(C),
"scboard_logging": float64(L),
"scboard_finishing": float64(G),
"scboard_idle_cleanup": float64(I),
"scboard_open": float64(open),
}
return fields
2015-08-27 09:24:26 +00:00
}
// Get tag(s) for the apache plugin
func getTags(addr *url.URL) map[string]string {
h := addr.Host
host, port, err := net.SplitHostPort(h)
if err != nil {
host = addr.Host
if addr.Scheme == "http" {
port = "80"
} else if addr.Scheme == "https" {
port = "443"
} else {
port = ""
}
2015-08-27 09:24:26 +00:00
}
return map[string]string{"server": host, "port": port}
2015-08-27 09:24:26 +00:00
}
func init() {
inputs.Add("apache", func() telegraf.Input {
2015-08-27 09:24:26 +00:00
return &Apache{}
})
}