mirror of
https://github.com/zrepl/zrepl.git
synced 2025-01-02 20:38:50 +01:00
logging: first outlet receives logger error message
Abandons stderr special-casing: * looks weird on shell and IO redirection to same file because of interleaving of stdout and stderr * better than a separate dedicated outlet because it does not require additional configuration fixes #28 BREAK SEMANTICS CONFIG
This commit is contained in:
parent
a7f70a566d
commit
2bfcfa5be8
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
type LoggingConfig struct {
|
||||
Outlets *logger.Outlets
|
||||
Outlets logger.Outlets
|
||||
}
|
||||
|
||||
type MetadataFlags int64
|
||||
@ -26,10 +26,9 @@ const (
|
||||
MetadataAll MetadataFlags = ^0
|
||||
)
|
||||
|
||||
func LoggerErrorOutlet() logger.Outlet {
|
||||
|
||||
func StdoutOutlet() logger.Outlet {
|
||||
formatter := &HumanFormatter{}
|
||||
writer := os.Stderr
|
||||
writer := os.Stdout
|
||||
formatter.SetMetadataFlags(MetadataAll)
|
||||
return WriterOutlet{
|
||||
formatter, writer,
|
||||
@ -39,8 +38,7 @@ func LoggerErrorOutlet() logger.Outlet {
|
||||
func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||
|
||||
c = &LoggingConfig{}
|
||||
|
||||
c.Outlets = logger.NewOutlets(LoggerErrorOutlet())
|
||||
c.Outlets = logger.NewOutlets()
|
||||
|
||||
var asList []interface{}
|
||||
if err = mapstructure.Decode(i, &asList); err != nil {
|
||||
|
@ -65,7 +65,7 @@ func init() {
|
||||
|
||||
func testCmdGlobalInit(cmd *cobra.Command, args []string) {
|
||||
|
||||
out := logger.NewOutlets(WriterOutlet{&NoFormatter{}, os.Stderr})
|
||||
out := logger.NewOutlets()
|
||||
out.Add(WriterOutlet{&NoFormatter{}, os.Stdout}, logger.Info)
|
||||
log := logger.NewLogger(out, 1*time.Second)
|
||||
testCmdGlobal.log = log
|
||||
|
@ -27,6 +27,10 @@ Check out :sampleconf:`random/logging.yml` for an example on how to configure mu
|
||||
|
||||
jobs: ...
|
||||
|
||||
.. ATTENTION::
|
||||
The **first outlet is special**: if an error writing to any outlet occurs, the first outlet receives the error and can print it.
|
||||
Thus, the first outlet must be the one that always works and does not block, e.g. ``stdout``, which is the default.
|
||||
|
||||
Default Configuration
|
||||
---------------------
|
||||
|
||||
@ -41,10 +45,6 @@ By default, the following logging configuration is used
|
||||
level: "warn"
|
||||
format: "human"
|
||||
|
||||
.. ATTENTION::
|
||||
Output to **stderr** should always be considered a **critical error**.
|
||||
Only errors in the logging infrastructure itself, e.g. IO errors when writing to an outlet, are sent to stderr.
|
||||
|
||||
Building Blocks
|
||||
---------------
|
||||
|
||||
|
@ -78,28 +78,32 @@ type Outlet interface {
|
||||
WriteEntry(ctx context.Context, entry Entry) error
|
||||
}
|
||||
|
||||
type Outlets struct {
|
||||
reg map[Level][]Outlet
|
||||
e Outlet
|
||||
type Outlets map[Level][]Outlet
|
||||
|
||||
func NewOutlets() Outlets {
|
||||
return make(map[Level][]Outlet, len(AllLevels))
|
||||
}
|
||||
|
||||
func NewOutlets(loggerErrorOutlet Outlet) *Outlets {
|
||||
return &Outlets{
|
||||
make(map[Level][]Outlet, len(AllLevels)),
|
||||
loggerErrorOutlet,
|
||||
}
|
||||
}
|
||||
|
||||
func (os *Outlets) Add(outlet Outlet, minLevel Level) {
|
||||
func (os Outlets) Add(outlet Outlet, minLevel Level) {
|
||||
for _, l := range AllLevels[minLevel:] {
|
||||
os.reg[l] = append(os.reg[l], outlet)
|
||||
os[l] = append(os[l], outlet)
|
||||
}
|
||||
}
|
||||
|
||||
func (os *Outlets) Get(level Level) []Outlet {
|
||||
return os.reg[level]
|
||||
func (os Outlets) Get(level Level) []Outlet {
|
||||
return os[level]
|
||||
}
|
||||
|
||||
func (os *Outlets) GetLoggerErrorOutlet() Outlet {
|
||||
return os.e
|
||||
// Return the first outlet added to this Outlets list using Add()
|
||||
// with minLevel <= Error.
|
||||
// If no such outlet is in this Outlets list, a discarding outlet is returned.
|
||||
func (os Outlets) GetLoggerErrorOutlet() Outlet {
|
||||
if len(os[Error]) < 1 {
|
||||
return nullOutlet{}
|
||||
}
|
||||
return os[Error][0]
|
||||
}
|
||||
|
||||
type nullOutlet struct{}
|
||||
|
||||
func (nullOutlet) WriteEntry(ctx context.Context, entry Entry) error { return nil }
|
||||
|
@ -17,13 +17,13 @@ const DefaultUserFieldCapacity = 5
|
||||
|
||||
type Logger struct {
|
||||
fields Fields
|
||||
outlets *Outlets
|
||||
outlets Outlets
|
||||
outletTimeout time.Duration
|
||||
|
||||
mtx *sync.Mutex
|
||||
}
|
||||
|
||||
func NewLogger(outlets *Outlets, outletTimeout time.Duration) *Logger {
|
||||
func NewLogger(outlets Outlets, outletTimeout time.Duration) *Logger {
|
||||
return &Logger{
|
||||
make(Fields, DefaultUserFieldCapacity),
|
||||
outlets,
|
||||
|
Loading…
Reference in New Issue
Block a user