// submit basically invokes the processing function (instrumented) and tries to buffer to graphite func (s *StatsDaemon) submit(c *counters.Counters, g *gauges.Gauges, t *timers.Timers, deadline time.Time) error { var buffer bytes.Buffer now := time.Now().Unix() // TODO: in future, buffer up data (with a TTL/max size) and submit later client, err := net.Dial("tcp", s.graphite_addr) if err != nil { // todo: remove these c.Process(&buffer, now, s.flushInterval) g.Process(&buffer, now, s.flushInterval) t.Process(&buffer, now, s.flushInterval) errmsg := fmt.Sprintf("dialing %s failed - %s", s.graphite_addr, err.Error()) return errors.New(errmsg) } defer client.Close() err = client.SetDeadline(deadline) if err != nil { errmsg := fmt.Sprintf("could not set deadline - %s", err.Error()) return errors.New(errmsg) } s.instrument(c, &buffer, now, "counter") s.instrument(g, &buffer, now, "gauge") s.instrument(t, &buffer, now, "timer") if s.debug { for _, line := range bytes.Split(buffer.Bytes(), []byte("\n")) { if len(line) == 0 { continue } log.Printf("DEBUG: WRITING %s", line) } } time_start := time.Now() _, err = client.Write(buffer.Bytes()) if err != nil { errmsg := fmt.Sprintf("failed to write stats - %s", err) return errors.New(errmsg) } time_end := time.Now() duration_ms := float64(time_end.Sub(time_start).Nanoseconds()) / float64(1000000) if s.debug { log.Println("submit() successfully finished") } buffer.Reset() fmt.Fprintf(&buffer, "%starget_type_is_gauge.type_is_send.unit_is_ms %f %d\n", s.prefix, duration_ms, now) _, err = client.Write(buffer.Bytes()) if err != nil { errmsg := fmt.Sprintf("failed to write target_type_is_gauge.type_is_send.unit_is_ms - %s", err) return errors.New(errmsg) } return nil }
// metricsMonitor basically guards the metrics datastructures. // it typically receives metrics on the Metrics channel but also responds to // external signals and every flushInterval, computes and flushes the data func (s *StatsDaemon) metricsMonitor() { period := time.Duration(s.flushInterval) * time.Second tick := ticker.GetAlignedTicker(period) var c *counters.Counters var g *gauges.Gauges var t *timers.Timers initializeCounters := func() { c = counters.New(s.prefix_rates) g = gauges.New(s.prefix_gauges) t = timers.New(s.prefix_timers, s.pct) for _, name := range []string{"timer", "gauge", "counter"} { c.Add(&common.Metric{ Bucket: fmt.Sprintf("%sdirection_is_in.statsd_type_is_%s.target_type_is_count.unit_is_Metric", s.prefix, name), Sampling: 1, }) } } initializeCounters() for { select { case sig := <-s.signalchan: switch sig { case syscall.SIGTERM, syscall.SIGINT: fmt.Printf("!! Caught signal %s... shutting down\n", sig) if err := s.submit(c, g, t, time.Now().Add(period)); err != nil { log.Printf("ERROR: %s", err) } return default: fmt.Printf("unknown signal %s, ignoring\n", sig) } case <-tick.C: go func(c *counters.Counters, g *gauges.Gauges, t *timers.Timers) { if err := s.submit(c, g, t, time.Now().Add(period)); err != nil { log.Printf("ERROR: %s", err) } s.events.Broadcast <- "flush" }(c, g, t) initializeCounters() tick = ticker.GetAlignedTicker(period) case m := <-s.Metrics: var name string if m.Modifier == "ms" { t.Add(m) name = "timer" } else if m.Modifier == "g" { if m.IsDelta { log.Fatal("delta gauge:", m) } g.Add(m) name = "gauge" } else if m.Modifier == "c" { c.Add(m) name = "counter" } else { name = "unknown" } c.Add(&common.Metric{ Bucket: fmt.Sprintf("%sdirection_is_in.statsd_type_is_%s.target_type_is_count.unit_is_Metric", s.prefix, name), Value: 1, Sampling: 1, }) } } }