// for debugging, show goroutine trace on receipt of USR1. uninstall by calling // Stop on the returned object func InstallStackTracer() stopper.Stopper { signals := make(chan os.Signal, 1) signal.Notify(signals, traceSignal) stopper := stopper.NewChanStopper() go func() { defer func() { signal.Stop(signals) close(signals) }() for { select { case <-signals: log.Print(instrumentation.GetStackTrace(true)) case <-stopper.Chan: return } } }() return stopper }
// NewGangliaReporterWithOptions is NewGangliaReporter with the groupName. func NewGangliaReporterWithOptions(interval time.Duration, groupName string) *Reporter { gm, err := NewGmetric() if err != nil { vlog.VLogfQuiet("ganglia", "Couldn't start Ganglia reporter: %s", err) return nil } else if gm == nil { return nil } stopper := stopper.NewChanStopper() gr := &Reporter{ ChanStopper: stopper, prefix: "", callbacks: []ReporterCallback{}, previous: make(map[string]gmetricSample), groupName: groupName, dmax: 0, } go func() { defer stopper.Done() for { select { case <-stopper.Chan: return case <-time.After(interval): go func() { // SendMetric "opens" and "closes" UDP connections each // time, but since we expect the callback to send several // metrics at once, avoid that here. conns := gm.OpenConnections() n := 0 sender := func(name string, value string, metricType uint32, units string, rate bool) { v := value if rate { prev, exists := gr.previous[name] units += "/sec" now := time.Now() switch metricType { case Ushort, Short, Uint, Int: i, err := strconv.Atoi(value) if err != nil { vlog.VLogfQuiet(name, "Value %q doesn't look like an int: %s", value, err) return } gr.previous[name] = gmetricSample{i, now} if !exists { return } delta := i - prev.value.(int) elapsed := time.Now().Sub(prev.when).Seconds() v = fmt.Sprint(float64(delta) / elapsed) // upgrade to a float to avoid loss of precision metricType = Float case Float, Double: f, err := strconv.ParseFloat(value, 64) if err != nil { vlog.VLogfQuiet(name, "Value %q doesn't look like a float: %s", value, err) return } gr.previous[name] = gmetricSample{f, now} if !exists { return } delta := f - prev.value.(float64) elapsed := time.Now().Sub(prev.when).Seconds() v = fmt.Sprint(delta / elapsed) case String: vlog.VLogfQuiet(name, "Can't compute deltas for string metric %q", value) return } } // gmetad fails to escape quotes, eventually generating // invalid xml. do it here as a workaround. v = html.EscapeString(v) name = html.EscapeString(name) units = html.EscapeString(units) n++ gm.SendMetricPackets( gr.prefix+name, v, metricType, units, gmetric.SLOPE_BOTH, uint32(interval.Seconds()), // tmax is the expected reporting interval gr.dmax, gr.groupName, gmetric.PACKET_BOTH, conns, ) if debug.On() { if rate { log.Printf("gmetric: name=%q, rate=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d", gr.prefix+name, v, value, metricType, units, gmetric.SLOPE_BOTH, uint32(interval.Seconds()), gr.dmax, gr.groupName, gmetric.PACKET_BOTH, ) } else { log.Printf("gmetric: name=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d", gr.prefix+name, v, metricType, units, gmetric.SLOPE_BOTH, uint32(interval.Seconds()), gr.dmax, gr.groupName, gmetric.PACKET_BOTH, ) } } } defer gm.CloseConnections(conns) for _, callback := range gr.callbacks { callback(sender) } if debug.On() { log.Printf("Published %d metrics to Ganglia", n) } }() } } }() return gr }