Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 12, 10:45 UTC
logging: Customizable zap cores (#6381)
Omar Ramadan committed
Jun 10, 2024 at 08:03 UTC
d85cc2ec103de72658c55ba74197337c99bd1f74
4 files changed
+69
-2
caddyconfig/httpcaddyfile/builtins.go
+17
@@ -849,6 +849,7 @@ func parseInvoke(h Helper) (caddyhttp.MiddlewareHandler, error) {
849
// log <logger_name> {
850
// hostnames <hostnames...>
851
// output <writer_module> ...
852
+// core <core_module> ...
853
// format <encoder_module> ...
854
// level <level>
855
// }
@@ -960,6 +961,22 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
961
}
962
cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings)
963
964
+ case "core":
965
+ if !h.NextArg() {
966
+ return nil, h.ArgErr()
967
+ }
968
+ moduleName := h.Val()
969
+ moduleID := "caddy.logging.cores." + moduleName
970
+ unm, err := caddyfile.UnmarshalModule(h.Dispenser, moduleID)
971
+ if err != nil {
972
+ return nil, err
973
+ }
974
+ core, ok := unm.(zapcore.Core)
975
+ if !ok {
976
+ return nil, h.Errf("module %s (%T) is not a zapcore.Core", moduleID, unm)
977
+ }
978
+ cl.CoreRaw = caddyconfig.JSONModuleObject(core, "module", moduleName, h.warnings)
979
+
980
case "format":
981
if !h.NextArg() {
982
return nil, h.ArgErr()
caddyconfig/httpcaddyfile/builtins_test.go
+4
-2
@@ -25,11 +25,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
25
{
26
input: `:8080 {
27
log {
28
+ core mock
29
output file foo.log
30
}
31
}
32
`,
32
- output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"writer":{"filename":"foo.log","output":"file"},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
33
+ output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
34
expectError: false,
35
},
36
{
@@ -53,11 +54,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
54
{
55
input: `:8080 {
56
log name-override {
57
+ core mock
58
output file foo.log
59
}
60
}
61
`,
60
- output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.name-override"]},"name-override":{"writer":{"filename":"foo.log","output":"file"},"include":["http.log.access.name-override"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"name-override"}}}}}}`,
62
+ output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.name-override"]},"name-override":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.name-override"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"name-override"}}}}}}`,
63
expectError: false,
64
},
65
} {
logging.go
+12
@@ -292,6 +292,10 @@ type BaseLog struct {
292
// The encoder is how the log entries are formatted or encoded.
293
EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
294
295
+ // Tees entries through a zap.Core module which can extract
296
+ // log entry metadata and fields for further processing.
297
+ CoreRaw json.RawMessage `json:"core,omitempty" caddy:"namespace=caddy.logging.cores inline_key=module"`
298
+
299
// Level is the minimum level to emit, and is inclusive.
300
// Possible levels: DEBUG, INFO, WARN, ERROR, PANIC, and FATAL
301
Level string `json:"level,omitempty"`
@@ -366,6 +370,14 @@ func (cl *BaseLog) provisionCommon(ctx Context, logging *Logging) error {
370
cl.encoder = newDefaultProductionLogEncoder(cl.writerOpener)
371
}
372
cl.buildCore()
373
+ if cl.CoreRaw != nil {
374
+ mod, err := ctx.LoadModule(cl, "CoreRaw")
375
+ if err != nil {
376
+ return fmt.Errorf("loading log core module: %v", err)
377
+ }
378
+ core := mod.(zapcore.Core)
379
+ cl.core = zapcore.NewTee(cl.core, core)
380
+ }
381
return nil
382
}
383
modules/logging/cores.go
new
+36
@@ -0,0 +1,36 @@
1
+package logging
2
+
3
+import (
4
+ "go.uber.org/zap/zapcore"
5
+
6
+ "github.com/caddyserver/caddy/v2"
7
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
8
+)
9
+
10
+func init() {
11
+ caddy.RegisterModule(MockCore{})
12
+}
13
+
14
+// MockCore is a no-op module, purely for testing
15
+type MockCore struct {
16
+ zapcore.Core `json:"-"`
17
+}
18
+
19
+// CaddyModule returns the Caddy module information.
20
+func (MockCore) CaddyModule() caddy.ModuleInfo {
21
+ return caddy.ModuleInfo{
22
+ ID: "caddy.logging.cores.mock",
23
+ New: func() caddy.Module { return new(MockCore) },
24
+ }
25
+}
26
+
27
+func (lec *MockCore) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
28
+ return nil
29
+}
30
+
31
+// Interface guards
32
+var (
33
+ _ zapcore.Core = (*MockCore)(nil)
34
+ _ caddy.Module = (*MockCore)(nil)
35
+ _ caddyfile.Unmarshaler = (*MockCore)(nil)
36
+)