func NewHandler(s api.FluxService, r *mux.Router, logger log.Logger, h metrics.Histogram) http.Handler { for method, handlerFunc := range map[string]func(api.FluxService) http.Handler{ "ListServices": handleListServices, "ListImages": handleListImages, "PostRelease": handlePostRelease, "GetRelease": handleGetRelease, "Automate": handleAutomate, "Deautomate": handleDeautomate, "Lock": handleLock, "Unlock": handleUnlock, "History": handleHistory, "GetConfig": handleGetConfig, "SetConfig": handleSetConfig, "RegisterDaemon": handleRegister, "IsConnected": handleIsConnected, } { var handler http.Handler handler = handlerFunc(s) handler = logging(handler, log.NewContext(logger).With("method", method)) handler = observing(handler, h.With("method", method)) r.Get(method).Handler(handler) } return r }
func populateNormalHistogram(t *testing.T, h metrics.Histogram, seed int64, mean, stdev int64) { rand.Seed(seed) for i := 0; i < 1234; i++ { sample := int64(rand.NormFloat64()*float64(stdev) + float64(mean)) h.Observe(sample) } }
func observing(next http.Handler, h metrics.Histogram) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { begin := time.Now() cw := &codeWriter{w, http.StatusOK} next.ServeHTTP(cw, r) h.With("status_code", strconv.Itoa(cw.code)).Observe(time.Since(begin).Seconds()) }) }
func httpInstrument(requests metrics.Counter, duration metrics.Histogram) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requests.Add(1) defer func(begin time.Time) { duration.Observe(time.Since(begin).Nanoseconds()) }(time.Now()) next.ServeHTTP(w, r) }) } }
// PopulateNormalHistogram makes a series of normal random observations into the // histogram. The number of observations is determined by Count. The randomness // is determined by Mean, Stdev, and the seed parameter. // // This is a low-level function, exported only for metrics that don't perform // dynamic quantile computation, like a Prometheus Histogram (c.f. Summary). In // most cases, you don't need to use this function, and can use TestHistogram // instead. func PopulateNormalHistogram(h metrics.Histogram, seed int) { r := rand.New(rand.NewSource(int64(seed))) for i := 0; i < Count; i++ { sample := r.NormFloat64()*float64(Stdev) + float64(Mean) if sample < 0 { sample = 0 } h.Observe(sample) } }
// PopulateNormalHistogram populates the Histogram with a normal distribution // of observations. func PopulateNormalHistogram(t *testing.T, h metrics.Histogram, seed int64, mean, stdev int64) { r := rand.New(rand.NewSource(seed)) for i := 0; i < population; i++ { sample := int64(r.NormFloat64()*float64(stdev) + float64(mean)) if sample < 0 { sample = 0 } h.Observe(sample) } }
// EndpointInstrumentingMiddleware returns an endpoint middleware that records // the duration of each invocation to the passed histogram. The middleware adds // a single field: "success", which is "true" if no error is returned, and // "false" otherwise. func EndpointInstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware { return func(next endpoint.Endpoint) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { defer func(begin time.Time) { duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds()) }(time.Now()) return next(ctx, request) } } }
func Measure(h metrics.Histogram, d time.Duration) { if h != nil && archaius.Conf.Collect { if d > maxHistObservable { h.Observe(int64(maxHistObservable)) } else { h.Observe(int64(d)) } s := sampleMap[h] if s != nil && len(s) < sampleCount { sampleMap[h] = append(s, int64(d)) } } }
func TestScaledHistogram(t *testing.T) { var ( quantiles = []int{50, 90, 99} scale = int64(10) metricName = "test_scaled_histogram" ) var h metrics.Histogram h = expvar.NewHistogram(metricName, 0, 1000, 3, quantiles...) h = metrics.NewScaledHistogram(h, scale) h = h.With(metrics.Field{Key: "a", Value: "b"}) const seed, mean, stdev = 333, 500, 100 // input values teststat.PopulateNormalHistogram(t, h, seed, mean, stdev) // will be scaled down assertExpvarNormalHistogram(t, metricName, mean/scale, stdev/scale, quantiles) }
func main() { var ( debugAddr = flag.String("debug.addr", ":8080", "Debug and metrics listen address") grpcAddr = flag.String("grpc.addr", ":8082", "gRPC (HTTP) listen address") appdashAddr = flag.String("appdash.addr", "", "Enable Appdash tracing via an Appdash server host:port") ircNick = flag.String("irc.nick", "gogrpctest", "IRC nickname") ircSecret = flag.String("irc.secret", "", "IRC password") ircHost = flag.String("irc.addr", "irc.freenode.net:6667", "IRC host system") ) flag.Parse() // Logging domain. var logger log.Logger { logger = log.NewLogfmtLogger(os.Stdout) logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC) logger = log.NewContext(logger).With("caller", log.DefaultCaller) } logger.Log("msg", "hello") defer logger.Log("msg", "goodbye") // Metrics domain. var cx, cxerr, joins metrics.Counter { // Business level metrics. cx = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: "relay", Name: "connect_succeeded", Help: "Total count of connects via the Connect method.", }, []string{}) cxerr = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: "relay", Name: "connect_failed", Help: "Total count of connect errors via the Connect method.", }, []string{}) joins = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: "relay", Name: "channels_joined", Help: "Total count of channels joined via the Join method.", }, []string{}) } var duration metrics.Histogram { // Transport level metrics. duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ Namespace: "relay", Name: "request_duration_ns", Help: "Request duration in nanoseconds.", }, []string{"method", "success"}) } // Tracing domain. var tracer stdopentracing.Tracer { if *appdashAddr != "" { logger := log.NewContext(logger).With("tracer", "Appdash") logger.Log("addr", *appdashAddr) tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr)) } else { logger := log.NewContext(logger).With("tracer", "none") logger.Log() tracer = stdopentracing.GlobalTracer() // no-op } } // Business domain. var service relay.Service { service = relay.NewBasicService(*ircNick, *ircHost, *ircSecret) service = relay.ServiceLoggingMiddleware(logger)(service) service = relay.ServiceInstrumentingMiddleware(cx, cxerr, joins)(service) } // Endpoint domain. var connectEndpoint endpoint.Endpoint { connectDuration := duration.With("method", "Connect") connectLogger := log.NewContext(logger).With("method", "Connect") connectEndpoint = relay.MakeConnectEndpoint(service) connectEndpoint = opentracing.TraceServer(tracer, "Connect")(connectEndpoint) connectEndpoint = relay.EndpointInstrumentingMiddleware(connectDuration)(connectEndpoint) connectEndpoint = relay.EndpointLoggingMiddleware(connectLogger)(connectEndpoint) } var joinEndpoint endpoint.Endpoint { joinDuration := duration.With("method", "Join") joinLogger := log.NewContext(logger).With("method", "Join") joinEndpoint = relay.MakeJoinEndpoint(service) joinEndpoint = opentracing.TraceServer(tracer, "Join")(joinEndpoint) joinEndpoint = relay.EndpointInstrumentingMiddleware(joinDuration)(joinEndpoint) joinEndpoint = relay.EndpointLoggingMiddleware(joinLogger)(joinEndpoint) } endpoints := relay.Endpoints{ ConnectEndpoint: connectEndpoint, JoinEndpoint: joinEndpoint, } // Mechanical domain. errc := make(chan error) ctx := context.Background() // Interrupt handler. go func() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errc <- fmt.Errorf("%s", <-c) }() // Debug listener. go func() { logger := log.NewContext(logger).With("transport", "debug") m := http.NewServeMux() m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) m.Handle("/metrics", stdprometheus.Handler()) logger.Log("addr", *debugAddr) errc <- http.ListenAndServe(*debugAddr, m) }() // gRPC transport. go func() { logger := log.NewContext(logger).With("transport", "gRPC") ln, err := net.Listen("tcp", *grpcAddr) if err != nil { errc <- err return } srv := relay.MakeGRPCServer(ctx, endpoints, tracer, logger) s := grpc.NewServer() pb.RegisterRelayServer(s, srv) logger.Log("addr", *grpcAddr) errc <- s.Serve(ln) }() // Run! logger.Log("exit", <-errc) }
func main() { var ( debugAddr = flag.String("debug.addr", ":8080", "Debug and metrics listen address") httpAddr = flag.String("http.addr", ":8081", "HTTP listen address") grpcAddr = flag.String("grpc.addr", ":8082", "gRPC (HTTP) listen address") thriftAddr = flag.String("thrift.addr", ":8083", "Thrift listen address") thriftProtocol = flag.String("thrift.protocol", "binary", "binary, compact, json, simplejson") thriftBufferSize = flag.Int("thrift.buffer.size", 0, "0 for unbuffered") thriftFramed = flag.Bool("thrift.framed", false, "true to enable framing") zipkinAddr = flag.String("zipkin.addr", "", "Enable Zipkin tracing via a Kafka server host:port") appdashAddr = flag.String("appdash.addr", "", "Enable Appdash tracing via an Appdash server host:port") lightstepToken = flag.String("lightstep.token", "", "Enable LightStep tracing via a LightStep access token") ) flag.Parse() // Logging domain. var logger log.Logger { logger = log.NewLogfmtLogger(os.Stdout) logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC) logger = log.NewContext(logger).With("caller", log.DefaultCaller) } logger.Log("msg", "hello") defer logger.Log("msg", "goodbye") // Metrics domain. var ints, chars metrics.Counter { // Business level metrics. ints = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: "addsvc", Name: "integers_summed", Help: "Total count of integers summed via the Sum method.", }, []string{}) chars = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: "addsvc", Name: "characters_concatenated", Help: "Total count of characters concatenated via the Concat method.", }, []string{}) } var duration metrics.Histogram { // Transport level metrics. duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ Namespace: "addsvc", Name: "request_duration_ns", Help: "Request duration in nanoseconds.", }, []string{"method", "success"}) } // Tracing domain. var tracer stdopentracing.Tracer { if *zipkinAddr != "" { logger := log.NewContext(logger).With("tracer", "Zipkin") logger.Log("addr", *zipkinAddr) collector, err := zipkin.NewKafkaCollector( strings.Split(*zipkinAddr, ","), zipkin.KafkaLogger(logger), ) if err != nil { logger.Log("err", err) os.Exit(1) } tracer, err = zipkin.NewTracer( zipkin.NewRecorder(collector, false, "localhost:80", "addsvc"), ) if err != nil { logger.Log("err", err) os.Exit(1) } } else if *appdashAddr != "" { logger := log.NewContext(logger).With("tracer", "Appdash") logger.Log("addr", *appdashAddr) tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr)) } else if *lightstepToken != "" { logger := log.NewContext(logger).With("tracer", "LightStep") logger.Log() // probably don't want to print out the token :) tracer = lightstep.NewTracer(lightstep.Options{ AccessToken: *lightstepToken, }) defer lightstep.FlushLightStepTracer(tracer) } else { logger := log.NewContext(logger).With("tracer", "none") logger.Log() tracer = stdopentracing.GlobalTracer() // no-op } } // Business domain. var service addsvc.Service { service = addsvc.NewBasicService() service = addsvc.ServiceLoggingMiddleware(logger)(service) service = addsvc.ServiceInstrumentingMiddleware(ints, chars)(service) } // Endpoint domain. var sumEndpoint endpoint.Endpoint { sumDuration := duration.With("method", "Sum") sumLogger := log.NewContext(logger).With("method", "Sum") sumEndpoint = addsvc.MakeSumEndpoint(service) sumEndpoint = opentracing.TraceServer(tracer, "Sum")(sumEndpoint) sumEndpoint = addsvc.EndpointInstrumentingMiddleware(sumDuration)(sumEndpoint) sumEndpoint = addsvc.EndpointLoggingMiddleware(sumLogger)(sumEndpoint) } var concatEndpoint endpoint.Endpoint { concatDuration := duration.With("method", "Concat") concatLogger := log.NewContext(logger).With("method", "Concat") concatEndpoint = addsvc.MakeConcatEndpoint(service) concatEndpoint = opentracing.TraceServer(tracer, "Concat")(concatEndpoint) concatEndpoint = addsvc.EndpointInstrumentingMiddleware(concatDuration)(concatEndpoint) concatEndpoint = addsvc.EndpointLoggingMiddleware(concatLogger)(concatEndpoint) } endpoints := addsvc.Endpoints{ SumEndpoint: sumEndpoint, ConcatEndpoint: concatEndpoint, } // Mechanical domain. errc := make(chan error) ctx := context.Background() // Interrupt handler. go func() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errc <- fmt.Errorf("%s", <-c) }() // Debug listener. go func() { logger := log.NewContext(logger).With("transport", "debug") m := http.NewServeMux() m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) m.Handle("/metrics", stdprometheus.Handler()) logger.Log("addr", *debugAddr) errc <- http.ListenAndServe(*debugAddr, m) }() // HTTP transport. go func() { logger := log.NewContext(logger).With("transport", "HTTP") h := addsvc.MakeHTTPHandler(ctx, endpoints, tracer, logger) logger.Log("addr", *httpAddr) errc <- http.ListenAndServe(*httpAddr, h) }() // gRPC transport. go func() { logger := log.NewContext(logger).With("transport", "gRPC") ln, err := net.Listen("tcp", *grpcAddr) if err != nil { errc <- err return } srv := addsvc.MakeGRPCServer(ctx, endpoints, tracer, logger) s := grpc.NewServer() pb.RegisterAddServer(s, srv) logger.Log("addr", *grpcAddr) errc <- s.Serve(ln) }() // Thrift transport. go func() { logger := log.NewContext(logger).With("transport", "Thrift") var protocolFactory thrift.TProtocolFactory switch *thriftProtocol { case "binary": protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() case "compact": protocolFactory = thrift.NewTCompactProtocolFactory() case "json": protocolFactory = thrift.NewTJSONProtocolFactory() case "simplejson": protocolFactory = thrift.NewTSimpleJSONProtocolFactory() default: errc <- fmt.Errorf("invalid Thrift protocol %q", *thriftProtocol) return } var transportFactory thrift.TTransportFactory if *thriftBufferSize > 0 { transportFactory = thrift.NewTBufferedTransportFactory(*thriftBufferSize) } else { transportFactory = thrift.NewTTransportFactory() } if *thriftFramed { transportFactory = thrift.NewTFramedTransportFactory(transportFactory) } transport, err := thrift.NewTServerSocket(*thriftAddr) if err != nil { errc <- err return } logger.Log("addr", *thriftAddr) errc <- thrift.NewTSimpleServer4( thriftadd.NewAddServiceProcessor(addsvc.MakeThriftHandler(ctx, endpoints)), transport, transportFactory, protocolFactory, ).Serve() }() // Run! logger.Log("exit", <-errc) }