aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOmar Ramadan <[email protected]>2024-06-10 08:03:24 -0700
committerGitHub <[email protected]>2024-06-10 09:03:24 -0600
commitd85cc2ec103de72658c55ba74197337c99bd1f74 (patch)
tree3632039ef94eb0963568df76aa9f542cdf949552
parent04fb9fe87ff7406b36a4a7f0a9215ab7a138d345 (diff)
downloadcaddy-d85cc2ec103de72658c55ba74197337c99bd1f74.tar.gz
caddy-d85cc2ec103de72658c55ba74197337c99bd1f74.zip
logging: Customizable zap cores (#6381)
-rw-r--r--caddyconfig/httpcaddyfile/builtins.go17
-rw-r--r--caddyconfig/httpcaddyfile/builtins_test.go6
-rw-r--r--logging.go12
-rw-r--r--modules/logging/cores.go36
4 files changed, 69 insertions, 2 deletions
diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go
index 35a08ef27..e1e95e00a 100644
--- a/caddyconfig/httpcaddyfile/builtins.go
+++ b/caddyconfig/httpcaddyfile/builtins.go
@@ -849,6 +849,7 @@ func parseInvoke(h Helper) (caddyhttp.MiddlewareHandler, error) {
// log <logger_name> {
// hostnames <hostnames...>
// output <writer_module> ...
+// core <core_module> ...
// format <encoder_module> ...
// level <level>
// }
@@ -960,6 +961,22 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
}
cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings)
+ case "core":
+ if !h.NextArg() {
+ return nil, h.ArgErr()
+ }
+ moduleName := h.Val()
+ moduleID := "caddy.logging.cores." + moduleName
+ unm, err := caddyfile.UnmarshalModule(h.Dispenser, moduleID)
+ if err != nil {
+ return nil, err
+ }
+ core, ok := unm.(zapcore.Core)
+ if !ok {
+ return nil, h.Errf("module %s (%T) is not a zapcore.Core", moduleID, unm)
+ }
+ cl.CoreRaw = caddyconfig.JSONModuleObject(core, "module", moduleName, h.warnings)
+
case "format":
if !h.NextArg() {
return nil, h.ArgErr()
diff --git a/caddyconfig/httpcaddyfile/builtins_test.go b/caddyconfig/httpcaddyfile/builtins_test.go
index 70f347dd9..cf7463484 100644
--- a/caddyconfig/httpcaddyfile/builtins_test.go
+++ b/caddyconfig/httpcaddyfile/builtins_test.go
@@ -25,11 +25,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
{
input: `:8080 {
log {
+ core mock
output file foo.log
}
}
`,
- 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"}}}}}}`,
+ 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"}}}}}}`,
expectError: false,
},
{
@@ -53,11 +54,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
{
input: `:8080 {
log name-override {
+ core mock
output file foo.log
}
}
`,
- 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"}}}}}}`,
+ 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"}}}}}}`,
expectError: false,
},
} {
diff --git a/logging.go b/logging.go
index d3e7bf32b..ca10beeed 100644
--- a/logging.go
+++ b/logging.go
@@ -292,6 +292,10 @@ type BaseLog struct {
// The encoder is how the log entries are formatted or encoded.
EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
+ // Tees entries through a zap.Core module which can extract
+ // log entry metadata and fields for further processing.
+ CoreRaw json.RawMessage `json:"core,omitempty" caddy:"namespace=caddy.logging.cores inline_key=module"`
+
// Level is the minimum level to emit, and is inclusive.
// Possible levels: DEBUG, INFO, WARN, ERROR, PANIC, and FATAL
Level string `json:"level,omitempty"`
@@ -366,6 +370,14 @@ func (cl *BaseLog) provisionCommon(ctx Context, logging *Logging) error {
cl.encoder = newDefaultProductionLogEncoder(cl.writerOpener)
}
cl.buildCore()
+ if cl.CoreRaw != nil {
+ mod, err := ctx.LoadModule(cl, "CoreRaw")
+ if err != nil {
+ return fmt.Errorf("loading log core module: %v", err)
+ }
+ core := mod.(zapcore.Core)
+ cl.core = zapcore.NewTee(cl.core, core)
+ }
return nil
}
diff --git a/modules/logging/cores.go b/modules/logging/cores.go
new file mode 100644
index 000000000..49aa7640c
--- /dev/null
+++ b/modules/logging/cores.go
@@ -0,0 +1,36 @@
+package logging
+
+import (
+ "go.uber.org/zap/zapcore"
+
+ "github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+)
+
+func init() {
+ caddy.RegisterModule(MockCore{})
+}
+
+// MockCore is a no-op module, purely for testing
+type MockCore struct {
+ zapcore.Core `json:"-"`
+}
+
+// CaddyModule returns the Caddy module information.
+func (MockCore) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ ID: "caddy.logging.cores.mock",
+ New: func() caddy.Module { return new(MockCore) },
+ }
+}
+
+func (lec *MockCore) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ return nil
+}
+
+// Interface guards
+var (
+ _ zapcore.Core = (*MockCore)(nil)
+ _ caddy.Module = (*MockCore)(nil)
+ _ caddyfile.Unmarshaler = (*MockCore)(nil)
+)