Skip to content

Commit

Permalink
Merge pull request prometheus#6 from prometheus/feature/user-love/htt…
Browse files Browse the repository at this point in the history
…p-mux-wrapper

Add HTTP multiplexor wrapper for automatic telemetry.
  • Loading branch information
matttproud committed Apr 2, 2013
2 parents 643925d + c5b4952 commit a087e01
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 78 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ _cgo_export.*
_testmain.go

*.exe

*~
*#
39 changes: 39 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

MAKE_ARTIFACTS = search_index

all: test

build:
go build ./...

test: build
go test ./... $(GO_TEST_FLAGS)

format:
find . -iname '*.go' -exec gofmt -w -s=true '{}' ';'

advice:
go tool vet .

search_index:
godoc -index -write_index -index_files='search_index'

documentation: search_index
godoc -http=:6060 -index -index_files='search_index'

clean:
rm -f $(MAKE_ARTIFACTS)

.PHONY: advice build clean documentation format test
9 changes: 0 additions & 9 deletions contributor/documentation.go

This file was deleted.

36 changes: 0 additions & 36 deletions contributor/responsewriter_delegator.go

This file was deleted.

54 changes: 54 additions & 0 deletions examples/delegator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2012, Matt T. Proud
// All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This skeletal example of the telemetry library is provided to demonstrate the
// use of boilerplate HTTP delegation telemetry methods.
package main

import (
"flag"
"github.com/prometheus/client_golang"
"github.com/prometheus/client_golang/exp"
"net/http"
)

// helloHandler demonstrates the DefaultCoarseMux's ability to sniff a
// http.ResponseWriter (specifically http.response) implicit setting of
// a response code.
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, hello, hello..."))
}

// goodbyeHandler demonstrates the DefaultCoarseMux's ability to sniff an
// http.ResponseWriter (specifically http.response) explicit setting of
// a response code.
func goodbyeHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusGone)
w.Write([]byte("... and now for the big goodbye!"))
}

// teapotHandler demonstrates the DefaultCoarseMux's ability to sniff an
// http.ResponseWriter (specifically http.response) explicit setting of
// a response code for pure comedic value.
func teapotHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTeapot)
w.Write([]byte("Short and stout..."))
}

var (
listeningAddress = flag.String("listeningAddress", ":8080", "The address to listen to requests on.")
)

func main() {
flag.Parse()

exp.HandleFunc("/hello", helloHandler)
exp.HandleFunc("/goodbye", goodbyeHandler)
exp.HandleFunc("/teapot", teapotHandler)
exp.Handle(registry.ExpositionResource, registry.DefaultHandler)

http.ListenAndServe(*listeningAddress, exp.DefaultCoarseMux)
}
41 changes: 18 additions & 23 deletions examples/random/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ import (
)

