func (e *Exporter) PushMetrics() {
	for _, target := range e.pushTargets {
		glog.V(2).Infof("pushing to %s", target.addr)
		conn, err := net.DialTimeout(target.net, target.addr, *writeDeadline)
		if err != nil {
			glog.Infof("pusher dial error: %s", err)
			continue
		}
		err = conn.SetDeadline(time.Now().Add(*writeDeadline))
		if err != nil {
			glog.Infof("Couldn't set deadline on connection: %s", err)
		}
		err = e.writeSocketMetrics(conn, target.f, target.total, target.success)
		if err != nil {
			glog.Infof("pusher write error: %s", err)
		}
		err = conn.Close()
		if err != nil {
			glog.Infof("connection close failed: %s", err)
		}
	}
}
PushMetrics sends metrics to each of the configured services.
package exporter
import (
	"context"
	"expvar"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"sync"
	"time"
	"github.com/golang/glog"
	"github.com/google/mtail/internal/metrics"
	"github.com/pkg/errors"
)
var (
	writeDeadline = flag.Duration("metric_push_write_deadline", 10*time.Second, "Time to wait for a push to succeed before exiting with an error.")
)
type Exporter struct {
	ctx           context.Context
	wg            sync.WaitGroup
	store         *metrics.Store
	pushInterval  time.Duration
	hostname      string
	omitProgLabel bool
	emitTimestamp bool
	pushTargets   []pushOptions
	initDone      chan struct{}
}
type Option func(*Exporter) error
func Hostname(hostname string) Option {
	return func(e *Exporter) error {
		e.hostname = hostname
		return nil
	}
}
func OmitProgLabel() Option {
	return func(e *Exporter) error {
		e.omitProgLabel = true
		return nil
	}
}
func EmitTimestamp() Option {
	return func(e *Exporter) error {
		e.emitTimestamp = true
		return nil
	}
}
func PushInterval(opt time.Duration) Option {
	return func(e *Exporter) error {
		e.pushInterval = opt
		return nil
	}
}
func New(ctx context.Context, wg *sync.WaitGroup, store *metrics.Store, options ...Option) (*Exporter, error) {
	if store == nil {
		return nil, errors.New("exporter needs a Store")
	}
	e := &Exporter{
		ctx:      ctx,
		store:    store,
		initDone: make(chan struct{}),
	}
	defer close(e.initDone)
	if err := e.SetOption(options...); err != nil {
		return nil, err
	}
	if e.hostname == "" {
		var err error
		e.hostname, err = os.Hostname()
		if err != nil {
			return nil, errors.Wrap(err, "getting hostname")
		}
	}
	if *collectdSocketPath != "" {
		o := pushOptions{"unix", *collectdSocketPath, metricToCollectd, collectdExportTotal, collectdExportSuccess}
		e.RegisterPushExport(o)
	}
	if *graphiteHostPort != "" {
		o := pushOptions{"tcp", *graphiteHostPort, metricToGraphite, graphiteExportTotal, graphiteExportSuccess}
		e.RegisterPushExport(o)
	}
	if *statsdHostPort != "" {
		o := pushOptions{"udp", *statsdHostPort, metricToStatsd, statsdExportTotal, statsdExportSuccess}
		e.RegisterPushExport(o)
	}
	e.StartMetricPush()
	go func() {
		<-e.initDone
		<-e.ctx.Done()
		e.wg.Wait()
	}()
	return e, nil
}
func (e *Exporter) SetOption(options ...Option) error {
	for _, option := range options {
		if err := option(e); err != nil {
			return err
		}
	}
	return nil
}
func formatLabels(name string, m map[string]string, ksep, sep, rep string) string {
	r := name
	if len(m) > 0 {
		var s []string
		for k, v := range m {
			k1 := strings.ReplaceAll(strings.ReplaceAll(k, ksep, rep), sep, rep)
			v1 := strings.ReplaceAll(strings.ReplaceAll(v, ksep, rep), sep, rep)
			s = append(s, fmt.Sprintf("%s%s%s", k1, ksep, v1))
		}
		return r + sep + strings.Join(s, sep)
	}
	return r
}
type formatter func(string, *metrics.Metric, *metrics.LabelSet, time.Duration) string
func (e *Exporter) writeSocketMetrics(c io.Writer, f formatter, exportTotal *expvar.Int, exportSuccess *expvar.Int) error {
	return e.store.Range(func(m *metrics.Metric) error {
		m.RLock()
		if m.Kind == metrics.Text {
			m.RUnlock()
			return nil
		}
		exportTotal.Add(1)
		lc := make(chan *metrics.LabelSet)
		go m.EmitLabelSets(lc)
		for l := range lc {
			line := f(e.hostname, m, l, e.pushInterval)
			n, err := fmt.Fprint(c, line)
			glog.V(2).Infof("Sent %d bytes\n", n)
			if err == nil {
				exportSuccess.Add(1)
			} else {
				return errors.Errorf("write error: %s\n", err)
			}
		}
		m.RUnlock()
		return nil
	})
}