Skip to content

Commit 9a714db

Browse files
committed
refactored debug logging
* relinted debugLog as debugLogf * fixes #244 (use logger.Debugf rather than logger.Printf) * fixes #118 (added support for injectable logger in Context and DefaultRouter) * removed global debug logger Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
1 parent 442694d commit 9a714db

4 files changed

Lines changed: 151 additions & 58 deletions

File tree

middleware/context.go

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,21 @@ import (
3737

3838
// Debug when true turns on verbose logging
3939
var Debug = logger.DebugEnabled()
40+
41+
// Logger is the standard libray logger used for printing debug messages
4042
var Logger logger.Logger = logger.StandardLogger{}
4143

42-
func debugLog(format string, args ...interface{}) { //nolint:goprintffuncname
43-
if Debug {
44-
Logger.Printf(format, args...)
44+
func debugLogfFunc(lg logger.Logger) func(string, ...any) {
45+
if logger.DebugEnabled() {
46+
if lg == nil {
47+
return Logger.Debugf
48+
}
49+
50+
return lg.Debugf
4551
}
52+
53+
// muted logger
54+
return func(_ string, _ ...any) {}
4655
}
4756

4857
// A Builder can create middlewares
@@ -75,10 +84,11 @@ func (fn ResponderFunc) WriteResponse(rw http.ResponseWriter, pr runtime.Produce
7584
// used throughout to store request context with the standard context attached
7685
// to the http.Request
7786
type Context struct {
78-
spec *loads.Document
79-
analyzer *analysis.Spec
80-
api RoutableAPI
81-
router Router
87+
spec *loads.Document
88+
analyzer *analysis.Spec
89+
api RoutableAPI
90+
router Router
91+
debugLogf func(string, ...any) // a logging function to debug context and all components using it
8292
}
8393

8494
type routableUntypedAPI struct {
@@ -191,7 +201,9 @@ func (r *routableUntypedAPI) DefaultConsumes() string {
191201
return r.defaultConsumes
192202
}
193203

194-
// NewRoutableContext creates a new context for a routable API
204+
// NewRoutableContext creates a new context for a routable API.
205+
//
206+
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
195207
func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Router) *Context {
196208
var an *analysis.Spec
197209
if spec != nil {
@@ -201,26 +213,40 @@ func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Ro
201213
return NewRoutableContextWithAnalyzedSpec(spec, an, routableAPI, routes)
202214
}
203215

204-
// NewRoutableContextWithAnalyzedSpec is like NewRoutableContext but takes in input the analysed spec too
216+
// NewRoutableContextWithAnalyzedSpec is like NewRoutableContext but takes as input an already analysed spec.
217+
//
218+
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
205219
func NewRoutableContextWithAnalyzedSpec(spec *loads.Document, an *analysis.Spec, routableAPI RoutableAPI, routes Router) *Context {
206220
// Either there are no spec doc and analysis, or both of them.
207221
if !((spec == nil && an == nil) || (spec != nil && an != nil)) {
208222
panic(errors.New(http.StatusInternalServerError, "routable context requires either both spec doc and analysis, or none of them"))
209223
}
210224

211-
ctx := &Context{spec: spec, api: routableAPI, analyzer: an, router: routes}
212-
return ctx
225+
return &Context{
226+
spec: spec,
227+
api: routableAPI,
228+
analyzer: an,
229+
router: routes,
230+
debugLogf: debugLogfFunc(nil),
231+
}
213232
}
214233

215-
// NewContext creates a new context wrapper
234+
// NewContext creates a new context wrapper.
235+
//
236+
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
216237
func NewContext(spec *loads.Document, api *untyped.API, routes Router) *Context {
217238
var an *analysis.Spec
218239
if spec != nil {
219240
an = analysis.New(spec.Spec())
220241
}
221-
ctx := &Context{spec: spec, analyzer: an}
242+
ctx := &Context{
243+
spec: spec,
244+
analyzer: an,
245+
router: routes,
246+
debugLogf: debugLogfFunc(nil),
247+
}
222248
ctx.api = newRoutableUntypedAPI(spec, api, ctx)
223-
ctx.router = routes
249+
224250
return ctx
225251
}
226252

@@ -284,6 +310,13 @@ func (c *Context) BasePath() string {
284310
return c.spec.BasePath()
285311
}
286312

313+
// SetLogger allows for injecting a logger to catch debug entries.
314+
//
315+
// The logger is enabled in DEBUG mode only.
316+
func (c *Context) SetLogger(lg logger.Logger) {
317+
c.debugLogf = debugLogfFunc(lg)
318+
}
319+
287320
// RequiredProduces returns the accepted content types for responses
288321
func (c *Context) RequiredProduces() []string {
289322
return c.analyzer.RequiredProduces()
@@ -301,6 +334,7 @@ func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, b
301334
if err != nil {
302335
res = append(res, err)
303336
} else {
337+
c.debugLogf("validating content type for %q against [%s]", ct, strings.Join(route.Consumes, ", "))
304338
if err := validateContentType(route.Consumes, ct); err != nil {
305339
res = append(res, err)
306340
}
@@ -399,16 +433,16 @@ func (c *Context) ResponseFormat(r *http.Request, offers []string) (string, *htt
399433
var rCtx = r.Context()
400434

401435
if v, ok := rCtx.Value(ctxResponseFormat).(string); ok {
402-
debugLog("[%s %s] found response format %q in context", r.Method, r.URL.Path, v)
436+
c.debugLogf("[%s %s] found response format %q in context", r.Method, r.URL.Path, v)
403437
return v, r
404438
}
405439

406440
format := NegotiateContentType(r, offers, "")
407441
if format != "" {
408-
debugLog("[%s %s] set response format %q in context", r.Method, r.URL.Path, format)
442+
c.debugLogf("[%s %s] set response format %q in context", r.Method, r.URL.Path, format)
409443
r = r.WithContext(stdContext.WithValue(rCtx, ctxResponseFormat, format))
410444
}
411-
debugLog("[%s %s] negotiated response format %q", r.Method, r.URL.Path, format)
445+
c.debugLogf("[%s %s] negotiated response format %q", r.Method, r.URL.Path, format)
412446
return format, r
413447
}
414448

@@ -471,7 +505,7 @@ func (c *Context) BindAndValidate(request *http.Request, matched *MatchedRoute)
471505
var rCtx = request.Context()
472506

473507
if v, ok := rCtx.Value(ctxBoundParams).(*validation); ok {
474-
debugLog("got cached validation (valid: %t)", len(v.result) == 0)
508+
c.debugLogf("got cached validation (valid: %t)", len(v.result) == 0)
475509
if len(v.result) > 0 {
476510
return v.bound, request, errors.CompositeValidationError(v.result...)
477511
}
@@ -483,7 +517,7 @@ func (c *Context) BindAndValidate(request *http.Request, matched *MatchedRoute)
483517
if len(result.result) > 0 {
484518
return result.bound, request, errors.CompositeValidationError(result.result...)
485519
}
486-
debugLog("no validation errors found")
520+
c.debugLogf("no validation errors found")
487521
return result.bound, request, nil
488522
}
489523

@@ -494,7 +528,7 @@ func (c *Context) NotFound(rw http.ResponseWriter, r *http.Request) {
494528

495529
// Respond renders the response after doing some content negotiation
496530
func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, data interface{}) {
497-
debugLog("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces)
531+
c.debugLogf("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces)
498532
offers := []string{}
499533
for _, mt := range produces {
500534
if mt != c.api.DefaultProduces() {
@@ -503,7 +537,7 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st
503537
}
504538
// the default producer is last so more specific producers take precedence
505539
offers = append(offers, c.api.DefaultProduces())
506-
debugLog("offers: %v", offers)
540+
c.debugLogf("offers: %v", offers)
507541

508542
var format string
509543
format, r = c.ResponseFormat(r, offers)

middleware/request.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import (
1919
"reflect"
2020

2121
"github.com/go-openapi/errors"
22+
"github.com/go-openapi/runtime"
23+
"github.com/go-openapi/runtime/logger"
2224
"github.com/go-openapi/spec"
2325
"github.com/go-openapi/strfmt"
24-
25-
"github.com/go-openapi/runtime"
2626
)
2727

2828
// UntypedRequestBinder binds and validates the data from a http request
@@ -31,6 +31,7 @@ type UntypedRequestBinder struct {
3131
Parameters map[string]spec.Parameter
3232
Formats strfmt.Registry
3333
paramBinders map[string]*untypedParamBinder
34+
debugLogf func(string, ...any) // a logging function to debug context and all components using it
3435
}
3536

3637
// NewUntypedRequestBinder creates a new binder for reading a request.
@@ -44,6 +45,7 @@ func NewUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Sw
4445
paramBinders: binders,
4546
Spec: spec,
4647
Formats: formats,
48+
debugLogf: debugLogfFunc(nil),
4749
}
4850
}
4951

@@ -52,10 +54,10 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara
5254
val := reflect.Indirect(reflect.ValueOf(data))
5355
isMap := val.Kind() == reflect.Map
5456
var result []error
55-
debugLog("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath())
57+
o.debugLogf("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath())
5658
for fieldName, param := range o.Parameters {
5759
binder := o.paramBinders[fieldName]
58-
debugLog("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath())
60+
o.debugLogf("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath())
5961
var target reflect.Value
6062
if !isMap {
6163
binder.Name = fieldName
@@ -102,3 +104,14 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara
102104

103105
return nil
104106
}
107+
108+
// SetLogger allows for injecting a logger to catch debug entries.
109+
//
110+
// The logger is enabled in DEBUG mode only.
111+
func (o *UntypedRequestBinder) SetLogger(lg logger.Logger) {
112+
o.debugLogf = debugLogfFunc(lg)
113+
}
114+
115+
func (o *UntypedRequestBinder) setDebugLogf(fn func(string, ...any)) {
116+
o.debugLogf = fn
117+
}

0 commit comments

Comments
 (0)