Skip to content

Commit c6677b8

Browse files
Merge pull request #2 from sinhashubham95/improvements
V2 Go-Actuator
2 parents ad1b1e7 + 80e75ec commit c6677b8

88 files changed

Lines changed: 772 additions & 2169 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 60 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
[![Coverage Status](https://coveralls.io/repos/github/sinhashubham95/go-actuator/badge.svg?branch=master)](https://coveralls.io/github/sinhashubham95/go-actuator?branch=master)
77
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go#utilities)
88

9-
GO actuator configures the set of actuator endpoints for your application. It is compatible with [Fast HTTP](https://github.com/valyala/fasthttp), [GIN](https://github.com/gin-gonic/gin) and [NET/HTTP](https://pkg.go.dev/net/http).
9+
GO actuator configures the set of actuator endpoints for your application. It is very extensible and can be configured with `Go's native HTTP Server Mux`, or with any `3rd party web framework` as well.
1010

1111
## Project Versioning
1212

1313
Go actuator uses [semantic versioning](http://semver.org/). API should not change between patch and minor releases. New minor versions may add additional features to the API.
1414

1515
## Installation
1616

17-
To install Gin package, you need to install Go and set your Go workspace first.
17+
To install `Go Actuator` package, you need to install Go and set your Go workspace first.
1818

1919
1. The first need Go installed (version 1.13+ is required), then you can use the below Go command to install Go Actuator.
2020

@@ -30,43 +30,78 @@ import "github.com/sinhashubham95/go-actuator"
3030

3131
## How to Use
3232

33-
The actuator library is compatible with the most famous web frameworks. This is highly configurable and each endpoint can be enabled or disabled during initialization. You can also specify a prefix path for each of these configured endpoints(with default value `/actuator`).
33+
The actuator library exposes a plain native handler function, and it is the responsibility of the application to put this handler to use. This can be used either directly with `Go's native HTTP Server Mux`, or with any `3rd party web framework` as well.
3434

3535
### Configuration
3636

3737
The configuration contains the following:-
3838

39-
1. **Endpoints** - This is the list of endpoints which will be enabled. This is not a mandatory parameter. If not provided, then all the endpoints will be enabled. The possible endpoints are - `/env`, `/httpTrace`, `/info`, `/metrics`, `/ping`, `/shutdown` and `/threadDump`. You can find the description of each of these endpoints below.
40-
41-
2. **Prefix** - This is the prefix request path for all the configured endpoints.
39+
1. **Endpoints** - This is the list of endpoints which will be enabled. This is not a mandatory parameter. If not provided, then all the endpoints will be enabled. The possible endpoints are - `/env`, `/info`, `/metrics`, `/ping`, `/shutdown` and `/threadDump`. You can find the description of each of these endpoints below.
40+
2. **Env** - This is the environment where the application is running. For example, `dev`, `stg`, `prod`, etc.
41+
3. **Name** - This is the name of the application which is using this actuator library.
42+
4. **Port** - This is the port where the application is running.
43+
5. **Version** - This is the current application version.
4244

4345
```go
44-
import "github.com/sinhashubham95/go-actuator/models"
46+
import actuator "github.com/sinhashubham95/go-actuator"
4547

46-
config := &models.Config{
48+
config := &actuator.Config{
4749
Endpoints: []int{
48-
models.Env, models.HTTPTrace, models.Info, models.Metrics, models.Ping, models.Shutdown, models.ThreadDump
50+
actuator.Env,
51+
actuator.Info,
52+
actuator.Metrics,
53+
actuator.Ping,
54+
actuator.Shutdown,
55+
actuator.ThreadDump,
4956
},
50-
Prefix: "/actuator"
57+
Env: "dev",
58+
Name: "Naruto Rocks",
59+
Port: 8080,
60+
Version: "0.1.0",
5161
}
5262
```
5363

64+
### Using with [Go's Native Server Mux](https://pkg.go.dev/net/http)
65+
66+
```go
67+
import (
68+
actuator "github.com/sinhashubham95/go-actuator"
69+
"net/http"
70+
)
71+
72+
// create a server
73+
mux := &http.ServeMux{}
74+
75+
// get the handler for actuator
76+
actuatorHandler := actuator.GetActuatorHandler(&Config{})
77+
// configure the handler at this base endpoint
78+
mux.Handle("/actuator", actuatorHandler)
79+
80+
// configure other handlers
81+
....
82+
```
83+
5484
### Using with [Fast HTTP](https://github.com/valyala/fasthttp)
5585

5686
```go
5787
import (
88+
"strings"
89+
5890
"github.com/valyala/fasthttp"
5991
actuator "github.com/sinhashubham95/go-actuator"
60-
"github.com/sinhashubham95/go-actuator/models"
6192
)
6293

63-
actuatorHandler := actuator.GetFastHTTPActuatorHandler(&models.Config{})
94+
// get the handler for actuator
95+
actuatorHandler := fasthttp.NewFastHTTPHandlerFunc(actuator.GetActuatorHandler(&Config{}))
96+
97+
// create a fast http handler
6498
handler := func(ctx *fasthttp.RequestCtx) {
65-
switch(ctx.Path()) {
66-
// your configured paths
67-
default:
99+
if strings.HasPrefix(ctx.Path(), "/actuator") {
100+
// use the actuator handler
68101
actuatorHandler(ctx)
102+
return
69103
}
104+
// other request handler calls
70105
}
71106
fasthttp.ListenAndServe(":8080", handler)
72107
```
@@ -80,21 +115,16 @@ import (
80115
"github.com/sinhashubham95/go-actuator/models"
81116
)
82117

118+
// create the gin engine
83119
engine := gin.Default()
84-
actuator.ConfigureGINActuatorEngine(&models.Config{}, engine)
85-
```
86-
87-
### Using with [Net HTTP](https://pkg.go.dev/net/http)
88120

89-
```go
90-
import (
91-
actuator "github.com/sinhashubham95/go-actuator"
92-
"github.com/sinhashubham95/go-actuator/models"
93-
"net/http"
94-
)
121+
// get the handler for actuator
122+
actuatorHandler := actuator.GetActuatorHandler(&Config{})
123+
ginActuatorHandler := func(ctx *gin.Context) {
124+
actuatorHandler(ctx.Writer, ctx.Request)
125+
}
95126

96-
mux := &http.ServeMux{}
97-
actuator.ConfigureNetHTTPHandler(&models.Config{}, mux)
127+
engine.GET("/actuator/*endpoint", ginActuatorHandler)
98128
```
99129

100130
## Endpoints
@@ -105,7 +135,7 @@ This is used to get all the environment variables for the runtime where the appl
105135

106136
```shell
107137
go build
108-
./${APPLICATION_NAME} -env=${ENVIRONMENT_NAME}
138+
./${APPLICATION_NAME}
109139
```
110140

111141
```json
@@ -115,59 +145,6 @@ go build
115145
}
116146
```
117147

118-
### HTTP Trace - `/actuator/httpTrace`
119-
120-
This is used to get the trace for the last 100 HTTP requests to your application. Now if this has to be used, then there is an extra configuration has to be done based on the web framework in use.
121-
122-
```go
123-
import (
124-
"github.com/gin-gonic/gin"
125-
actuatorCore "github.com/sinhashubham95/go-actuator/core"
126-
"github.com/valyala/fasthttp"
127-
"net/http"
128-
)
129-
130-
// Using with Fast HTTP
131-
fasthttp.ListenAndServe(":8080", actuatorCore.WrapFastHTTPHandler(func (ctx *fasthttp.RequestCtx) {
132-
// handle your request
133-
}))
134-
135-
// Using with GIN
136-
router := gin.Default()
137-
router.Use(actuatorCore.GINTracer())
138-
139-
// Using with Net HTTP
140-
mux := &http.ServeMux{}
141-
mux.Handle("/route1", actuatorCore.WrapNetHTTPHandler(func (writer http.ResponseWriter, request *http.Request) {}))
142-
mux.Handle("/route2", actuatorCore.WrapNetHTTPHandler(func (writer http.ResponseWriter, request *http.Request) {}))
143-
```
144-
145-
```json
146-
[
147-
{
148-
"timestamp": "2019-08-05T19:28:36.353Z",
149-
"duration": 1234,
150-
"request": {
151-
"method": "GET",
152-
"url": "https://google.co.in",
153-
"headers": {
154-
"accept-language": [
155-
"en-GB,en-US;q=0.9,en;q=0.8"
156-
]
157-
}
158-
},
159-
"response": {
160-
"status": 200,
161-
"headers": {
162-
"content-type": [
163-
"application/json"
164-
]
165-
}
166-
}
167-
}
168-
]
169-
```
170-
171148
### Info - `/actuator/info`
172149

173150
This is used to get the basic information for an application. To get the correct and relevant information for your application you need to change the build script as well as the run script for your application as follows.
@@ -180,8 +157,8 @@ commitAuthor=$(git --no-pager show -s --format='%an <%ae>' "$commitId")
180157
gitUrl=$(git config --get remote.origin.url)
181158
userName=$(whoami)
182159
hostName=$(hostname)
183-
go build -ldflags " -X github.com/sinhashubham95/go-actuator/core.BuildStamp=$buildStamp -X github.com/sinhashubham95/go-actuator/core.GitCommitID=$commitId -X github.com/sinhashubham95/go-actuator/core.GitPrimaryBranch=$2 -X github.com/sinhashubham95/go-actuator/core.GitURL=$gitUrl -X github.com/sinhashubham95/go-actuator/core.Username=$userName -X github.com/sinhashubham95/go-actuator/core.HostName=$hostName -X \"github.com/sinhashubham95/go-actuator/core.GitCommitTime=$commitTime\" -X \"github.com/sinhashubham95/go-actuator/core.GitCommitAuthor=$commitAuthor\""
184-
./${APPLICATION_NAME} -env=${ENVIRONMENT_NAME} -name=${APPLICATION_NAME} -port=${APPLICATION_PORT} -version=${APPLICATION_VERSION}
160+
go build -ldflags " -X github.com/sinhashubham95/go-actuator.BuildStamp=$buildStamp -X github.com/sinhashubham95/go-actuator.GitCommitID=$commitId -X github.com/sinhashubham95/go-actuator.GitPrimaryBranch=$2 -X github.com/sinhashubham95/go-actuator.GitURL=$gitUrl -X github.com/sinhashubham95/go-actuator.Username=$userName -X github.com/sinhashubham95/go-actuator.HostName=$hostName -X \"github.com/sinhashubham95/go-actuator.GitCommitTime=$commitTime\" -X \"github.com/sinhashubham95/go-actuator.GitCommitAuthor=$commitAuthor\""
161+
./${APPLICATION_NAME}
185162
```
186163

187164
```json

‎actuator.go‎

Lines changed: 79 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,95 @@
11
package actuator
22

33
import (
4-
"github.com/gin-gonic/gin"
5-
"github.com/valyala/fasthttp"
4+
"fmt"
65
"net/http"
6+
)
77

8-
fastHTTPControllers "github.com/sinhashubham95/go-actuator/controllers/fasthttp"
9-
ginControllers "github.com/sinhashubham95/go-actuator/controllers/gin"
10-
netHTTPControllers "github.com/sinhashubham95/go-actuator/controllers/nethttp"
11-
"github.com/sinhashubham95/go-actuator/models"
8+
// Endpoints enumeration
9+
const (
10+
Env = iota
11+
Info
12+
Metrics
13+
Ping
14+
Shutdown
15+
ThreadDump
1216
)
1317

14-
// GetFastHTTPActuatorHandler is used to get the request handler for fast http
15-
func GetFastHTTPActuatorHandler(config *models.Config) fasthttp.RequestHandler {
16-
handleConfigs(config)
17-
return func(ctx *fasthttp.RequestCtx) {
18-
fastHTTPControllers.HandleRequest(config, ctx)
18+
// AllEndpoints is the list of endpoints supported
19+
var AllEndpoints = []int{Env, Info, Metrics, Ping, Shutdown, ThreadDump}
20+
21+
// Config is the set of configurable parameters for the actuator setup
22+
type Config struct {
23+
Endpoints []int
24+
Env string
25+
Name string
26+
Port int
27+
Version string
28+
}
29+
30+
func (config *Config) validate() {
31+
for _, endpoint := range config.Endpoints {
32+
if !isValidEndpoint(endpoint) {
33+
panic(fmt.Errorf("invalid endpoint %d provided", endpoint))
34+
}
1935
}
2036
}
2137

22-
// ConfigureGINActuatorEngine is used to configure the gin engine with the actuator handlers
23-
func ConfigureGINActuatorEngine(config *models.Config, engine *gin.Engine) {
24-
handleConfigs(config)
25-
ginControllers.ConfigureHandlers(config, engine)
38+
// Default is used to fill the default configs in case of any missing ones
39+
func (config *Config) setDefaults() {
40+
if config.Endpoints == nil {
41+
config.Endpoints = AllEndpoints
42+
}
2643
}
2744

28-
// ConfigureNetHTTPHandler is used to configure the net http mux with the actuator handlers
29-
func ConfigureNetHTTPHandler(config *models.Config, mux *http.ServeMux) {
45+
// GetActuatorHandler is used to get the handler function for the actuator endpoints
46+
// This single handler is sufficient for handling all the endpoints.
47+
func GetActuatorHandler(config *Config) http.HandlerFunc {
48+
if config == nil {
49+
config = &Config{}
50+
}
3051
handleConfigs(config)
31-
netHTTPControllers.ConfigureHandlers(config, mux)
52+
handlerMap := getHandlerMap(config)
53+
return func(writer http.ResponseWriter, request *http.Request) {
54+
if request.Method != http.MethodGet {
55+
// method not allowed for the requested resource
56+
sendStringResponse(writer, http.StatusMethodNotAllowed, methodNotAllowedError)
57+
return
58+
}
59+
endpoint := fmt.Sprintf("/%s", getLastStringAfterDelimiter(request.URL.Path, slash))
60+
if handler, ok := handlerMap[endpoint]; ok {
61+
handler(writer, request)
62+
return
63+
}
64+
// incorrect endpoint
65+
// or endpoint not enabled
66+
sendStringResponse(writer, http.StatusNotFound, notFoundError)
67+
}
3268
}
3369

34-
func handleConfigs(config *models.Config) {
35-
config.Validate()
36-
config.Default()
70+
func handleConfigs(config *Config) {
71+
config.validate()
72+
config.setDefaults()
73+
}
74+
75+
func getHandlerMap(config *Config) map[string]http.HandlerFunc {
76+
handlerMap := make(map[string]http.HandlerFunc, len(config.Endpoints))
77+
for _, e := range config.Endpoints {
78+
// now one by one add the handler of each endpoint
79+
switch e {
80+
case Env:
81+
handlerMap[envEndpoint] = getEnvHandler(config)
82+
case Info:
83+
handlerMap[infoEndpoint] = getInfoHandler(config)
84+
case Metrics:
85+
handlerMap[metricsEndpoint] = handleMetrics
86+
case Ping:
87+
handlerMap[pingEndpoint] = handlePing
88+
case Shutdown:
89+
handlerMap[shutdownEndpoint] = handleShutdown
90+
case ThreadDump:
91+
handlerMap[threadDumpEndpoint] = handleThreadDump
92+
}
93+
}
94+
return handlerMap
3795
}

0 commit comments

Comments
 (0)