uds.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // +build !windows
  2. package statsd
  3. import (
  4. "net"
  5. "sync"
  6. "time"
  7. )
  8. // udsWriter is an internal class wrapping around management of UDS connection
  9. type udsWriter struct {
  10. // Address to send metrics to, needed to allow reconnection on error
  11. addr net.Addr
  12. // Established connection object, or nil if not connected yet
  13. conn net.Conn
  14. // write timeout
  15. writeTimeout time.Duration
  16. sync.RWMutex // used to lock conn / writer can replace it
  17. }
  18. // newUDSWriter returns a pointer to a new udsWriter given a socket file path as addr.
  19. func newUDSWriter(addr string, writeTimeout time.Duration) (*udsWriter, error) {
  20. udsAddr, err := net.ResolveUnixAddr("unixgram", addr)
  21. if err != nil {
  22. return nil, err
  23. }
  24. // Defer connection to first Write
  25. writer := &udsWriter{addr: udsAddr, conn: nil, writeTimeout: writeTimeout}
  26. return writer, nil
  27. }
  28. // Write data to the UDS connection with write timeout and minimal error handling:
  29. // create the connection if nil, and destroy it if the statsd server has disconnected
  30. func (w *udsWriter) Write(data []byte) (int, error) {
  31. conn, err := w.ensureConnection()
  32. if err != nil {
  33. return 0, err
  34. }
  35. conn.SetWriteDeadline(time.Now().Add(w.writeTimeout))
  36. n, e := conn.Write(data)
  37. if err, isNetworkErr := e.(net.Error); err != nil && (!isNetworkErr || !err.Temporary()) {
  38. // Statsd server disconnected, retry connecting at next packet
  39. w.unsetConnection()
  40. return 0, e
  41. }
  42. return n, e
  43. }
  44. func (w *udsWriter) Close() error {
  45. if w.conn != nil {
  46. return w.conn.Close()
  47. }
  48. return nil
  49. }
  50. func (w *udsWriter) ensureConnection() (net.Conn, error) {
  51. // Check if we've already got a socket we can use
  52. w.RLock()
  53. currentConn := w.conn
  54. w.RUnlock()
  55. if currentConn != nil {
  56. return currentConn, nil
  57. }
  58. // Looks like we might need to connect - try again with write locking.
  59. w.Lock()
  60. defer w.Unlock()
  61. if w.conn != nil {
  62. return w.conn, nil
  63. }
  64. newConn, err := net.Dial(w.addr.Network(), w.addr.String())
  65. if err != nil {
  66. return nil, err
  67. }
  68. w.conn = newConn
  69. return newConn, nil
  70. }
  71. func (w *udsWriter) unsetConnection() {
  72. w.Lock()
  73. defer w.Unlock()
  74. w.conn = nil
  75. }