Skip to content
This repository was archived by the owner on Mar 16, 2021. It is now read-only.

Commit fab091d

Browse files
memorymxschmitt
authored andcommitted
add healthcheck handling (#106)
Add a special path -- `/ok` that can be used as a healthcheck for e.g. kubernetes or amazon ECS. When not in debug mode, do not generate logs for the healthcheck path. This requires implementing our own version of ginrus.Ginrus, as the upstream one does not support the `notlogged` argument(s) that gin.LoggerWithWriter has.
1 parent fbf2344 commit fab091d

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

handlers/handlers.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"net/http"
77
"time"
88

9-
"github.com/gin-gonic/contrib/ginrus"
109
"github.com/sirupsen/logrus"
1110

1211
"github.com/gin-gonic/gin"
@@ -27,6 +26,63 @@ type Handler struct {
2726
// DoNotPrivateKeyChecking is used for testing
2827
var DoNotPrivateKeyChecking = false
2928

29+
type loggerEntryWithFields interface {
30+
WithFields(fields logrus.Fields) *logrus.Entry
31+
}
32+
33+
// Ginrus returns a gin.HandlerFunc (middleware) that logs requests using logrus.
34+
//
35+
// Requests with errors are logged using logrus.Error().
36+
// Requests without errors are logged using logrus.Info().
37+
//
38+
// It receives:
39+
// 1. A time package format string (e.g. time.RFC3339).
40+
// 2. A boolean stating whether to use UTC time zone or local.
41+
// 3. Optionally, a list of paths to skip logging for (this is why
42+
// we are not using upstream github.com/gin-gonic/contrib/ginrus)
43+
func Ginrus(logger loggerEntryWithFields, timeFormat string, utc bool, notlogged ...string) gin.HandlerFunc {
44+
var skip map[string]struct{}
45+
if length := len(notlogged); length > 0 {
46+
skip = make(map[string]struct{}, length)
47+
for _, path := range notlogged {
48+
skip[path] = struct{}{}
49+
}
50+
}
51+
52+
return func(c *gin.Context) {
53+
start := time.Now()
54+
// some evil middlewares modify this values
55+
path := c.Request.URL.Path
56+
c.Next()
57+
58+
// log only when path is not being skipped
59+
if _, ok := skip[path]; !ok {
60+
end := time.Now()
61+
latency := end.Sub(start)
62+
if utc {
63+
end = end.UTC()
64+
}
65+
66+
entry := logger.WithFields(logrus.Fields{
67+
"status": c.Writer.Status(),
68+
"method": c.Request.Method,
69+
"path": path,
70+
"ip": c.ClientIP(),
71+
"latency": latency,
72+
"user-agent": c.Request.UserAgent(),
73+
"time": end.Format(timeFormat),
74+
})
75+
76+
if len(c.Errors) > 0 {
77+
// Append error field if this is an erroneous request.
78+
entry.Error(c.Errors.String())
79+
} else {
80+
entry.Info()
81+
}
82+
}
83+
}
84+
}
85+
3086
// New initializes the http handlers
3187
func New(store stores.Store) (*Handler, error) {
3288
if !util.GetConfig().EnableDebugMode {
@@ -78,7 +134,12 @@ func (h *Handler) setHandlers() error {
78134
if err := h.addTemplatesFromFS([]string{"token.html", "protected.html"}); err != nil {
79135
return errors.Wrap(err, "could not add templates from FS")
80136
}
81-
h.engine.Use(ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, false))
137+
if !util.GetConfig().EnableDebugMode {
138+
// if we are not in debug mode, do not log healthchecks
139+
h.engine.Use(Ginrus(logrus.StandardLogger(), time.RFC3339, false, "/ok"))
140+
} else {
141+
h.engine.Use(Ginrus(logrus.StandardLogger(), time.RFC3339, false))
142+
}
82143
protected := h.engine.Group("/api/v1/protected")
83144
if util.GetConfig().AuthBackend == "oauth" {
84145
logrus.Info("Using OAuth auth backend")
@@ -96,6 +157,7 @@ func (h *Handler) setHandlers() error {
96157

97158
h.engine.GET("/api/v1/info", h.handleInfo)
98159
h.engine.GET("/d/:id/:hash", h.handleDelete)
160+
h.engine.GET("/ok", h.handleHealthcheck)
99161

100162
// Handling the shorted URLs, if no one exists, it checks
101163
// in the filesystem and sets headers for caching

handlers/public.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ func (h *Handler) handleGetVisitors(c *gin.Context) {
139139
c.JSON(http.StatusOK, dataSets)
140140
}
141141

142+
// handleHealthcheck returns success for healthcheckers without polluting logs
143+
func (h *Handler) handleHealthcheck(c *gin.Context) {
144+
out := struct {
145+
Status string `json:"status"`
146+
}{
147+
"OK",
148+
}
149+
c.JSON(http.StatusOK, out)
150+
}
151+
142152
func (h *Handler) handleInfo(c *gin.Context) {
143153
out := struct {
144154
util.Info

0 commit comments

Comments
 (0)