| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- // +build !windows
- package statsd
- import (
- "net"
- "sync"
- "time"
- )
- /*
- UDSTimeout holds the default timeout for UDS socket writes, as they can get
- blocking when the receiving buffer is full.
- */
- const defaultUDSTimeout = 100 * time.Millisecond
- // udsWriter is an internal class wrapping around management of UDS connection
- type udsWriter struct {
- // Address to send metrics to, needed to allow reconnection on error
- addr net.Addr
- // Established connection object, or nil if not connected yet
- conn net.Conn
- // write timeout
- writeTimeout time.Duration
- sync.RWMutex // used to lock conn / writer can replace it
- }
- // newUDSWriter returns a pointer to a new udsWriter given a socket file path as addr.
- func newUDSWriter(addr string) (*udsWriter, error) {
- udsAddr, err := net.ResolveUnixAddr("unixgram", addr)
- if err != nil {
- return nil, err
- }
- // Defer connection to first Write
- writer := &udsWriter{addr: udsAddr, conn: nil, writeTimeout: defaultUDSTimeout}
- return writer, nil
- }
- // SetWriteTimeout allows the user to set a custom write timeout
- func (w *udsWriter) SetWriteTimeout(d time.Duration) error {
- w.writeTimeout = d
- return nil
- }
- // Write data to the UDS connection with write timeout and minimal error handling:
- // create the connection if nil, and destroy it if the statsd server has disconnected
- func (w *udsWriter) Write(data []byte) (int, error) {
- conn, err := w.ensureConnection()
- if err != nil {
- return 0, err
- }
- conn.SetWriteDeadline(time.Now().Add(w.writeTimeout))
- n, e := conn.Write(data)
- if err, isNetworkErr := e.(net.Error); err != nil && (!isNetworkErr || !err.Temporary()) {
- // Statsd server disconnected, retry connecting at next packet
- w.unsetConnection()
- return 0, e
- }
- return n, e
- }
- func (w *udsWriter) Close() error {
- if w.conn != nil {
- return w.conn.Close()
- }
- return nil
- }
- func (w *udsWriter) ensureConnection() (net.Conn, error) {
- // Check if we've already got a socket we can use
- w.RLock()
- currentConn := w.conn
- w.RUnlock()
- if currentConn != nil {
- return currentConn, nil
- }
- // Looks like we might need to connect - try again with write locking.
- w.Lock()
- defer w.Unlock()
- if w.conn != nil {
- return w.conn, nil
- }
- newConn, err := net.Dial(w.addr.Network(), w.addr.String())
- if err != nil {
- return nil, err
- }
- w.conn = newConn
- return newConn, nil
- }
- func (w *udsWriter) unsetConnection() {
- w.Lock()
- defer w.Unlock()
- w.conn = nil
- }
|