embed: support multiple log outputs for structured logging
Zap support multi-writer by default.
@@ -23,6 +23,7 @@ import (
"net/url"
"os"
"path/filepath"
+ "sort"
"strings"
"sync"
"syscall"
@@ -394,7 +395,17 @@ func (cfg *Config) setupLogging() error {
}
case "zap":
- // TODO: make this more configurable
+ if cfg.LogOutput == "" {
+ cfg.LogOutput = DefaultLogOutput
+ }
+ outputs := strings.Split(cfg.LogOutput, ",")
+ for _, v := range outputs {
+ if v == DefaultLogOutput {
+ panic(fmt.Errorf("multi logoutput for %q is not supported yet", DefaultLogOutput))
+ }
+ }
+
+ // TODO: use zapcore to support more features?
lcfg := zap.Config{
Level: zap.NewAtomicLevelAt(zap.InfoLevel),
Development: false,
@@ -404,35 +415,50 @@ func (cfg *Config) setupLogging() error {
},
Encoding: "json",
EncoderConfig: zap.NewProductionEncoderConfig(),
+
+ OutputPaths: make([]string, 0),
+ ErrorOutputPaths: make([]string, 0),
}
- switch cfg.LogOutput {
+ outputPaths, errOutputPaths := make(map[string]struct{}), make(map[string]struct{})
+ for _, v := range outputs {
+ switch v {
case DefaultLogOutput:
if syscall.Getppid() == 1 {
// capnslog initially SetFormatter(NewDefaultFormatter(os.Stderr))
// where "NewDefaultFormatter" returns "NewJournaldFormatter"
// when syscall.Getppid() == 1, specify 'stdout' or 'stderr' to
// skip journald logging even when running under systemd
- fmt.Println("running under init, which may be systemd!")
// TODO: capnlog.NewJournaldFormatter()
- lcfg.OutputPaths = []string{"stderr"}
- lcfg.ErrorOutputPaths = []string{"stderr"}
- } else {
- lcfg.OutputPaths = []string{"stderr"}
- lcfg.ErrorOutputPaths = []string{"stderr"}
+ fmt.Println("running under init, which may be systemd!")
+ outputPaths["stderr"] = struct{}{}
+ errOutputPaths["stderr"] = struct{}{}
+ continue
}
+ outputPaths["stderr"] = struct{}{}
+ errOutputPaths["stderr"] = struct{}{}
+
case "stderr":
- lcfg.OutputPaths = []string{"stderr"}
- lcfg.ErrorOutputPaths = []string{"stderr"}
+ outputPaths["stderr"] = struct{}{}
+ errOutputPaths["stderr"] = struct{}{}
case "stdout":
- lcfg.OutputPaths = []string{"stdout"}
- lcfg.ErrorOutputPaths = []string{"stdout"}
+ outputPaths["stdout"] = struct{}{}
+ errOutputPaths["stdout"] = struct{}{}
default:
- lcfg.OutputPaths = []string{cfg.LogOutput}
- lcfg.ErrorOutputPaths = []string{cfg.LogOutput}
+ outputPaths[v] = struct{}{}
+ errOutputPaths[v] = struct{}{}
+ }
+ }
+ for v := range outputPaths {
+ lcfg.OutputPaths = append(lcfg.OutputPaths, v)
+ }
+ for v := range errOutputPaths {
+ lcfg.ErrorOutputPaths = append(lcfg.ErrorOutputPaths, v)
}
+ sort.Strings(lcfg.OutputPaths)
+ sort.Strings(lcfg.ErrorOutputPaths)
if cfg.Debug {
lcfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
