mirror of
https://github.com/zrepl/zrepl.git
synced 2025-01-05 05:48:57 +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 {
|
type LoggingConfig struct {
|
||||||
Outlets *logger.Outlets
|
Outlets logger.Outlets
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetadataFlags int64
|
type MetadataFlags int64
|
||||||
@ -26,10 +26,9 @@ const (
|
|||||||
MetadataAll MetadataFlags = ^0
|
MetadataAll MetadataFlags = ^0
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoggerErrorOutlet() logger.Outlet {
|
func StdoutOutlet() logger.Outlet {
|
||||||
|
|
||||||
formatter := &HumanFormatter{}
|
formatter := &HumanFormatter{}
|
||||||
writer := os.Stderr
|
writer := os.Stdout
|
||||||
formatter.SetMetadataFlags(MetadataAll)
|
formatter.SetMetadataFlags(MetadataAll)
|
||||||
return WriterOutlet{
|
return WriterOutlet{
|
||||||
formatter, writer,
|
formatter, writer,
|
||||||
@ -39,8 +38,7 @@ func LoggerErrorOutlet() logger.Outlet {
|
|||||||
func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||||
|
|
||||||
c = &LoggingConfig{}
|
c = &LoggingConfig{}
|
||||||
|
c.Outlets = logger.NewOutlets()
|
||||||
c.Outlets = logger.NewOutlets(LoggerErrorOutlet())
|
|
||||||
|
|
||||||
var asList []interface{}
|
var asList []interface{}
|
||||||
if err = mapstructure.Decode(i, &asList); err != nil {
|
if err = mapstructure.Decode(i, &asList); err != nil {
|
||||||
|
@ -65,7 +65,7 @@ func init() {
|
|||||||
|
|
||||||
func testCmdGlobalInit(cmd *cobra.Command, args []string) {
|
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)
|
out.Add(WriterOutlet{&NoFormatter{}, os.Stdout}, logger.Info)
|
||||||
log := logger.NewLogger(out, 1*time.Second)
|
log := logger.NewLogger(out, 1*time.Second)
|
||||||
testCmdGlobal.log = log
|
testCmdGlobal.log = log
|
||||||
|
@ -27,6 +27,10 @@ Check out :sampleconf:`random/logging.yml` for an example on how to configure mu
|
|||||||
|
|
||||||
jobs: ...
|
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
|
Default Configuration
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
@ -41,10 +45,6 @@ By default, the following logging configuration is used
|
|||||||
level: "warn"
|
level: "warn"
|
||||||
format: "human"
|
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
|
Building Blocks
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -78,28 +78,32 @@ type Outlet interface {
|
|||||||
WriteEntry(ctx context.Context, entry Entry) error
|
WriteEntry(ctx context.Context, entry Entry) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Outlets struct {
|
type Outlets map[Level][]Outlet
|
||||||
reg map[Level][]Outlet
|
|
||||||
e Outlet
|
func NewOutlets() Outlets {
|
||||||
|
return make(map[Level][]Outlet, len(AllLevels))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOutlets(loggerErrorOutlet Outlet) *Outlets {
|
func (os Outlets) Add(outlet Outlet, minLevel Level) {
|
||||||
return &Outlets{
|
|
||||||
make(map[Level][]Outlet, len(AllLevels)),
|
|
||||||
loggerErrorOutlet,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (os *Outlets) Add(outlet Outlet, minLevel Level) {
|
|
||||||
for _, l := range AllLevels[minLevel:] {
|
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 {
|
func (os Outlets) Get(level Level) []Outlet {
|
||||||
return os.reg[level]
|
return os[level]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (os *Outlets) GetLoggerErrorOutlet() Outlet {
|
// Return the first outlet added to this Outlets list using Add()
|
||||||
return os.e
|
// 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 {
|
type Logger struct {
|
||||||
fields Fields
|
fields Fields
|
||||||
outlets *Outlets
|
outlets Outlets
|
||||||
outletTimeout time.Duration
|
outletTimeout time.Duration
|
||||||
|
|
||||||
mtx *sync.Mutex
|
mtx *sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogger(outlets *Outlets, outletTimeout time.Duration) *Logger {
|
func NewLogger(outlets Outlets, outletTimeout time.Duration) *Logger {
|
||||||
return &Logger{
|
return &Logger{
|
||||||
make(Fields, DefaultUserFieldCapacity),
|
make(Fields, DefaultUserFieldCapacity),
|
||||||
outlets,
|
outlets,
|
||||||
|
Loading…
Reference in New Issue
Block a user