@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 10, 22:30 UTC

master
go 691 lines 24.2 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddy
16
17 import (
18 "context"
19 "encoding/json"
20 "fmt"
21 "log"
22 "log/slog"
23 "reflect"
24 "sync"
25
26 "github.com/caddyserver/certmagic"
27 "github.com/prometheus/client_golang/prometheus"
28 "github.com/prometheus/client_golang/prometheus/collectors"
29 "go.uber.org/zap"
30 "go.uber.org/zap/exp/zapslog"
31 "go.uber.org/zap/zapcore"
32
33 "github.com/caddyserver/caddy/v2/internal/filesystems"
34 )
35
36 // Context is a type which defines the lifetime of modules that
37 // are loaded and provides access to the parent configuration
38 // that spawned the modules which are loaded. It should be used
39 // with care and wrapped with derivation functions from the
40 // standard context package only if you don't need the Caddy
41 // specific features. These contexts are canceled when the
42 // lifetime of the modules loaded from it is over.
43 //
44 // Use NewContext() to get a valid value (but most modules will
45 // not actually need to do this).
46 type Context struct {
47 context.Context
48
49 moduleInstances map[string][]Module
50 cfg *Config
51 ancestry []Module
52 cleanupFuncs []func() // invoked at every config unload
53 exitFuncs []func(context.Context) // invoked at config unload ONLY IF the process is exiting (EXPERIMENTAL)
54 metricsRegistry *prometheus.Registry
55 }
56
57 // NewContext provides a new context derived from the given
58 // context ctx. Normally, you will not need to call this
59 // function unless you are loading modules which have a
60 // different lifespan than the ones for the context the
61 // module was provisioned with. Be sure to call the cancel
62 // func when the context is to be cleaned up so that
63 // modules which are loaded will be properly unloaded.
64 // See standard library context package's documentation.
65 func NewContext(ctx Context) (Context, context.CancelFunc) {
66 newCtx, cancelCause := NewContextWithCause(ctx)
67 return newCtx, func() { cancelCause(nil) }
68 }
69
70 // NewContextWithCause is like NewContext but returns a context.CancelCauseFunc.
71 // EXPERIMENTAL: This API is subject to change.
72 func NewContextWithCause(ctx Context) (Context, context.CancelCauseFunc) {
73 newCtx := Context{moduleInstances: make(map[string][]Module), cfg: ctx.cfg, metricsRegistry: prometheus.NewPedanticRegistry()}
74 c, cancel := context.WithCancelCause(ctx.Context)
75 wrappedCancel := func(cause error) {
76 cancel(cause)
77
78 for _, f := range ctx.cleanupFuncs {
79 f()
80 }
81
82 for modName, modInstances := range newCtx.moduleInstances {
83 for _, inst := range modInstances {
84 if cu, ok := inst.(CleanerUpper); ok {
85 err := cu.Cleanup()
86 if err != nil {
87 log.Printf("[ERROR] %s (%p): cleanup: %v", modName, inst, err)
88 }
89 }
90 }
91 }
92 }
93 newCtx.Context = c
94 newCtx.initMetrics()
95 return newCtx, wrappedCancel
96 }
97
98 // OnCancel executes f when ctx is canceled.
99 func (ctx *Context) OnCancel(f func()) {
100 ctx.cleanupFuncs = append(ctx.cleanupFuncs, f)
101 }
102
103 // FileSystems returns a ref to the FilesystemMap.
104 // EXPERIMENTAL: This API is subject to change.
105 func (ctx *Context) FileSystems() FileSystems {
106 // if no config is loaded, we use a default filesystemmap, which includes the osfs
107 if ctx.cfg == nil {
108 return &filesystems.FileSystemMap{}
109 }
110 return ctx.cfg.fileSystems
111 }
112
113 // Returns the active metrics registry for the context
114 // EXPERIMENTAL: This API is subject to change.
115 func (ctx *Context) GetMetricsRegistry() *prometheus.Registry {
116 return ctx.metricsRegistry
117 }
118
119 func (ctx *Context) initMetrics() {
120 ctx.metricsRegistry.MustRegister(
121 collectors.NewBuildInfoCollector(),
122 collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
123 collectors.NewGoCollector(),
124 adminMetrics.requestCount,
125 adminMetrics.requestErrors,
126 globalMetrics.configSuccess,
127 globalMetrics.configSuccessTime,
128 )
129 }
130
131 // OnExit executes f when the process exits gracefully.
132 // The function is only executed if the process is gracefully
133 // shut down while this context is active.
134 //
135 // EXPERIMENTAL API: subject to change or removal.
136 func (ctx *Context) OnExit(f func(context.Context)) {
137 ctx.exitFuncs = append(ctx.exitFuncs, f)
138 }
139
140 // LoadModule loads the Caddy module(s) from the specified field of the parent struct
141 // pointer and returns the loaded module(s). The struct pointer and its field name as
142 // a string are necessary so that reflection can be used to read the struct tag on the
143 // field to get the module namespace and inline module name key (if specified).
144 //
145 // The field can be any one of the supported raw module types: json.RawMessage,
146 // []json.RawMessage, map[string]json.RawMessage, or []map[string]json.RawMessage.
147 // ModuleMap may be used in place of map[string]json.RawMessage. The return value's
148 // underlying type mirrors the input field's type:
149 //
150 // json.RawMessage => any
151 // []json.RawMessage => []any
152 // [][]json.RawMessage => [][]any
153 // map[string]json.RawMessage => map[string]any
154 // []map[string]json.RawMessage => []map[string]any
155 //
156 // The field must have a "caddy" struct tag in this format:
157 //
158 // caddy:"key1=val1 key2=val2"
159 //
160 // To load modules, a "namespace" key is required. For example, to load modules
161 // in the "http.handlers" namespace, you'd put: `namespace=http.handlers` in the
162 // Caddy struct tag.
163 //
164 // The module name must also be available. If the field type is a map or slice of maps,
165 // then key is assumed to be the module name if an "inline_key" is NOT specified in the
166 // caddy struct tag. In this case, the module name does NOT need to be specified in-line
167 // with the module itself.
168 //
169 // If not a map, or if inline_key is non-empty, then the module name must be embedded
170 // into the values, which must be objects; then there must be a key in those objects
171 // where its associated value is the module name. This is called the "inline key",
172 // meaning the key containing the module's name that is defined inline with the module
173 // itself. You must specify the inline key in a struct tag, along with the namespace:
174 //
175 // caddy:"namespace=http.handlers inline_key=handler"
176 //
177 // This will look for a key/value pair like `"handler": "..."` in the json.RawMessage
178 // in order to know the module name.
179 //
180 // To make use of the loaded module(s) (the return value), you will probably want
181 // to type-assert each 'any' value(s) to the types that are useful to you
182 // and store them on the same struct. Storing them on the same struct makes for
183 // easy garbage collection when your host module is no longer needed.
184 //
185 // Loaded modules have already been provisioned and validated. Upon returning
186 // successfully, this method clears the json.RawMessage(s) in the field since
187 // the raw JSON is no longer needed, and this allows the GC to free up memory.
188 func (ctx Context) LoadModule(structPointer any, fieldName string) (any, error) {
189 val := reflect.ValueOf(structPointer).Elem().FieldByName(fieldName)
190 typ := val.Type()
191
192 field, ok := reflect.TypeOf(structPointer).Elem().FieldByName(fieldName)
193 if !ok {
194 panic(fmt.Sprintf("field %s does not exist in %#v", fieldName, structPointer))
195 }
196
197 opts, err := ParseStructTag(field.Tag.Get("caddy"))
198 if err != nil {
199 panic(fmt.Sprintf("malformed tag on field %s: %v", fieldName, err))
200 }
201
202 moduleNamespace, ok := opts["namespace"]
203 if !ok {
204 panic(fmt.Sprintf("missing 'namespace' key in struct tag on field %s", fieldName))
205 }
206 inlineModuleKey := opts["inline_key"]
207
208 var result any
209
210 // Unsupported field kinds return an error.
211 //exhaustive:ignore
212 switch val.Kind() {
213 case reflect.Slice:
214 if isJSONRawMessage(typ) {
215 // val is `json.RawMessage` ([]uint8 under the hood)
216
217 if inlineModuleKey == "" {
218 panic("unable to determine module name without inline_key when type is not a ModuleMap")
219 }
220 val, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, val.Interface().(json.RawMessage))
221 if err != nil {
222 return nil, err
223 }
224 result = val
225 } else if isJSONRawMessage(typ.Elem()) {
226 // val is `[]json.RawMessage`
227
228 if inlineModuleKey == "" {
229 panic("unable to determine module name without inline_key because type is not a ModuleMap")
230 }
231 var all []any
232 for i := 0; i < val.Len(); i++ {
233 val, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, val.Index(i).Interface().(json.RawMessage))
234 if err != nil {
235 return nil, fmt.Errorf("position %d: %v", i, err)
236 }
237 all = append(all, val)
238 }
239 result = all
240 } else if typ.Elem().Kind() == reflect.Slice && isJSONRawMessage(typ.Elem().Elem()) {
241 // val is `[][]json.RawMessage`
242
243 if inlineModuleKey == "" {
244 panic("unable to determine module name without inline_key because type is not a ModuleMap")
245 }
246 var all [][]any
247 for i := 0; i < val.Len(); i++ {
248 innerVal := val.Index(i)
249 var allInner []any
250 for j := 0; j < innerVal.Len(); j++ {
251 innerInnerVal, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, innerVal.Index(j).Interface().(json.RawMessage))
252 if err != nil {
253 return nil, fmt.Errorf("position %d: %v", j, err)
254 }
255 allInner = append(allInner, innerInnerVal)
256 }
257 all = append(all, allInner)
258 }
259 result = all
260 } else if isModuleMapType(typ.Elem()) {
261 // val is `[]map[string]json.RawMessage`
262
263 var all []map[string]any
264 for i := 0; i < val.Len(); i++ {
265 thisSet, err := ctx.loadModulesFromSomeMap(moduleNamespace, inlineModuleKey, val.Index(i))
266 if err != nil {
267 return nil, err
268 }
269 all = append(all, thisSet)
270 }
271 result = all
272 }
273
274 case reflect.Map:
275 // val is a ModuleMap or some other kind of map
276 result, err = ctx.loadModulesFromSomeMap(moduleNamespace, inlineModuleKey, val)
277 if err != nil {
278 return nil, err
279 }
280
281 default:
282 return nil, fmt.Errorf("unrecognized type for module: %s", typ)
283 }
284
285 // we're done with the raw bytes; allow GC to deallocate
286 val.Set(reflect.Zero(typ))
287
288 return result, nil
289 }
290
291 // emitEvent is a small convenience method so the caddy core can emit events, if the event app is configured.
292 func (ctx Context) emitEvent(name string, data map[string]any) Event {
293 if ctx.cfg == nil || ctx.cfg.eventEmitter == nil {
294 return Event{}
295 }
296 return ctx.cfg.eventEmitter.Emit(ctx, name, data)
297 }
298
299 // loadModulesFromSomeMap loads modules from val, which must be a type of map[string]any.
300 // Depending on inlineModuleKey, it will be interpreted as either a ModuleMap (key is the module
301 // name) or as a regular map (key is not the module name, and module name is defined inline).
302 func (ctx Context) loadModulesFromSomeMap(namespace, inlineModuleKey string, val reflect.Value) (map[string]any, error) {
303 // if no inline_key is specified, then val must be a ModuleMap,
304 // where the key is the module name
305 if inlineModuleKey == "" {
306 if !isModuleMapType(val.Type()) {
307 panic(fmt.Sprintf("expected ModuleMap because inline_key is empty; but we do not recognize this type: %s", val.Type()))
308 }
309 return ctx.loadModuleMap(namespace, val)
310 }
311
312 // otherwise, val is a map with modules, but the module name is
313 // inline with each value (the key means something else)
314 return ctx.loadModulesFromRegularMap(namespace, inlineModuleKey, val)
315 }
316
317 // loadModulesFromRegularMap loads modules from val, where val is a map[string]json.RawMessage.
318 // Map keys are NOT interpreted as module names, so module names are still expected to appear
319 // inline with the objects.
320 func (ctx Context) loadModulesFromRegularMap(namespace, inlineModuleKey string, val reflect.Value) (map[string]any, error) {
321 mods := make(map[string]any)
322 iter := val.MapRange()
323 for iter.Next() {
324 k := iter.Key()
325 v := iter.Value()
326 mod, err := ctx.loadModuleInline(inlineModuleKey, namespace, v.Interface().(json.RawMessage))
327 if err != nil {
328 return nil, fmt.Errorf("key %s: %v", k, err)
329 }
330 mods[k.String()] = mod
331 }
332 return mods, nil
333 }
334
335 // loadModuleMap loads modules from a ModuleMap, i.e. map[string]any, where the key is the
336 // module name. With a module map, module names do not need to be defined inline with their values.
337 func (ctx Context) loadModuleMap(namespace string, val reflect.Value) (map[string]any, error) {
338 all := make(map[string]any)
339 iter := val.MapRange()
340 for iter.Next() {
341 k := iter.Key().Interface().(string)
342 v := iter.Value().Interface().(json.RawMessage)
343 moduleName := namespace + "." + k
344 if namespace == "" {
345 moduleName = k
346 }
347 val, err := ctx.LoadModuleByID(moduleName, v)
348 if err != nil {
349 return nil, fmt.Errorf("module name '%s': %v", k, err)
350 }
351 all[k] = val
352 }
353 return all, nil
354 }
355
356 // LoadModuleByID decodes rawMsg into a new instance of mod and
357 // returns the value. If mod.New is nil, an error is returned.
358 // If the module implements Validator or Provisioner interfaces,
359 // those methods are invoked to ensure the module is fully
360 // configured and valid before being used.
361 //
362 // This is a lower-level method and will usually not be called
363 // directly by most modules. However, this method is useful when
364 // dynamically loading/unloading modules in their own context,
365 // like from embedded scripts, etc.
366 func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error) {
367 modulesMu.RLock()
368 modInfo, ok := modules[id]
369 modulesMu.RUnlock()
370 if !ok {
371 return nil, fmt.Errorf("unknown module: %s", id)
372 }
373
374 if modInfo.New == nil {
375 return nil, fmt.Errorf("module '%s' has no constructor", modInfo.ID)
376 }
377
378 val := modInfo.New()
379
380 // value must be a pointer for unmarshaling into concrete type, even if
381 // the module's concrete type is a slice or map; New() *should* return
382 // a pointer, otherwise unmarshaling errors or panics will occur
383 if rv := reflect.ValueOf(val); rv.Kind() != reflect.Pointer {
384 log.Printf("[WARNING] ModuleInfo.New() for module '%s' did not return a pointer,"+
385 " so we are using reflection to make a pointer instead; please fix this by"+
386 " using new(Type) or &Type notation in your module's New() function.", id)
387 val = reflect.New(rv.Type()).Elem().Addr().Interface().(Module)
388 }
389
390 // fill in its config only if there is a config to fill in
391 if len(rawMsg) > 0 {
392 err := StrictUnmarshalJSON(rawMsg, &val)
393 if err != nil {
394 return nil, fmt.Errorf("decoding module config: %s: %v", modInfo, err)
395 }
396 }
397
398 if val == nil {
399 // returned module values are almost always type-asserted
400 // before being used, so a nil value would panic; and there
401 // is no good reason to explicitly declare null modules in
402 // a config; it might be because the user is trying to achieve
403 // a result the developer isn't expecting, which is a smell
404 return nil, fmt.Errorf("module value cannot be null")
405 }
406
407 var err error
408
409 // if this is an app module, keep a reference to it,
410 // since submodules may need to reference it during
411 // provisioning (even though the parent app module
412 // may not be fully provisioned yet; this is the case
413 // with the tls app's automation policies, which may
414 // refer to the tls app to check if a global DNS
415 // module has been configured for DNS challenges)
416 if appModule, ok := val.(App); ok {
417 ctx.cfg.apps[id] = appModule
418 defer func() {
419 if err != nil {
420 ctx.cfg.failedApps[id] = err
421 }
422 }()
423 }
424
425 ctx.ancestry = append(ctx.ancestry, val)
426
427 if prov, ok := val.(Provisioner); ok {
428 err = prov.Provision(ctx)
429 if err != nil {
430 // incomplete provisioning could have left state
431 // dangling, so make sure it gets cleaned up
432 if cleanerUpper, ok := val.(CleanerUpper); ok {
433 err2 := cleanerUpper.Cleanup()
434 if err2 != nil {
435 err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
436 }
437 }
438 return nil, fmt.Errorf("provision %s: %v", modInfo, err)
439 }
440 }
441
442 if validator, ok := val.(Validator); ok {
443 err = validator.Validate()
444 if err != nil {
445 // since the module was already provisioned, make sure we clean up
446 if cleanerUpper, ok := val.(CleanerUpper); ok {
447 err2 := cleanerUpper.Cleanup()
448 if err2 != nil {
449 err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
450 }
451 }
452 return nil, fmt.Errorf("%s: invalid configuration: %v", modInfo, err)
453 }
454 }
455
456 ctx.moduleInstances[id] = append(ctx.moduleInstances[id], val)
457
458 // if the loaded module happens to be an app that can emit events, store it so the
459 // core can have access to emit events without an import cycle
460 if ee, ok := val.(eventEmitter); ok {
461 if _, ok := ee.(App); ok {
462 ctx.cfg.eventEmitter = ee
463 }
464 }
465
466 return val, nil
467 }
468
469 // loadModuleInline loads a module from a JSON raw message which decodes to
470 // a map[string]any, where one of the object keys is moduleNameKey
471 // and the corresponding value is the module name (as a string) which can
472 // be found in the given scope. In other words, the module name is declared
473 // in-line with the module itself.
474 //
475 // This allows modules to be decoded into their concrete types and used when
476 // their names cannot be the unique key in a map, such as when there are
477 // multiple instances in the map or it appears in an array (where there are
478 // no custom keys). In other words, the key containing the module name is
479 // treated special/separate from all the other keys in the object.
480 func (ctx Context) loadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (any, error) {
481 moduleName, raw, err := getModuleNameInline(moduleNameKey, raw)
482 if err != nil {
483 return nil, err
484 }
485
486 val, err := ctx.LoadModuleByID(moduleScope+"."+moduleName, raw)
487 if err != nil {
488 return nil, fmt.Errorf("loading module '%s': %v", moduleName, err)
489 }
490
491 return val, nil
492 }
493
494 // App returns the configured app named name. If that app has
495 // not yet been loaded and provisioned, it will be immediately
496 // loaded and provisioned. If no app with that name is
497 // configured, a new empty one will be instantiated instead.
498 // (The app module must still be registered.) This must not be
499 // called during the Provision/Validate phase to reference a
500 // module's own host app (since the parent app module is still
501 // in the process of being provisioned, it is not yet ready).
502 //
503 // We return any type instead of the App type because it is NOT
504 // intended for the caller of this method to be the one to start
505 // or stop App modules. The caller is expected to assert to the
506 // concrete type.
507 func (ctx Context) App(name string) (any, error) {
508 // if the app failed to load before, return the cached error
509 if err, ok := ctx.cfg.failedApps[name]; ok {
510 return nil, fmt.Errorf("loading %s app module: %v", name, err)
511 }
512 if app, ok := ctx.cfg.apps[name]; ok {
513 return app, nil
514 }
515 appRaw := ctx.cfg.AppsRaw[name]
516 modVal, err := ctx.LoadModuleByID(name, appRaw)
517 if err != nil {
518 return nil, fmt.Errorf("loading %s app module: %v", name, err)
519 }
520 if appRaw != nil {
521 ctx.cfg.AppsRaw[name] = nil // allow GC to deallocate
522 }
523 return modVal, nil
524 }
525
526 // AppIfConfigured is like App, but it returns an error if the
527 // app has not been configured. This is useful when the app is
528 // required and its absence is a configuration error; or when
529 // the app is optional and you don't want to instantiate a
530 // new one that hasn't been explicitly configured. If the app
531 // is not in the configuration, the error wraps ErrNotConfigured.
532 func (ctx Context) AppIfConfigured(name string) (any, error) {
533 if ctx.cfg == nil {
534 return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
535 }
536 // if the app failed to load before, return the cached error
537 if err, ok := ctx.cfg.failedApps[name]; ok {
538 return nil, fmt.Errorf("loading %s app module: %v", name, err)
539 }
540 if app, ok := ctx.cfg.apps[name]; ok {
541 return app, nil
542 }
543 appRaw := ctx.cfg.AppsRaw[name]
544 if appRaw == nil {
545 return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
546 }
547 return ctx.App(name)
548 }
549
550 // ErrNotConfigured indicates a module is not configured.
551 var ErrNotConfigured = fmt.Errorf("module not configured")
552
553 // Storage returns the configured Caddy storage implementation.
554 func (ctx Context) Storage() certmagic.Storage {
555 return ctx.cfg.storage
556 }
557
558 // Logger returns a logger that is intended for use by the most
559 // recent module associated with the context. Callers should not
560 // pass in any arguments unless they want to associate with a
561 // different module; it panics if more than 1 value is passed in.
562 //
563 // Originally, this method's signature was `Logger(mod Module)`,
564 // requiring that an instance of a Caddy module be passed in.
565 // However, that is no longer necessary, as the closest module
566 // most recently associated with the context will be automatically
567 // assumed. To prevent a sudden breaking change, this method's
568 // signature has been changed to be variadic, but we may remove
569 // the parameter altogether in the future. Callers should not
570 // pass in any argument. If there is valid need to specify a
571 // different module, please open an issue to discuss.
572 //
573 // PARTIALLY DEPRECATED: The Logger(module) form is deprecated and
574 // may be removed in the future. Do not pass in any arguments.
575 func (ctx Context) Logger(module ...Module) *zap.Logger {
576 if len(module) > 1 {
577 panic("more than 1 module passed in")
578 }
579 if ctx.cfg == nil {
580 // often the case in tests; just use a dev logger
581 l, err := zap.NewDevelopment()
582 if err != nil {
583 panic("config missing, unable to create dev logger: " + err.Error())
584 }
585 return l
586 }
587 mod := ctx.Module()
588 if len(module) > 0 {
589 mod = module[0]
590 }
591 if mod == nil {
592 return Log()
593 }
594 return ctx.cfg.Logging.Logger(mod)
595 }
596
597 type slogHandlerFactory func(handler slog.Handler, core zapcore.Core, moduleID string) slog.Handler
598
599 var (
600 slogHandlerFactories []slogHandlerFactory
601 slogHandlerFactoriesMu sync.RWMutex
602 )
603
604 // RegisterSlogHandlerFactory allows modules to register custom log/slog.Handler,
605 // for instance, to add contextual data to the logs.
606 func RegisterSlogHandlerFactory(factory slogHandlerFactory) {
607 slogHandlerFactoriesMu.Lock()
608 slogHandlerFactories = append(slogHandlerFactories, factory)
609 slogHandlerFactoriesMu.Unlock()
610 }
611
612 // Slogger returns a slog logger that is intended for use by
613 // the most recent module associated with the context.
614 func (ctx Context) Slogger() *slog.Logger {
615 var (
616 handler slog.Handler
617 core zapcore.Core
618 moduleID string
619 )
620
621 // the default enables traces at ERROR level, this disables
622 // them by setting it to a level higher than any other level
623 tracesOpt := zapslog.AddStacktraceAt(slog.Level(127))
624
625 if ctx.cfg == nil {
626 // often the case in tests; just use a dev logger
627 l, err := zap.NewDevelopment()
628 if err != nil {
629 panic("config missing, unable to create dev logger: " + err.Error())
630 }
631
632 core = l.Core()
633 handler = zapslog.NewHandler(core, tracesOpt)
634 } else {
635 mod := ctx.Module()
636 if mod == nil {
637 core = Log().Core()
638 handler = zapslog.NewHandler(core, tracesOpt)
639 } else {
640 moduleID = string(mod.CaddyModule().ID)
641 core = ctx.cfg.Logging.Logger(mod).Core()
642 handler = zapslog.NewHandler(core, zapslog.WithName(moduleID), tracesOpt)
643 }
644 }
645
646 slogHandlerFactoriesMu.RLock()
647 for _, f := range slogHandlerFactories {
648 handler = f(handler, core, moduleID)
649 }
650 slogHandlerFactoriesMu.RUnlock()
651
652 return slog.New(handler)
653 }
654
655 // Modules returns the lineage of modules that this context provisioned,
656 // with the most recent/current module being last in the list.
657 func (ctx Context) Modules() []Module {
658 mods := make([]Module, len(ctx.ancestry))
659 copy(mods, ctx.ancestry)
660 return mods
661 }
662
663 // Module returns the current module, or the most recent one
664 // provisioned by the context.
665 func (ctx Context) Module() Module {
666 if len(ctx.ancestry) == 0 {
667 return nil
668 }
669 return ctx.ancestry[len(ctx.ancestry)-1]
670 }
671
672 // WithValue returns a new context with the given key-value pair.
673 func (ctx *Context) WithValue(key, value any) Context {
674 return Context{
675 Context: context.WithValue(ctx.Context, key, value),
676 moduleInstances: ctx.moduleInstances,
677 cfg: ctx.cfg,
678 ancestry: ctx.ancestry,
679 cleanupFuncs: ctx.cleanupFuncs,
680 exitFuncs: ctx.exitFuncs,
681 metricsRegistry: ctx.metricsRegistry,
682 }
683 }
684
685 // eventEmitter is a small interface that inverts dependencies for
686 // the caddyevents package, so the core can emit events without an
687 // import cycle (i.e. the caddy package doesn't have to import
688 // the caddyevents package, which imports the caddy package).
689 type eventEmitter interface {
690 Emit(ctx Context, eventName string, data map[string]any) Event
691 }