var (
listeningAddress string

barDomain float64
barMean float64
fooDomain float64
barDomain = flag.Float64("random.fooDomain", 200, "The domain for the random parameter foo.")
barMean = flag.Float64("random.barDomain", 10, "The domain for the random parameter bar.")
fooDomain = flag.Float64("random.barMean", 100, "The mean for the random parameter bar.")

// Create a histogram to track fictitious interservice RPC latency for three
// distinct services.
rpc_latency = metrics.NewHistogram(&metrics.HistogramSpecification{
rpcLatency = metrics.NewHistogram(&metrics.HistogramSpecification{
// Four distinct histogram buckets for values:
// - equally-sized,
// - 0 to 50, 50 to 100, 100 to 150, and 150 to 200.
Expand All @@ -45,44 +43,41 @@ var (
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99},
})

rpc_calls = metrics.NewCounter()
rpcCalls = metrics.NewCounter()

// If for whatever reason you are resistant to the idea of having a static
// registry for metrics, which is a really bad idea when using Prometheus-
// enabled library code, you can create your own.
customRegistry = registry.NewRegistry()
)

func init() {
flag.StringVar(&listeningAddress, "listeningAddress", ":8080", "The address to listen to requests on.")
flag.Float64Var(&fooDomain, "random.fooDomain", 200, "The domain for the random parameter foo.")
flag.Float64Var(&barDomain, "random.barDomain", 10, "The domain for the random parameter bar.")
flag.Float64Var(&barMean, "random.barMean", 100, "The mean for the random parameter bar.")
}

func main() {
flag.Parse()

go func() {
for {
rpc_latency.Add(map[string]string{"service": "foo"}, rand.Float64()*fooDomain)
rpc_calls.Increment(map[string]string{"service": "foo"})
rpcLatency.Add(map[string]string{"service": "foo"}, rand.Float64()**fooDomain)
rpcCalls.Increment(map[string]string{"service": "foo"})

rpc_latency.Add(map[string]string{"service": "bar"}, (rand.NormFloat64()*barDomain)+barMean)
rpc_calls.Increment(map[string]string{"service": "bar"})
rpcLatency.Add(map[string]string{"service": "bar"}, (rand.NormFloat64()**barDomain)+*barMean)
rpcCalls.Increment(map[string]string{"service": "bar"})

rpc_latency.Add(map[string]string{"service": "zed"}, rand.ExpFloat64())
rpc_calls.Increment(map[string]string{"service": "zed"})
rpcLatency.Add(map[string]string{"service": "zed"}, rand.ExpFloat64())
rpcCalls.Increment(map[string]string{"service": "zed"})

time.Sleep(100 * time.Millisecond)
}
}()

http.Handle(registry.ExpositionResource, customRegistry.Handler())
http.ListenAndServe(listeningAddress, nil)
http.ListenAndServe(*listeningAddress, nil)
}

func init() {
customRegistry.Register("rpc_latency_microseconds", "RPC latency.", registry.NilLabels, rpc_latency)
customRegistry.Register("rpc_calls_total", "RPC calls.", registry.NilLabels, rpc_calls)
customRegistry.Register("rpc_latency_microseconds", "RPC latency.", registry.NilLabels, rpcLatency)
customRegistry.Register("rpc_calls_total", "RPC calls.", registry.NilLabels, rpcCalls)
}

var (
listeningAddress = flag.String("listeningAddress", ":8080", "The address to listen to requests on.")
)
14 changes: 5 additions & 9 deletions examples/simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,13 @@ import (
"net/http"
)

var (
listeningAddress string
)

func init() {
flag.StringVar(&listeningAddress, "listeningAddress", ":8080", "The address to listen to requests on.")
}

func main() {
flag.Parse()

http.Handle(registry.ExpositionResource, registry.DefaultHandler)
http.ListenAndServe(listeningAddress, nil)
http.ListenAndServe(*listeningAddress, nil)
}

var (
listeningAddress = flag.String("listeningAddress", ":8080", "The address to listen to requests on.")
)
111 changes: 111 additions & 0 deletions exp/coarsemux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) 2013, Matt T. Proud
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.

package exp

import (
"fmt"
"github.com/prometheus/client_golang"
"github.com/prometheus/client_golang/metrics"
"net/http"
"strings"
"time"
)

const (
handler = "handler"
code = "code"
method = "method"
)

type (
coarseMux struct {
*http.ServeMux
}

handlerDelegator struct {
delegate http.Handler
pattern string
}
)

var (
requestCounts = metrics.NewCounter()
requestDuration = metrics.NewCounter()
requestDurations = metrics.NewDefaultHistogram()
requestBytes = metrics.NewCounter()
responseBytes = metrics.NewCounter()

// DefaultCoarseMux is a drop-in replacement for http.DefaultServeMux that
// provides standardized telemetry for Go's standard HTTP handler registration
// and dispatch API.
//
// The name is due to the coarse grouping of telemetry by (HTTP Method, HTTP Response Code,
// and handler match pattern) triples.
DefaultCoarseMux = newCoarseMux()
)

func (h handlerDelegator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rwd := NewResponseWriterDelegator(w)

defer func() {
duration := float64(time.Since(start) / time.Microsecond)
status := rwd.Status()
labels := map[string]string{handler: h.pattern, code: status, method: strings.ToLower(r.Method)}
requestCounts.Increment(labels)
requestDuration.IncrementBy(labels, duration)
requestDurations.Add(labels, duration)
requestBytes.IncrementBy(labels, float64(computeApproximateRequestSize(*r)))
responseBytes.IncrementBy(labels, float64(rwd.BytesWritten))
}()

h.delegate.ServeHTTP(rwd, r)
}

func (h handlerDelegator) String() string {
return fmt.Sprintf("handlerDelegator wrapping %s for %s", h.delegate, h.pattern)
}

// Handle registers a http.Handler to this CoarseMux. See http.ServeMux.Handle.
func (m *coarseMux) handle(pattern string, handler http.Handler) {
m.ServeMux.Handle(pattern, handlerDelegator{
delegate: handler,
pattern: pattern,
})
}

// Handle registers a handler to this CoarseMux. See http.ServeMux.HandleFunc.
func (m *coarseMux) handleFunc(pattern string, handler http.HandlerFunc) {
m.ServeMux.Handle(pattern, handlerDelegator{
delegate: handler,
pattern: pattern,
})
}

func newCoarseMux() *coarseMux {
return &coarseMux{
ServeMux: http.NewServeMux(),
}
}

// Handle registers a http.Handler to DefaultCoarseMux. See http.Handle.
func Handle(pattern string, handler http.Handler) {
DefaultCoarseMux.handle(pattern, handler)
}

// HandleFunc registers a handler to DefaultCoarseMux. See http.HandleFunc.
func HandleFunc(pattern string, handler http.HandlerFunc) {
DefaultCoarseMux.handleFunc(pattern, handler)
}

func init() {
registry.Register("http_requests_total", "A counter of the total number of HTTP requests made against the default multiplexor.", registry.NilLabels, requestCounts)
registry.Register("http_request_durations_total_microseconds", "The total amount of time the default multiplexor has spent answering HTTP requests (microseconds).", registry.NilLabels, requestDuration)
registry.Register("http_request_durations_microseconds", "The amounts of time the default multiplexor has spent answering HTTP requests (microseconds).", registry.NilLabels, requestDurations)
registry.Register("http_request_bytes_total", "The total volume of content body sizes received (bytes).", registry.NilLabels, requestBytes)
registry.Register("http_response_bytes_total", "The total volume of response payloads emitted (bytes).", registry.NilLabels, responseBytes)
}
11 changes: 11 additions & 0 deletions exp/documentation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) 2013, Matt T. Proud
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.

// A repository of various immature Prometheus client components that may
// assist in your use of the library. Items contained herein are regarded as
// especially interface unstable and may change without warning. Upon
// maturation, they should be migrated into a formal package for users.
package exp
Loading

0 comments on commit a087e01

Please sign in to comment.