Why i do not use Golang Standard Logging

When dealing with logs in Go, I think many people use third-party logging libraries.

I think the reason is that the standard log package has poor functionality, especially no level.

However, in terms of Go language fundamentalism, you still want to use the standard.

Rest assured, the standard log package doesn’t have levels, but that doesn’t mean it doesn’t take levels into account.

It only differs in its role as a logging package from the regular log libraries found in other languages.

Regarding the log level, the log level is basically not unified , I think that there are various things such as no Debug Level, no Warning Level, and expression with a character string.

It is not a problem that they are not unified, and it is natural that the required log functions differ depending on the target of the program, so
you should be able to declare the level you originally need.

For example, for Unix commands, standard output and standard error are often sufficient.

On the other hand, when it gets too big, you will want a warning level.
The character string is a character string and it is a little complicated when it comes to switching the output destination.

However, adding features to accommodate any project at the package level will make the library bloated and lose its flexibility and simplicity.

The Go language log package is low-profile but takes over the minimal implementation.

If it is small, it can be used alone, but if you want something a little more elaborate, you may be able to connect to a Go language-like program by wrapping it to some extent instead of using an external library .

For example, the smaller scale is the official Go Playground source, and the
larger one is the upspin source of the google project developed by Dr. Rob Pike.

Go Playground

Let’s first look at the Go Playground implementation.
A small scale server program with no subpackages.
I have a file called logger.go that wraps a log package

type logger interface {
Printf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
}
// stdLogger implements the logger interface using the log package.
// There is no need to specify a date/time prefix since stdout and stderr
// are logged in StackDriver with those values already present.
type stdLogger struct {
stderr *stdlog.Logger
stdout *stdlog.Logger
}
func newStdLogger() *stdLogger {
return &stdLogger{
stdout: stdlog.New(os.Stdout, "", 0),
stderr: stdlog.New(os.Stderr, "", 0),
}
}
func (l *stdLogger) Printf(format string, args ...interface{}) {
l.stdout.Printf(format, args...)
}
func (l *stdLogger) Errorf(format string, args ...interface{}) {
l.stderr.Printf(format, args...)
}
func (l *stdLogger) Fatalf(format string, args ...interface{}) {
l.stderr.Fatalf(format, args...)
}

First, the logger interface is defined.
It is a common usage in Go language Interface, and it defines what is necessary so that the requester does not care about the implementation instead of hiding the implementation.

The contents are declared only for those used in the form of Printf for standard output, Errorf for standard error output, and Fatalf for standard error output + end.

And it handles the standard log package internally and leaves the format related to it, and what it is doing is just wrapping.

Don’t feel like you have to implement a logging library one by one.
This is like a declaration. It just declares the best logging API for this program.
However, it is troublesome to implement the logging library one by one, so let’s use an external library.

upspin

The following example is a full-scale project with lots of subpackages upspin.
This time, there is a log library as a subpackage.

It’s a bit off topic, but it’s not uncommon to create a wrapper for the standard library in Go.

Especially, there are many errors and logs. In such a case, some people may give it a name like Deep Throating Bomber, which is full of originality, or give it a name like errgo if you don’t want to put it on, but since

you are trying to do the same thing, just put it on. Give it a name by role. What you need is a package that handles errors and a package that handles logging, not logdragon or errgoblin. That is errors and log. What you want to do is explicit, isn’t it?

The story goes back to the upspin log package, but since it is long, I will post it halfway.

// Logger is the interface for logging messages.
type Logger interface {
// Printf writes a formated message to the log.
Printf(format string, v ...interface{})
// Print writes a message to the log.
Print(v ...interface{})
// Println writes a line to the log.
Println(v ...interface{})
// Fatal writes a message to the log and aborts.
Fatal(v ...interface{})
// Fatalf writes a formated message to the log and aborts.
Fatalf(format string, v ...interface{})
}
// Level represents the level of logging.
type Level int
// Different levels of logging.
const (
DebugLevel Level = iota
InfoLevel
ErrorLevel
DisabledLevel
)
// ExternalLogger describes a service that processes logs.
type ExternalLogger interface {
Log(Level, string)
Flush()
}
// The set of default loggers for each log level.
var (
Debug = &logger{DebugLevel}
Info = &logger{InfoLevel}
Error = &logger{ErrorLevel}
)

Please see. This time, the Error system has disappeared from Interface, and only the Print system and Fatal system are available.

Please see. We have logger instances for each level, and we have separate levels with and without abort.

Moreover, please see. Print system has all unmarked, + ln, + f, but Fatal system does not have + ln, only what you really need is defined. It’s clean. Yes, use an external library.

The upspin is recommended because it can be used as a reference in various ways other than logs.

Anyway, in the Go language, the poorness of the standard library is not a special problem if it puts a cushion between the log implementation and it is rather strong. I will use it instead of matching it with an external library, so let’s face it with the feeling that you should match it.

By preparing an Interface that defines what you need, you can ignore the circumstances of the library and implementation.

If you don’t use an external library, you can reduce the dependencies, and even if you use it, you can easily replace it if you have a cushion. For example, at the time of testing, you can replace * testing.T with the one that has * testing.T internally and maps the output function to the logger interface.
It’s hot.

Also, if you want a feature other than the log level, consider whether it’s not a log feature but some other role. In some cases, it can be solved by creating a wrapper for Writer that is passed to the log constructor.

Of course, if you are using a large framework or you do not need to do so much for logging, there are situations where it is better to use an external library such as execution speed, but Go language logging is a rare form, so there are options like this. I would appreciate it if you could know that.
Let’s use an external library.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store