Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
name: CI

on: [push]
on: [push, pull_request]

jobs:
check:
name: Check
runs-on: ubuntu-latest
# Execute the checks inside the contianer instead the VM.
# Execute the checks inside the container instead the VM.
container: golangci/golangci-lint:v1.27.0-alpine
steps:
- uses: actions/checkout@v1
- run: golangci-lint run -E goimports

test:
name: Test
unit-test:
name: Unit test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-go@v1
with:
go-version: 1.14
- run: make test

integration-test:
name: Integration test
runs-on: ubuntu-latest
needs: [check, unit-test]
steps:
- uses: actions/checkout@v1
- uses: actions/setup-go@v1
with:
go-version: 1.14
- run: make integration-test
8 changes: 8 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---

run:
skip-dirs:
- test/integration/crd

build-tags:
- integration
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@

## [Unreleased]

Breaking change. The library has been refactored to be more flexible when adding new framework/libraries.

### Added

- New middleware helper for the Echo framework
- New middleware helper for the Echo framework.

### Changed

- Refactored internally how the Middleware works and gets the data to make it easier to extend and more reliable.
- Added `Reporter` interface as the service responsible of getting the data to be measured.
- All different framwork helpers now implement with the new Reporter way.
- Fixed Gin returning duplicated data (#31).
- Standard handler now is on `middleware/std` instead of `middleware`.

### Removed

- Middleware interface in favor of a struct.

## [0.6.1] - 2020-02-07

Expand Down
27 changes: 16 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@

UNIT_TEST_CMD := go test `go list ./... | grep -v vendor` -race
INTEGRATION_TEST_CMD := go test `go list ./... | grep -v vendor` -race -tags='integration'
UNIT_TEST_CMD := go test `go list ./... | grep -v test\/integration` -race
INTEGRATION_TEST_CMD := go test ./test/integration -race -tags='integration'
BENCHMARK_CMD := go test `go list ./... | grep -v vendor` -benchmem -bench=.
CHECK_CMD = golangci-lint run -E goimports
DEPS_CMD := go mod tidy
MOCKS_CMD := go generate ./internal/mocks

help: ## Show this help.
@echo "Help"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[93m %s\n", $$1, $$2}'


.PHONY: default
default: test

.PHONY: unit-test
unit-test:
unit-test: ## Execute unit tests.
$(UNIT_TEST_CMD)

.PHONY: integration-test
integration-test:
integration-test: ## Execute unit tests.
$(INTEGRATION_TEST_CMD)

.PHONY: test
test: integration-test
.PHONY: test ## Alias for unit tests.
test: unit-test

.PHONY: benchmark
benchmark:
benchmark: ## Execute benchmarks.
$(BENCHMARK_CMD)

.PHONY: check
check:
check: ## Execute check.
$(CHECK_CMD)

.PHONY: deps
deps:
deps: ## Tidy dependencies.
$(DEPS_CMD)

.PHONY: mocks
mocks:
mocks: ## Generates mocks.
$(MOCKS_CMD)

.PHONY: docs
docs:
docs: ## Runs docs example on :6060.
godoc -http=":6060"
82 changes: 65 additions & 17 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# go-http-metrics [![Build Status][github-actions-image]][github-actions-url] [![Go Report Card][goreport-image]][goreport-url] [![GoDoc][godoc-image]][godoc-url]

go-http-metrics knows how to measure http metrics in different metric formats. The metrics measured are based on [RED] and/or [Four golden signals], follow standards and try to be measured in a efficient way.

It measures based on a middleware that is compatible with Go core net/http handler, if you are using a framework that isn't directly compatible with go's `http.Handler` interface, do not worry, there are multiple helpers available to get middlewares for the most used http Go frameworks. If there isn't you can open an issue or a PR.
go-http-metrics knows how to measure http metrics in different metric formats. and Go HTTP framewrok/libs. The metrics measured are based on [RED] and/or [Four golden signals], follow standards and try to be measured in a efficient way.

## Table of contents

Expand Down Expand Up @@ -58,6 +56,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
"github.com/slok/go-http-metrics/middleware"
middlewarestd "github.com/slok/go-http-metrics/middleware/std"
)

func main() {
Expand All @@ -67,11 +66,11 @@ func main() {
})

// Our handler.
myHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("hello world!"))
})
h := mdlw.Handler("", myHandler)
h = middlewarestd.Measure("", mdlw, h)

