@marcel91200 / caddy Mirror · read-only / commits / d85cc2ec
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 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 +)