git » chasquid » commit c4e8b22

Introduce expvar counters

author Alberto Bertogli
2016-10-08 11:36:48 UTC
committer Alberto Bertogli
2016-10-09 23:51:05 UTC
parent 641406cede9a4e9fb967c08650610a65f28a69b3

Introduce expvar counters

This patch introduces expvar counters to chasquid and the queue
packages.

For now there's only a handful of counters, but they will be expanded in
future patches.

chasquid.go +14 -0
internal/courier/smtp.go +10 -0
internal/queue/queue.go +15 -0

diff --git a/chasquid.go b/chasquid.go
index ae5b261..a5ecbe2 100644
--- a/chasquid.go
+++ b/chasquid.go
@@ -3,6 +3,7 @@ package main
 import (
 	"bytes"
 	"crypto/tls"
+	"expvar"
 	"flag"
 	"fmt"
 	"io"
@@ -14,6 +15,7 @@ import (
 	"os"
 	"os/signal"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"syscall"
 	"time"
@@ -37,11 +39,19 @@ import (
 	"github.com/golang/glog"
 )
 
+// Command-line flags.
 var (
 	configDir = flag.String("config_dir", "/etc/chasquid",
 		"configuration directory")
 )
 
+// Exported variables.
+var (
+	commandCount      = expvar.NewMap("chasquid/smtpIn/commandCount")
+	responseCodeCount = expvar.NewMap("chasquid/smtpIn/responseCodeCount")
+	spfResultCount    = expvar.NewMap("chasquid/smtpIn/spfResultCount")
+)
+
 func main() {
 	flag.Parse()
 
@@ -476,6 +486,7 @@ loop:
 			break
 		}
 
+		commandCount.Add(cmd, 1)
 		if cmd == "AUTH" {
 			c.tr.Debugf("-> AUTH <redacted>")
 		} else {
@@ -642,6 +653,8 @@ func (c *Conn) MAIL(params string) (code int, msg string) {
 			c.spfResult, c.spfError = spf.CheckHost(
 				tcp.IP, envelope.DomainOf(e.Address))
 			c.tr.Debugf("SPF %v (%v)", c.spfResult, c.spfError)
+			spfResultCount.Add(string(c.spfResult), 1)
+
 			// https://tools.ietf.org/html/rfc7208#section-8
 			// We opt not to fail on errors, to avoid accidents to prevent
 			// delivery.
@@ -964,6 +977,7 @@ func (c *Conn) readLine() (line string, err error) {
 func (c *Conn) writeResponse(code int, msg string) error {
 	defer c.tc.W.Flush()
 
+	responseCodeCount.Add(strconv.Itoa(code), 1)
 	return writeResponse(c.tc.W, code, msg)
 }
 
diff --git a/internal/courier/smtp.go b/internal/courier/smtp.go
index 6e3b916..f57faa7 100644
--- a/internal/courier/smtp.go
+++ b/internal/courier/smtp.go
@@ -2,6 +2,7 @@ package courier
 
 import (
 	"crypto/tls"
+	"expvar"
 	"flag"
 	"net"
 	"os"
@@ -29,6 +30,11 @@ var (
 	fakeMX = map[string]string{}
 )
 
+// Exported variables.
+var (
+	tlsCount = expvar.NewMap("chasquid/smtpOut/tlsCount")
+)
+
 // SMTP delivers remote mail via outgoing SMTP.
 type SMTP struct {
 }
@@ -91,6 +97,7 @@ retry:
 			// Unfortunately, many servers use self-signed certs, so if we
 			// fail verification we just try again without validating.
 			if insecure {
+				tlsCount.Add("tls:failed", 1)
 				return tr.Errorf("TLS error: %v", err), false
 			}
 
@@ -101,10 +108,13 @@ retry:
 
 		if config.InsecureSkipVerify {
 			tr.Debugf("Insecure - using TLS, but cert does not match %s", mx)
+			tlsCount.Add("tls:insecure", 1)
 		} else {
+			tlsCount.Add("tls:secure", 1)
 			tr.Debugf("Secure - using TLS")
 		}
 	} else {
+		tlsCount.Add("plain", 1)
 		tr.Debugf("Insecure - NOT using TLS")
 	}
 
diff --git a/internal/queue/queue.go b/internal/queue/queue.go
index 363ff78..dd043d7 100644
--- a/internal/queue/queue.go
+++ b/internal/queue/queue.go
@@ -9,6 +9,7 @@ import (
 	"context"
 	"crypto/rand"
 	"encoding/base64"
+	"expvar"
 	"fmt"
 	"os"
 	"os/exec"
@@ -51,6 +52,14 @@ var (
 	errQueueFull = fmt.Errorf("Queue size too big, try again later")
 )
 
+// Exported variables.
+var (
+	putCount        = expvar.NewInt("chasquid/queue/putCount")
+	itemsWritten    = expvar.NewInt("chasquid/queue/itemsWritten")
+	dsnQueued       = expvar.NewInt("chasquid/queue/dsnQueued")
+	deliverAttempts = expvar.NewMap("chasquid/queue/deliverAttempts")
+)
+
 // Channel used to get random IDs for items in the queue.
 var newID chan string
 
@@ -148,6 +157,7 @@ func (q *Queue) Put(hostname, from string, to []string, data []byte) (string, er
 	if q.Len() >= maxQueueSize {
 		return "", errQueueFull
 	}
+	putCount.Add(1)
 
 	item := &Item{
 		Message: Message{
@@ -272,6 +282,7 @@ func ItemFromFile(fname string) (*Item, error) {
 func (item *Item) WriteTo(dir string) error {
 	item.Lock()
 	defer item.Unlock()
+	itemsWritten.Add(1)
 
 	var err error
 	item.CreatedAtTs, err = ptypes.TimestampProto(item.CreatedAt)
@@ -394,12 +405,14 @@ func sendDSN(tr *trace.Trace, q *Queue, item *Item) {
 	}
 
 	tr.Printf("queued DSN: %s", id)
+	dsnQueued.Add(1)
 }
 
 // deliver the item to the given recipient, using the couriers from the queue.
 // Return an error (if any), and whether it is permanent or not.
 func (item *Item) deliver(q *Queue, rcpt *Recipient) (err error, permanent bool) {
 	if rcpt.Type == Recipient_PIPE {
+		deliverAttempts.Add("pipe", 1)
 		c := strings.Fields(rcpt.Address)
 		if len(c) == 0 {
 			return fmt.Errorf("empty pipe"), true
@@ -413,8 +426,10 @@ func (item *Item) deliver(q *Queue, rcpt *Recipient) (err error, permanent bool)
 
 	} else {
 		if envelope.DomainIn(rcpt.Address, q.localDomains) {
+			deliverAttempts.Add("email:local", 1)
 			return q.localC.Deliver(item.From, rcpt.Address, item.Data)
 		} else {
+			deliverAttempts.Add("email:remote", 1)
 			from := item.From
 			if !envelope.DomainIn(item.From, q.localDomains) {
 				// We're sending from a non-local to a non-local. This should