// Serve metrics.
log.Printf("serving metrics at: %s", ":9090")
Expand All @@ -87,6 +86,67 @@ func main() {

For more examples check the [examples]. [default][default-example] and [custom][custom-example] are the examples for Go net/http std library users.

## V1 migration

From `v0` to `v1` the middleware API has changed (not the metrics API or metric implementations).

This changes was because `v0` started with `http.Handler` in mind, but then the support for some libs/middlwares were implemented, the way these frameworks/libraries are designed itself, are not compatible with go's `http.Handler`, but we want to support these and many more. So to be able to support these correctly and many more,an internal big refactor was required, this lead us to the need to leak a little bit of this internal refactor.

Here is how you can migrate the standard `http.Handler` from one to another (the other framework/lib migrations are straignforward).

Before:

```go
package main

import (
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
"github.com/slok/go-http-metrics/middleware"
)

func main() {
// Create our middleware.
mdlw := middleware.New(middleware.Config{
Recorder: metrics.NewRecorder(metrics.Config{}),
})

myHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("hello world!"))
})

// Measure handler.
h := mdlw.Handler("", myHandler)
}
```

After:

```go
package main

import (
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
"github.com/slok/go-http-metrics/middleware"
middlewarestd "github.com/slok/go-http-metrics/middleware/std"
)

func main() {
// Create our middleware.
mdlw := middleware.New(middleware.Config{
Recorder: metrics.NewRecorder(metrics.Config{}),
})

myHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("hello world!"))
})

// Measure handler.
h := middlewarestd.Measure("", mdlw, myHandler)
}
```

## Prometheus query examples

Get the request rate by handler:
Expand Down Expand Up @@ -196,18 +256,6 @@ Same options as the Prometheus recorder.

This Option is used to unregister the Recorder views before are being registered, this is option is mainly due to the nature of OpenCensus implementation and the huge usage fo global state making impossible to run multiple tests. On regular usage of the library this setting is very rare that needs to be used.

## Benchmarks

```text
pkg: github.com/slok/go-http-metrics/middleware

BenchmarkMiddlewareHandler/benchmark_with_default_settings.-4 1000000 1206 ns/op 256 B/op 6 allocs/op
BenchmarkMiddlewareHandler/benchmark_disabling_measuring_size.-4 1000000 1198 ns/op 256 B/op 6 allocs/op
BenchmarkMiddlewareHandler/benchmark_disabling_inflights.-4 1000000 1139 ns/op 256 B/op 6 allocs/op
BenchmarkMiddlewareHandler/benchmark_with_grouped_status_code.-4 1000000 1534 ns/op 256 B/op 7 allocs/op
BenchmarkMiddlewareHandler/benchmark_with_predefined_handler_ID-4 1000000 1258 ns/op 256 B/op 6 allocs/op
```

[github-actions-image]: https://github.com/slok/go-http-metrics/workflows/CI/badge.svg
[github-actions-url]: https://github.com/slok/go-http-metrics/actions
[goreport-image]: https://goreportcard.com/badge/github.com/slok/go-http-metrics
Expand Down
3 changes: 2 additions & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ the main Go net/http handler:
"github.com/prometheus/client_golang/prometheus/promhttp"
httpmetrics "github.com/slok/go-http-metrics/metrics/prometheus"
httpmiddleware "github.com/slok/go-http-metrics/middleware"
httpstdmiddleware "github.com/slok/go-http-metrics/middleware/std"
)

func main() {
Expand All @@ -24,7 +25,7 @@ the main Go net/http handler:
w.WriteHeader(http.StatusOK)
w.Write([]byte("hello world!"))
})
h := mdlw.Handler("", myHandler)
h := httpstdmiddleware.Measure("", mdlw, myHandler)

// Serve metrics.
log.Printf("serving metrics at: %s", ":9090")
Expand Down
9 changes: 5 additions & 4 deletions examples/custom/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
"github.com/slok/go-http-metrics/middleware"
"github.com/slok/go-http-metrics/middleware/std"
)

