Skip to content

Commit d85cc2e

Browse files
authored
logging: Customizable zap cores (#6381)
1 parent 04fb9fe commit d85cc2e

File tree

4 files changed

+69
-2
lines changed

4 files changed

+69
-2
lines changed

caddyconfig/httpcaddyfile/builtins.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@ func parseInvoke(h Helper) (caddyhttp.MiddlewareHandler, error) {
849849
// log <logger_name> {
850850
// hostnames <hostnames...>
851851
// output <writer_module> ...
852+
// core <core_module> ...
852853
// format <encoder_module> ...
853854
// level <level>
854855
// }
@@ -960,6 +961,22 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
960961
}
961962
cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings)
962963

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+
963980
case "format":
964981
if !h.NextArg() {
965982
return nil, h.ArgErr()

caddyconfig/httpcaddyfile/builtins_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
2525
{
2626
input: `:8080 {
2727
log {
28+
core mock
2829
output file foo.log
2930
}
3031
}
3132
`,
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"}}}}}}`,
3334
expectError: false,
3435
},
3536
{
@@ -53,11 +54,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
5354
{
5455
input: `:8080 {
5556
log name-override {
57+
core mock
5658
output file foo.log
5759
}
5860
}
5961
`,
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"}}}}}}`,
6163
expectError: false,
6264
},
6365
} {

logging.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,10 @@ type BaseLog struct {
292292
// The encoder is how the log entries are formatted or encoded.
293293
EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
294294

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+
295299
// Level is the minimum level to emit, and is inclusive.
296300
// Possible levels: DEBUG, INFO, WARN, ERROR, PANIC, and FATAL
297301
Level string `json:"level,omitempty"`
@@ -366,6 +370,14 @@ func (cl *BaseLog) provisionCommon(ctx Context, logging *Logging) error {
366370
cl.encoder = newDefaultProductionLogEncoder(cl.writerOpener)
367371
}
368372
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+
}
369381
return nil
370382
}
371383

modules/logging/cores.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
)

0 commit comments

Comments
 (0)