0

I know that golang's log.New() can create a new Logger with it's own output writer.

But what happens if I give all 5 log.New() Logger the same io writer? Example: 5x Logger (INFO,WARN,etc.) logs to the same file, created with the lumberjack module or with os.OpenFile.

Would that create issues? (e.g. No concurrent writes?)

Or what would be the preferred method to create 5 new log functions that automatically prefix INFO/WARN/etc.?

Thanks! BR Marcus

2 Answers 2

1

The documentation says:

A Logger represents an active logging object that generates lines of output to an io.Writer. Each logging operation makes a single call to the Writer's Write method. A Logger can be used simultaneously from multiple goroutines; it guarantees to serialize access to the Writer.

A logger serializes access to it's writer, but there's no coordination between loggers.

A writer shared between loggers must support concurrent calls to Write and must not interleave data from those concurrent calls.

If the writer does not have those properties, wrap the writer with a type that adds synchronization.

type syncWriter struct {
    mu sync.Mutex
    w  io.Writer
}

func (sw *syncWriter) Write(p []byte) (int, error) {
    sw.mu.Lock()
    defer sw.mu.Unlock()
    return sw.w.Write(p)
}
Sign up to request clarification or add additional context in comments.

3 Comments

Does the default io.Writer from os.OpenFile have support for synchronization?
@marcus what kind of synchronization do you have in mind ? The net and os packages use concurrent safe file descriptor, order of logs should be correct most of the time but it isn’t guaranteed .
@Marcus An *os.File supports concurrent calls to Write. The package documentation is silent on the interleaving issue, but in practice it is not a problem.
1

There is plenty of logger implementations that supports log levels e. g. sirupsen/logrus.

To avoid another dependency wrap log.Logger and implement custom Info/Warn/Error methods. log.Logger is concurrent safe and there is no need to have multiple loggers.

Example:

type Logger struct {
    l *log.Logger
}

// You could use one string argument 
// func (l *Logger) Info(msg string) { 
//     l.l.Println(fmt.Sprintf("[INFO]: %s", msg))
// }
func (l *Logger) Info(args ...interface{}) { 
    args = append(make([]interface{}, 1, len(args)+1), args...)
    args[0] = "[INFO]"
    l.l.Println(args...)
}

func (l *Logger) Error(args ...interface{}) {
    args = append(make([]interface{}, 1, len(args)+1), args...)
    args[0] = "[ERROR]"
    l.l.Println(args...)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.