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  }