func (sb *StatsdBuffer) Close() (err error) {
	req := closeRequest{reply: make(chan error, 0)}
	sb.closeChannel <- req
	err = <-req.reply
	err2 := sb.statsd.Close()
	if err != nil {
		return err
	}
	return err2
}
Close sends a close event to the collector asking to stop & flush pending stats
and closes the statsd client
package statsd
import (
	"log"
	"os"
	"time"
	"github.com/quipo/statsd/event"
)
type closeRequest struct {
	reply chan error
}
type StatsdBuffer struct {
	statsd        *StatsdClient
	flushInterval time.Duration
	eventChannel  chan event.Event
	events        map[string]event.Event
	closeChannel  chan closeRequest
	Logger        Logger
	Verbose       bool
}
func NewStatsdBuffer(interval time.Duration, client *StatsdClient) *StatsdBuffer {
	sb := &StatsdBuffer{
		flushInterval: interval,
		statsd:        client,
		eventChannel:  make(chan event.Event, 100),
		events:        make(map[string]event.Event, 0),
		closeChannel:  make(chan closeRequest, 0),
		Logger:        log.New(os.Stdout, "[BufferedStatsdClient] ", log.Ldate|log.Ltime),
		Verbose:       true,
	}
	go sb.collector()
	return sb
}
func (sb *StatsdBuffer) CreateSocket() error {
	return sb.statsd.CreateSocket()
}
func (sb *StatsdBuffer) CreateTCPSocket() error {
	return sb.statsd.CreateTCPSocket()
}
func (sb *StatsdBuffer) Incr(stat string, count int64) error {
	if 0 != count {
		sb.eventChannel <- &event.Increment{Name: stat, Value: count}
	}
	return nil
}
func (sb *StatsdBuffer) Decr(stat string, count int64) error {
	if 0 != count {
		sb.eventChannel <- &event.Increment{Name: stat, Value: -count}
	}
	return nil
}
func (sb *StatsdBuffer) Timing(stat string, delta int64) error {
	sb.eventChannel <- event.NewTiming(stat, delta)
	return nil
}
func (sb *StatsdBuffer) PrecisionTiming(stat string, delta time.Duration) error {
	sb.eventChannel <- event.NewPrecisionTiming(stat, delta)
	return nil
}
func (sb *StatsdBuffer) Gauge(stat string, value int64) error {
	sb.eventChannel <- &event.Gauge{Name: stat, Value: value}
	return nil
}
func (sb *StatsdBuffer) GaugeDelta(stat string, value int64) error {
	sb.eventChannel <- &event.GaugeDelta{Name: stat, Value: value}
	return nil
}
func (sb *StatsdBuffer) FGauge(stat string, value float64) error {
	sb.eventChannel <- &event.FGauge{Name: stat, Value: value}
	return nil
}
func (sb *StatsdBuffer) FGaugeDelta(stat string, value float64) error {
	sb.eventChannel <- &event.FGaugeDelta{Name: stat, Value: value}
	return nil
}
func (sb *StatsdBuffer) Absolute(stat string, value int64) error {
	sb.eventChannel <- &event.Absolute{Name: stat, Values: []int64{value}}
	return nil
}
func (sb *StatsdBuffer) FAbsolute(stat string, value float64) error {
	sb.eventChannel <- &event.FAbsolute{Name: stat, Values: []float64{value}}
	return nil
}
func (sb *StatsdBuffer) Total(stat string, value int64) error {
	sb.eventChannel <- &event.Total{Name: stat, Value: value}
	return nil
}
func initMemoisedKeyMap() func(typ string, key string) string {
	m := make(map[string]map[string]string)
	return func(typ string, key string) string {
		if _, ok := m[typ]; !ok {
			m[typ] = make(map[string]string)
		}
		if k, ok := m[typ][key]; !ok {
			m[typ][key] = typ + "|" + key
			return m[typ][key]
		} else {
			return k  
		}
	}
}
func (sb *StatsdBuffer) collector() {
	defer func(sb *StatsdBuffer) {
		if r := recover(); r != nil {
			sb.Logger.Println("Caught panic, flushing stats before throwing the panic again")
			sb.flush()
			panic(r)
		}
	}(sb)
	keyFor := initMemoisedKeyMap()  
	ticker := time.NewTicker(sb.flushInterval)
	for {
		select {
		case <-ticker.C:
			sb.flush()
		case e := <-sb.eventChannel:
			k := keyFor(e.TypeString(), e.Key())  
			if e2, ok := sb.events[k]; ok {
				e2.Update(e)
				sb.events[k] = e2
			} else {
				sb.events[k] = e
			}
		case c := <-sb.closeChannel:
			if sb.Verbose {
				sb.Logger.Println("Asked to terminate. Flushing stats before returning.")
			}
			c.reply <- sb.flush()
			return
		}
	}
}