@@ -37,12 +37,21 @@ import (
3737
3838// Debug when true turns on verbose logging
3939var Debug = logger .DebugEnabled ()
40+
41+ // Logger is the standard libray logger used for printing debug messages
4042var 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
7786type 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
8494type 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.
195207func 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.
205219func 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.
216237func 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
288321func (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
496530func (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 )
0 commit comments