Skip to content

Commit

Permalink
Logging Middleware: Add GitHub API Rate Limiting information
Browse files Browse the repository at this point in the history
  • Loading branch information
andygrunwald committed Jan 25, 2025
1 parent b9e479f commit b65a26f
Showing 1 changed file with 55 additions and 0 deletions.
55 changes: 55 additions & 0 deletions githubapp/middleware_logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,21 @@ import (
"io"
"net/http"
"regexp"
"strconv"
"time"

"github.com/gregjones/httpcache"
"github.com/rs/zerolog"
)

const (
headerRateLimit = "X-Ratelimit-Limit"
headerRateRemaining = "X-Ratelimit-Remaining"
headerRateUsed = "X-Ratelimit-Used"
headerRateReset = "X-Ratelimit-Reset"
headerRateResource = "X-Ratelimit-Resource"
)

// ClientLogging creates client middleware that logs request and response
// information at the given level. If the request fails without creating a
// response, it is logged with a status code of -1. The middleware uses a
Expand Down Expand Up @@ -83,6 +92,10 @@ func ClientLogging(lvl zerolog.Level, opts ...ClientLoggingOption) ClientMiddlew
Int64("size", -1)
}

if options.LogRateLimitInformation {
addRateLimitInformationToLog(evt, res)
}

evt.Msg("github_request")
return res, err
})
Expand All @@ -95,6 +108,9 @@ type ClientLoggingOption func(*clientLoggingOptions)
type clientLoggingOptions struct {
RequestBodyPatterns []*regexp.Regexp
ResponseBodyPatterns []*regexp.Regexp

// Output control
LogRateLimitInformation bool
}

// LogRequestBody enables request body logging for requests to paths matching
Expand All @@ -117,6 +133,22 @@ func LogResponseBody(patterns ...string) ClientLoggingOption {
}
}

// EnableRateLimitInformation enables logging of rate limit information like
// the number of requests remaining in the current rate limit window.
func EnableRateLimitInformation() ClientLoggingOption {
return func(opts *clientLoggingOptions) {
opts.LogRateLimitInformation = true
}
}

// DisableRateLimitInformation disables logging of rate limit information like
// the number of requests remaining in the current rate limit window.
func DisableRateLimitInformation() ClientLoggingOption {
return func(opts *clientLoggingOptions) {
opts.LogRateLimitInformation = false
}
}

func mirrorRequestBody(r *http.Request) (*http.Request, []byte, error) {
switch {
case r.Body == nil || r.Body == http.NoBody:
Expand Down Expand Up @@ -174,3 +206,26 @@ func requestMatches(r *http.Request, pats []*regexp.Regexp) bool {
func closeBody(b io.ReadCloser) {
_ = b.Close() // per http.Transport impl, ignoring close errors is fine
}

func addRateLimitInformationToLog(evt *zerolog.Event, res *http.Response) {
if limitHeader := res.Header.Get(headerRateLimit); limitHeader != "" {
limit, _ := strconv.Atoi(limitHeader)
evt.Int("ratelimit-limit", limit)
}
if remainingHeader := res.Header.Get(headerRateRemaining); remainingHeader != "" {
remaining, _ := strconv.Atoi(remainingHeader)
evt.Int("ratelimit-remaining", remaining)
}
if usedHeader := res.Header.Get(headerRateUsed); usedHeader != "" {
used, _ := strconv.Atoi(usedHeader)
evt.Int("ratelimit-used", used)
}
if resetHeader := res.Header.Get(headerRateReset); resetHeader != "" {
if v, _ := strconv.ParseInt(resetHeader, 10, 64); v != 0 {
evt.Time("ratelimit-reset", time.Unix(v, 0))
}
}
if resourceHeader := res.Header.Get(headerRateResource); resourceHeader != "" {
evt.Str("ratelimit-resource", resourceHeader)
}
}

0 comments on commit b65a26f

Please sign in to comment.