Skip to content

Commit

Permalink
Instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
vfarcic committed Jul 18, 2017
1 parent 6da00f3 commit 8d31d94
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 86 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,3 +2,4 @@
/*.iml
/go-demo
/docker/registry/
/tmp
2 changes: 1 addition & 1 deletion Dockerfile.big
@@ -1,4 +1,4 @@
FROM golang:1.6 AS build
FROM golang:1.7 AS build
ADD . /src
WORKDIR /src
RUN go get -d -v -t
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.multistage
@@ -1,4 +1,4 @@
FROM golang:1.6 AS build
FROM golang:1.7 AS build
ADD . /src
WORKDIR /src
RUN go get -d -v -t
Expand Down
2 changes: 1 addition & 1 deletion integration_test.go
Expand Up @@ -33,7 +33,7 @@ import (

type IntegrationTestSuite struct {
suite.Suite
hostIp string
hostIp string
}

func (s *IntegrationTestSuite) SetupTest() {
Expand Down
77 changes: 58 additions & 19 deletions main.go
@@ -1,37 +1,46 @@
package main

import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"io"
"log"
"math/rand"
"net/http"
"os"
"strconv"
"time"
"gopkg.in/mgo.v2/bson"
"fmt"
"strings"
"math/rand"
"time"
)

var coll *mgo.Collection
var sleep = time.Sleep
var logFatal = log.Fatal
var logPrintf = log.Printf
var httpHandleFunc = http.HandleFunc
var httpListenAndServe = http.ListenAndServe

type Person struct {
Name string
}

// TODO: Test
var (
histogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "resp_time",
Help: "Request response time",
}, []string{"code", "method", "path", "query"})
)

func main() {
setupDb()
RunServer()
}

func init() {
prometheus.MustRegister(histogram)
}

// TODO: Test

func setupDb() {
Expand All @@ -47,13 +56,18 @@ func setupDb() {
}

func RunServer() {
httpHandleFunc("/demo/hello", HelloServer)
httpHandleFunc("/demo/person", PersonServer)
httpHandleFunc("/demo/random-error", RandomErrorServer)
logFatal("ListenAndServe: ", httpListenAndServe(":8080", nil))
mux := http.NewServeMux()
mux.HandleFunc("/demo/hello", HelloServer)
mux.HandleFunc("/demo/person", PersonServer)
mux.HandleFunc("/demo/random-error", RandomErrorServer)
mux.Handle("/metrics", prometheusHandler())
logFatal("ListenAndServe: ", httpListenAndServe(":8080", mux))
}

func HelloServer(w http.ResponseWriter, req *http.Request) {
start := time.Now()
defer func() { recordMetrics(start, req, http.StatusOK) }()

logPrintf("%s request to %s\n", req.Method, req.RequestURI)
delay := req.URL.Query().Get("delay")
if len(delay) > 0 {
Expand All @@ -64,28 +78,37 @@ func HelloServer(w http.ResponseWriter, req *http.Request) {
}

func RandomErrorServer(w http.ResponseWriter, req *http.Request) {
code := http.StatusOK
start := time.Now()
defer func() { recordMetrics(start, req, code) }()

logPrintf("%s request to %s\n", req.Method, req.RequestURI)
rand.Seed(time.Now().UnixNano())
n := rand.Intn(10)
msg := "Everything is still OK\n"
if n == 0 {
w.WriteHeader(http.StatusInternalServerError)
msg := "ERROR: Something, somewhere, went wrong!\n"
code = http.StatusInternalServerError
msg = "ERROR: Something, somewhere, went wrong!\n"
logPrintf(msg)
io.WriteString(w, msg)
} else {
io.WriteString(w, "Everything is still OK\n")
}
w.WriteHeader(code)
io.WriteString(w, msg)
}

func PersonServer(w http.ResponseWriter, req *http.Request) {
logPrintf("%s request to %s\n", req.Method, req.RequestURI)
code := http.StatusOK
start := time.Now()
defer func() { recordMetrics(start, req, code) }()

logPrintf("%s request to %s\n", req.Method, req.RequestURI)
msg := "Everything is OK"
if req.Method == "PUT" {
name := req.URL.Query().Get("name")
if _, err := upsertId(name, &Person{
Name: name,
}); err != nil {
panic(err)
code = http.StatusInternalServerError
msg = err.Error()
}
} else {
var res []Person
Expand All @@ -95,10 +118,15 @@ func PersonServer(w http.ResponseWriter, req *http.Request) {
var names []string
for _, p := range res {
names = append(names, p.Name)
io.WriteString(w, fmt.Sprintln(p.Name))
}
io.WriteString(w, strings.Join(names, "\n"))
msg = strings.Join(names, "\n")
}
w.WriteHeader(code)
io.WriteString(w, msg)
}

var prometheusHandler = func() http.Handler {
return prometheus.Handler()
}

var findPeople = func(res *[]Person) error {
Expand All @@ -109,3 +137,14 @@ var upsertId = func(id interface{}, update interface{}) (info *mgo.ChangeInfo, e
return coll.UpsertId(id, update)
}

func recordMetrics(start time.Time, req *http.Request, code int) {
duration := time.Since(start)
histogram.With(
prometheus.Labels{
"code": fmt.Sprintf("%d", code),
"method": req.Method,
"path": req.URL.Path,
"query": req.URL.RawQuery,
},
).Observe(duration.Seconds())
}
88 changes: 26 additions & 62 deletions main_test.go
Expand Up @@ -8,65 +8,42 @@ import (
"net/http"
"testing"
"time"
"reflect"
)

// Setup
// Suite

type MainTestSuite struct {
suite.Suite
}

func (s *MainTestSuite) SetupTest() {
}

// RunServer

func (s *MainTestSuite) Test_RunServer_InvokesHandleFuncWithHelloServer() {
httpHandleFuncOrig := httpHandleFunc
defer func() { httpHandleFunc = httpHandleFuncOrig }()
wasCalled := false
httpHandleFunc = func(pattern string, handler func(http.ResponseWriter, *http.Request)) {
if pattern == "/demo/hello" && reflect.ValueOf(handler) == reflect.ValueOf(HelloServer) {
wasCalled = true
}
}

RunServer()
// Suite

s.True(wasCalled)
func TestMainSuite(t *testing.T) {
logFatalOrig := logFatal
logPrintfOrig := logPrintf
httpListenAndServeOrig := httpListenAndServe
defer func() {
logFatal = logFatalOrig
logPrintf = logPrintfOrig
httpListenAndServe = httpListenAndServeOrig
}()
logFatal = func(v ...interface{}) {}
logPrintf = func(format string, v ...interface{}) {}
httpListenAndServe = func(addr string, handler http.Handler) error { return nil }
suite.Run(t, new(MainTestSuite))
}

func (s *MainTestSuite) Test_RunServer_InvokesHandleFuncWithPersonServer() {
httpHandleFuncOrig := httpHandleFunc
defer func() { httpHandleFunc = httpHandleFuncOrig }()
wasCalled := false
httpHandleFunc = func(pattern string, handler func(http.ResponseWriter, *http.Request)) {
if pattern == "/demo/person" && reflect.ValueOf(handler) == reflect.ValueOf(PersonServer) {
wasCalled = true
}
}

RunServer()
func (s *MainTestSuite) SetupTest() {}

s.True(wasCalled)
}
// init

func (s *MainTestSuite) Test_RunServer_InvokesHandleFuncWithRandomErrorServer() {
httpHandleFuncOrig := httpHandleFunc
defer func() { httpHandleFunc = httpHandleFuncOrig }()
wasCalled := false
httpHandleFunc = func(pattern string, handler func(http.ResponseWriter, *http.Request)) {
if pattern == "/demo/random-error" && reflect.ValueOf(handler) == reflect.ValueOf(RandomErrorServer) {
wasCalled = true
}
}

RunServer()

s.True(wasCalled)
func (s *MainTestSuite) Test_SetupMetrics_InitializesHistogram() {
s.NotNil(histogram)
}

// RunServer

func (s *MainTestSuite) Test_RunServer_InvokesListenAndServe() {
actual := ""
httpListenAndServe = func(addr string, handler http.Handler) error {
Expand Down Expand Up @@ -160,14 +137,16 @@ func (s *MainTestSuite) Test_PersonServer_Panics_WhenUpsertIdReturnsError() {
req, _ := http.NewRequest("PUT", "/demo/person?name=Viktor", nil)
w := getResponseWriterMock()

s.Panics(func() { PersonServer(w, req) })
PersonServer(w, req)

w.AssertCalled(s.T(), "Write", []byte("This is an error"))
}

func (s *MainTestSuite) Test_PersonServer_WritesPeople() {
findPeopleOrig := findPeople
people := []Person{
Person{Name: "Viktor"},
Person{Name: "Sara"},
{Name: "Viktor"},
{Name: "Sara"},
}
defer func() { findPeople = findPeopleOrig }()
findPeople = func(res *[]Person) error {
Expand All @@ -194,21 +173,6 @@ func (s *MainTestSuite) Test_PersonServer_Panics_WhenFindReturnsError() {
s.Panics(func() { PersonServer(w, req) })
}

// Suite

func TestMainSuite(t *testing.T) {
logFatalOrig := logFatal
defer func() { logFatal = logFatalOrig }()
logFatal = func(v ...interface{}) {}
logPrintfOrig := logPrintf
defer func() { logPrintf = logPrintfOrig }()
logPrintf = func(format string, v ...interface{}) {}
httpListenAndServeOrig := httpListenAndServe
defer func() { httpListenAndServe = httpListenAndServeOrig }()
httpListenAndServe = func(addr string, handler http.Handler) error { return nil }
suite.Run(t, new(MainTestSuite))
}

type ResponseWriterMock struct {
mock.Mock
}
Expand Down
4 changes: 2 additions & 2 deletions production_test.go
Expand Up @@ -7,14 +7,14 @@ import (
"github.com/stretchr/testify/suite"
"net/http"
"os"
"strconv"
"testing"
"time"
"strconv"
)

type ProductionTestSuite struct {
suite.Suite
hostIp string
hostIp string
}

func (s *ProductionTestSuite) SetupTest() {
Expand Down

0 comments on commit 8d31d94

Please sign in to comment.