github.com/nsqio/nsq@v1.3.0/nsqd/statsd.go (about) 1 package nsqd 2 3 import ( 4 "fmt" 5 "math" 6 "net" 7 "strings" 8 "time" 9 10 "github.com/nsqio/nsq/internal/statsd" 11 "github.com/nsqio/nsq/internal/writers" 12 ) 13 14 type Uint64Slice []uint64 15 16 func (s Uint64Slice) Len() int { 17 return len(s) 18 } 19 20 func (s Uint64Slice) Swap(i, j int) { 21 s[i], s[j] = s[j], s[i] 22 } 23 24 func (s Uint64Slice) Less(i, j int) bool { 25 return s[i] < s[j] 26 } 27 28 func (n *NSQD) statsdLoop() { 29 var lastMemStats memStats 30 var lastStats Stats 31 interval := n.getOpts().StatsdInterval 32 ticker := time.NewTicker(interval) 33 for { 34 select { 35 case <-n.exitChan: 36 goto exit 37 case <-ticker.C: 38 addr := n.getOpts().StatsdAddress 39 prefix := n.getOpts().StatsdPrefix 40 excludeEphemeral := n.getOpts().StatsdExcludeEphemeral 41 conn, err := net.DialTimeout("udp", addr, time.Second) 42 if err != nil { 43 n.logf(LOG_ERROR, "failed to create UDP socket to statsd(%s)", addr) 44 continue 45 } 46 sw := writers.NewSpreadWriter(conn, interval-time.Second, n.exitChan) 47 bw := writers.NewBoundaryBufferedWriter(sw, n.getOpts().StatsdUDPPacketSize) 48 client := statsd.NewClient(bw, prefix) 49 50 n.logf(LOG_INFO, "STATSD: pushing stats to %s", addr) 51 52 stats := n.GetStats("", "", false) 53 for _, topic := range stats.Topics { 54 if excludeEphemeral && strings.HasSuffix(topic.TopicName, "#ephemeral") { 55 continue 56 } 57 58 // try to find the topic in the last collection 59 lastTopic := TopicStats{} 60 for _, checkTopic := range lastStats.Topics { 61 if topic.TopicName == checkTopic.TopicName { 62 lastTopic = checkTopic 63 break 64 } 65 } 66 diff := topic.MessageCount - lastTopic.MessageCount 67 stat := fmt.Sprintf("topic.%s.message_count", topic.TopicName) 68 client.Incr(stat, int64(diff)) 69 70 diff = topic.MessageBytes - lastTopic.MessageBytes 71 stat = fmt.Sprintf("topic.%s.message_bytes", topic.TopicName) 72 client.Incr(stat, int64(diff)) 73 74 stat = fmt.Sprintf("topic.%s.depth", topic.TopicName) 75 client.Gauge(stat, topic.Depth) 76 77 stat = fmt.Sprintf("topic.%s.backend_depth", topic.TopicName) 78 client.Gauge(stat, topic.BackendDepth) 79 80 for _, item := range topic.E2eProcessingLatency.Percentiles { 81 stat = fmt.Sprintf("topic.%s.e2e_processing_latency_%.0f", topic.TopicName, item["quantile"]*100.0) 82 // We can cast the value to int64 since a value of 1 is the 83 // minimum resolution we will have, so there is no loss of 84 // accuracy 85 client.Gauge(stat, int64(item["value"])) 86 } 87 88 for _, channel := range topic.Channels { 89 if excludeEphemeral && strings.HasSuffix(channel.ChannelName, "#ephemeral") { 90 continue 91 } 92 93 // try to find the channel in the last collection 94 lastChannel := ChannelStats{} 95 for _, checkChannel := range lastTopic.Channels { 96 if channel.ChannelName == checkChannel.ChannelName { 97 lastChannel = checkChannel 98 break 99 } 100 } 101 diff := channel.MessageCount - lastChannel.MessageCount 102 stat := fmt.Sprintf("topic.%s.channel.%s.message_count", topic.TopicName, channel.ChannelName) 103 client.Incr(stat, int64(diff)) 104 105 stat = fmt.Sprintf("topic.%s.channel.%s.depth", topic.TopicName, channel.ChannelName) 106 client.Gauge(stat, channel.Depth) 107 108 stat = fmt.Sprintf("topic.%s.channel.%s.backend_depth", topic.TopicName, channel.ChannelName) 109 client.Gauge(stat, channel.BackendDepth) 110 111 stat = fmt.Sprintf("topic.%s.channel.%s.in_flight_count", topic.TopicName, channel.ChannelName) 112 client.Gauge(stat, int64(channel.InFlightCount)) 113 114 stat = fmt.Sprintf("topic.%s.channel.%s.deferred_count", topic.TopicName, channel.ChannelName) 115 client.Gauge(stat, int64(channel.DeferredCount)) 116 117 diff = channel.RequeueCount - lastChannel.RequeueCount 118 stat = fmt.Sprintf("topic.%s.channel.%s.requeue_count", topic.TopicName, channel.ChannelName) 119 client.Incr(stat, int64(diff)) 120 121 diff = channel.TimeoutCount - lastChannel.TimeoutCount 122 stat = fmt.Sprintf("topic.%s.channel.%s.timeout_count", topic.TopicName, channel.ChannelName) 123 client.Incr(stat, int64(diff)) 124 125 stat = fmt.Sprintf("topic.%s.channel.%s.clients", topic.TopicName, channel.ChannelName) 126 client.Gauge(stat, int64(channel.ClientCount)) 127 128 for _, item := range channel.E2eProcessingLatency.Percentiles { 129 stat = fmt.Sprintf("topic.%s.channel.%s.e2e_processing_latency_%.0f", topic.TopicName, channel.ChannelName, item["quantile"]*100.0) 130 client.Gauge(stat, int64(item["value"])) 131 } 132 } 133 } 134 lastStats = stats 135 136 if n.getOpts().StatsdMemStats { 137 ms := getMemStats() 138 139 client.Gauge("mem.heap_objects", int64(ms.HeapObjects)) 140 client.Gauge("mem.heap_idle_bytes", int64(ms.HeapIdleBytes)) 141 client.Gauge("mem.heap_in_use_bytes", int64(ms.HeapInUseBytes)) 142 client.Gauge("mem.heap_released_bytes", int64(ms.HeapReleasedBytes)) 143 client.Gauge("mem.gc_pause_usec_100", int64(ms.GCPauseUsec100)) 144 client.Gauge("mem.gc_pause_usec_99", int64(ms.GCPauseUsec99)) 145 client.Gauge("mem.gc_pause_usec_95", int64(ms.GCPauseUsec95)) 146 client.Gauge("mem.next_gc_bytes", int64(ms.NextGCBytes)) 147 client.Incr("mem.gc_runs", int64(ms.GCTotalRuns-lastMemStats.GCTotalRuns)) 148 149 lastMemStats = ms 150 } 151 152 bw.Flush() 153 sw.Flush() 154 conn.Close() 155 } 156 } 157 158 exit: 159 ticker.Stop() 160 n.logf(LOG_INFO, "STATSD: closing") 161 } 162 163 func percentile(perc float64, arr []uint64, length int) uint64 { 164 if length == 0 { 165 return 0 166 } 167 indexOfPerc := int(math.Floor(((perc / 100.0) * float64(length)) + 0.5)) 168 if indexOfPerc >= length { 169 indexOfPerc = length - 1 170 } 171 return arr[indexOfPerc] 172 }