const (
Expand Down Expand Up @@ -50,10 +51,10 @@ func main() {
// Wrape our middleware on each of the different handlers with the ID of the handler
// this way we reduce the cardinality, for example: `/test/2` and `/test/4` will
// have the same `handler` label on the metric: `/test/:testID`
mux.Handle("/", mdlw.Handler("/", rooth))
mux.Handle("/test/1", mdlw.Handler("/test/:testID", testh))
mux.Handle("/test/2", mdlw.Handler("/test/:testID", testh2))
mux.Handle("/other-test", mdlw.Handler("/other-test", othetesth))
mux.Handle("/", std.Measure("/", mdlw, rooth))
mux.Handle("/test/1", std.Measure("/test/:testID", mdlw, testh))
mux.Handle("/test/2", std.Measure("/test/:testID", mdlw, testh2))
mux.Handle("/other-test", std.Measure("/other-test", mdlw, othetesth))

// Serve our handler.
go func() {
Expand Down
3 changes: 2 additions & 1 deletion examples/default/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
"github.com/slok/go-http-metrics/middleware"
"github.com/slok/go-http-metrics/middleware/std"
)

const (
Expand Down Expand Up @@ -45,7 +46,7 @@ func main() {

// Wrap our main handler, we pass empty handler ID so the middleware inferes
// the handler label from the URL.
h := mdlw.Handler("", mux)
h := std.Measure("", mdlw, mux)

// Serve our handler.
go func() {
Expand Down
7 changes: 5 additions & 2 deletions examples/echo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

metrics "github.com/slok/go-http-metrics/metrics/prometheus"
"github.com/slok/go-http-metrics/middleware"
echoMiddleware "github.com/slok/go-http-metrics/middleware/echo"
echomiddleware "github.com/slok/go-http-metrics/middleware/echo"
)

const (
Expand All @@ -28,12 +28,15 @@ func main() {

// Create Echo instance and global middleware.
e := echo.New()
e.Use(echoMiddleware.Handler("", mdlw))
e.Use(echomiddleware.Measure("", mdlw))

// Add our handler.
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello world")
})
e.GET("/json", func(c echo.Context) error {
return c.JSON(http.StatusAccepted, map[string]string{"hello": "world"})
})
e.GET("/wrong", func(c echo.Context) error {
return c.String(http.StatusTooManyRequests, "oops")
})
Expand Down
10 changes: 8 additions & 2 deletions examples/gin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,17 @@ func main() {

// Create Gin engine and global middleware.
engine := gin.New()
engine.Use(ginmiddleware.Handler("", mdlw))
engine.Use(ginmiddleware.Measure("", mdlw))

// Add our handler.
engine.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello world")
c.String(http.StatusOK, "Hello %s", "world")
})
engine.GET("/json", func(c *gin.Context) {
c.JSON(http.StatusAccepted, map[string]string{"hello": "world"})
})
engine.GET("/yaml", func(c *gin.Context) {
c.YAML(http.StatusCreated, map[string]string{"hello": "world"})
})
engine.GET("/wrong", func(c *gin.Context) {
c.String(http.StatusTooManyRequests, "oops")
Expand Down
2 changes: 1 addition & 1 deletion examples/gorestful/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func main() {
c := gorestful.NewContainer()

// Add the middleware for all routes.
c.Filter(gorestfulmiddleware.Handler("", mdlw))
c.Filter(gorestfulmiddleware.Measure("", mdlw))

// Add our handler.
ws := &gorestful.WebService{}
Expand Down
6 changes: 3 additions & 3 deletions examples/httprouter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ func main() {
r := httprouter.New()

// Add the middleware to each route.
r.GET("/", httproutermiddleware.Handler("/", h, mdlw))
r.GET("/test/:id", httproutermiddleware.Handler("/test/:id", h1, mdlw))
r.GET("/test2/:id", httproutermiddleware.Handler("/test2/:id", h2, mdlw))
r.GET("/", httproutermiddleware.Measure("/", h, mdlw))
r.GET("/test/:id", httproutermiddleware.Measure("/test/:id", h1, mdlw))
r.GET("/test2/:id", httproutermiddleware.Measure("/test2/:id", h2, mdlw))

// Serve our handler.
go func() {
Expand Down
2 changes: 1 addition & 1 deletion examples/negroni/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func main() {
n := negroni.Classic()

// Add the middleware to negroni.
n.Use(negronimiddleware.Handler("", mdlw))
n.Use(negronimiddleware.Measure("", mdlw))

// Finally set our router on negroni.
n.UseHandler(mux)
Expand Down
Loading