stdoutclient.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package statsd
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "strings"
  7. "time"
  8. "github.com/quipo/statsd/event"
  9. )
  10. // StdoutClient implements a "no-op" statsd in case there is no statsd server
  11. type StdoutClient struct {
  12. FD *os.File
  13. prefix string
  14. Logger Logger
  15. }
  16. // NewStdoutClient - Factory
  17. func NewStdoutClient(filename string, prefix string) *StdoutClient {
  18. var err error
  19. // allow %HOST% in the prefix string
  20. prefix = strings.Replace(prefix, "%HOST%", Hostname, 1)
  21. var fh *os.File
  22. if filename == "" {
  23. fh = os.Stdout
  24. } else {
  25. fh, err = os.OpenFile(filename, os.O_WRONLY, 0644)
  26. if nil != err {
  27. fmt.Printf("Cannot open file '%s' for stats output: %s\n", filename, err.Error())
  28. }
  29. }
  30. return &StdoutClient{
  31. FD: fh,
  32. prefix: prefix,
  33. Logger: log.New(os.Stdout, "[StdoutClient] ", log.Ldate|log.Ltime),
  34. }
  35. }
  36. // CreateSocket does nothing
  37. func (s *StdoutClient) CreateSocket() error {
  38. if s.FD == nil {
  39. s.FD = os.Stdout
  40. }
  41. return nil
  42. }
  43. // CreateTCPSocket does nothing
  44. func (s *StdoutClient) CreateTCPSocket() error {
  45. if s.FD == nil {
  46. s.FD = os.Stdout
  47. }
  48. return nil
  49. }
  50. // Close does nothing
  51. func (s *StdoutClient) Close() error {
  52. return nil
  53. }
  54. // Incr - Increment a counter metric. Often used to note a particular event
  55. func (s *StdoutClient) Incr(stat string, count int64) error {
  56. if 0 != count {
  57. return s.send(stat, "%d|c", count)
  58. }
  59. return nil
  60. }
  61. // Decr - Decrement a counter metric. Often used to note a particular event
  62. func (s *StdoutClient) Decr(stat string, count int64) error {
  63. if 0 != count {
  64. return s.send(stat, "%d|c", -count)
  65. }
  66. return nil
  67. }
  68. // Timing - Track a duration event
  69. // the time delta must be given in milliseconds
  70. func (s *StdoutClient) Timing(stat string, delta int64) error {
  71. return s.send(stat, "%d|ms", delta)
  72. }
  73. // PrecisionTiming - Track a duration event
  74. // the time delta has to be a duration
  75. func (s *StdoutClient) PrecisionTiming(stat string, delta time.Duration) error {
  76. return s.send(stat, "%.6f|ms", float64(delta)/float64(time.Millisecond))
  77. }
  78. // Gauge - Gauges are a constant data type. They are not subject to averaging,
  79. // and they don’t change unless you change them. That is, once you set a gauge value,
  80. // it will be a flat line on the graph until you change it again. If you specify
  81. // delta to be true, that specifies that the gauge should be updated, not set. Due to the
  82. // underlying protocol, you can't explicitly set a gauge to a negative number without
  83. // first setting it to zero.
  84. func (s *StdoutClient) Gauge(stat string, value int64) error {
  85. if value < 0 {
  86. err := s.send(stat, "%d|g", 0)
  87. if nil != err {
  88. return err
  89. }
  90. return s.send(stat, "%d|g", value)
  91. }
  92. return s.send(stat, "%d|g", value)
  93. }
  94. // GaugeDelta -- Send a change for a gauge
  95. func (s *StdoutClient) GaugeDelta(stat string, value int64) error {
  96. // Gauge Deltas are always sent with a leading '+' or '-'. The '-' takes care of itself but the '+' must added by hand
  97. if value < 0 {
  98. return s.send(stat, "%d|g", value)
  99. }
  100. return s.send(stat, "+%d|g", value)
  101. }
  102. // FGauge -- Send a floating point value for a gauge
  103. func (s *StdoutClient) FGauge(stat string, value float64) error {
  104. if value < 0 {
  105. err := s.send(stat, "%d|g", 0)
  106. if nil != err {
  107. return err
  108. }
  109. return s.send(stat, "%g|g", value)
  110. }
  111. return s.send(stat, "%g|g", value)
  112. }
  113. // FGaugeDelta -- Send a floating point change for a gauge
  114. func (s *StdoutClient) FGaugeDelta(stat string, value float64) error {
  115. if value < 0 {
  116. return s.send(stat, "%g|g", value)
  117. }
  118. return s.send(stat, "+%g|g", value)
  119. }
  120. // Absolute - Send absolute-valued metric (not averaged/aggregated)
  121. func (s *StdoutClient) Absolute(stat string, value int64) error {
  122. return s.send(stat, "%d|a", value)
  123. }
  124. // FAbsolute - Send absolute-valued floating point metric (not averaged/aggregated)
  125. func (s *StdoutClient) FAbsolute(stat string, value float64) error {
  126. return s.send(stat, "%g|a", value)
  127. }
  128. // Total - Send a metric that is continously increasing, e.g. read operations since boot
  129. func (s *StdoutClient) Total(stat string, value int64) error {
  130. return s.send(stat, "%d|t", value)
  131. }
  132. // write a UDP packet with the statsd event
  133. func (s *StdoutClient) send(stat string, format string, value interface{}) error {
  134. stat = strings.Replace(stat, "%HOST%", Hostname, 1)
  135. // if sending tcp append a newline
  136. format = fmt.Sprintf("%s%s:%s\n", s.prefix, stat, format)
  137. _, err := fmt.Fprintf(s.FD, format, value)
  138. return err
  139. }
  140. // SendEvent - Sends stats from an event object
  141. func (s *StdoutClient) SendEvent(e event.Event) error {
  142. for _, stat := range e.Stats() {
  143. //fmt.Printf("SENDING EVENT %s%s\n", s.prefix, strings.Replace(stat, "%HOST%", Hostname, 1))
  144. _, err := fmt.Fprintf(s.FD, "%s%s", s.prefix, strings.Replace(stat, "%HOST%", Hostname, 1))
  145. if nil != err {
  146. return err
  147. }
  148. }
  149. return nil
  150. }
  151. // SendEvents - Sends stats from all the event objects.
  152. // Tries to bundle many together into one fmt.Fprintf based on UDPPayloadSize.
  153. func (s *StdoutClient) SendEvents(events map[string]event.Event) error {
  154. var n int
  155. var stats = make([]string, 0)
  156. for _, e := range events {
  157. for _, stat := range e.Stats() {
  158. stat = fmt.Sprintf("%s%s", s.prefix, strings.Replace(stat, "%HOST%", Hostname, 1))
  159. _n := n + len(stat) + 1
  160. if _n > UDPPayloadSize {
  161. // with this last event, the UDP payload would be too big
  162. if _, err := fmt.Fprintf(s.FD, strings.Join(stats, "\n")); err != nil {
  163. return err
  164. }
  165. // reset payload after flushing, and add the last event
  166. stats = []string{stat}
  167. n = len(stat)
  168. continue
  169. }
  170. // can fit more into the current payload
  171. n = _n
  172. stats = append(stats, stat)
  173. }
  174. }
  175. if len(stats) != 0 {
  176. if _, err := fmt.Fprintf(s.FD, strings.Join(stats, "\n")); err != nil {
  177. return err
  178. }
  179. }
  180. return nil
  181. }