“This is the fifth day of my participation in the November Gwen Challenge. See details of the event: The Last Gwen Challenge 2021”.

The logging framework Logrus

Logrus is a structured logging framework, and its API is fully compatible with the Golang standard library logger logging API.

This means you can replace Logger directly with Logrus. Logrus has the following features:

  • Fully compatible withgolangStandard library log module:logrusThere are seven log levels:debug,info,warn,error,fatal,Panic and Trace, this isgolangStandard library log moduleAPIA superset of. If your project uses the standard library logging module, you can migrate to it at minimal costlogrusOn.
  • extensibleHookMechanism: Allows users to passhookTo distribute logs anywhere, such as the local file system, standard output,logstash,elasticsearchormqWait, or passhookDefine log content and format.
  • Optional log output formats:logrusThere are two built-in log formats,JSONFormatterandTextFormatterIf these two formats do not meet your requirements, you can implement the interface yourselfFormatterTo define your own log format.
  • FieldMechanism:logrusEncouraged byFieldMechanism for fine, structured logging, rather than logging through verbose messages.
  • logrusIs a pluggable, structured logging framework.

1 installation

go get github.com/sirupsen/logrus
Copy the code

2 First example

Logrus and golang log module is fully compatible with standard library, so you can use the log “github.com/sirupsen/logrus” replace all log import.

package main

import (
	"os"
	// Import the logrus log package, alias log
	log "github.com/sirupsen/logrus"
)

func main(a) {
	// Set the log level
	log.SetLevel(log.DebugLevel)
	// Set the log output to where
	// To print logs to standard output, that is, directly to the console.
	log.SetOutput(os.Stdout)
	// Set to true to show where the log is printed in the code
	//log.SetReportCaller(true)

	// Set logs to be output in JSON format. If logs are not output in text format by default
	log.SetFormatter(&log.JSONFormatter{})

	// Prints logs
	log.Debug("Debug Information")
	log.Info("Warning message")
	log.Warn("Warning Message")
	log.Error("Error message")
	// log.panic (" fatal error ")
	//
	Log. Fields is an alias of the map[string]interface{} type
	log.WithFields(log.Fields{
		"user_id":    1001."ip":         "192.168.0. 100"."request_id": "ec2bf8e55a11474392f8867e92624e04",
	}).Info("User login failed.")} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output as follows -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - {"level":"debug"."msg":"Debug Information"."time":"2020-03-06T23:52:41+08:00"}
{"level":"info"."msg":"Warning message"."time":"2020-03-06T23:52:41+08:00"}
{"level":"warning"."msg":"Warning Message"."time":"2020-03-06T23:52:41+08:00"}
{"level":"error"."msg":"Error message"."time":"2020-03-06T23:52:41+08:00"}
{"ip":"192.168.0. 100"."level":"info"."msg":"User login failed."."request_id":"ec2bf8e55a11474392f8867e92624e04"."time":"2020-03-06T23:52:41+08:00"."user_id":1001}
Copy the code

3 Logger

Logger is a relatively advanced use, and for a large project, you often need a global logrus instance, a Logger object, to log all the project logs. Such as

package main

import (
	"github.com/sirupsen/logrus"
	"os"
)

Logrus provides the New() function to create an instance of Logrus.
Any number of Logrus instances can be created in the project.
var log = logrus.New()

func main(a) {
	// Sets the message output for the current logrus instance, similarly,
	// You can set the output of logrus instances to any io.writer
	log.Out = os.Stdout

	// Set the message output format to JSON for the current Logrus instance.
	// It is also possible to set logging levels and hooks for a single Logrus instance, which is not described here.
	log.Formatter = &logrus.JSONFormatter{}
	Log. Fields is an alias of the map[string]interface{} type
	log.WithFields(logrus.Fields{
		"name":    "tom"."address": "chengdu",
	}).Info("Make a little progress every day")} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output as follows -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - {"address":"chengdu"."level":"info"."msg":"Make a little progress every day"."name":"tom"."time":"2020-03-07T00:01:31+08:00"}
Copy the code

4 Fields

Logrus encourages careful structured logging through log fields rather than verbose, unresolvable error messages. For example:

log.Fatalf("Failed to send event %s to topic %s with key %d", event, topic, key)
Copy the code

inlogrusI don’t think so.logrusThe following alternatives are encouraged:

log.WithFields(log.Fields{
  "event": event,
  "topic": topic,
  "key": key,
}).Fatal("Failed to send event")
Copy the code

The WithFields API above regulates the user to log in the way it advocates. But WithFields is still optional, because in some cases the user really only needs to record a simple message.

5 Hooks

One of logrus’s most exciting features is its extensible HOOK mechanism. Logrus can be extended by adding hooks to Logrus at initialization.

5.1 Hook Interface Definition

The hook interface for Logrus is defined as follows. The principle is to intercept and modify logrus.entry every time this is written to the log.

// Logrus triggers a HOOK when logging messages for logging Levels(),
// Modify logrus.entry as defined by the Fire method.
type Hook interface {
	Levels() []Level
	Fire(*Entry) error
}
Copy the code

5.2 Simple Hook Definition Example

A simple custom hook is as follows. DefaultFieldHook definition adds the default field appName=”myAppName” to log messages at all levels.

type DefaultFieldHook struct{}func (hook *DefaultFieldHook) Fire(entry *log.Entry) error {
    entry.Data["appName"] = "MyAppName"
    return nil
}

func (hook *DefaultFieldHook) Levels(a) []log.Level {
    return log.AllLevels
}
Copy the code

5.3 Hook Simple use

  • hookIs also simple to use, called before initializationlog.AddHook(hook)Add the correspondinghookCan.
  • logrusOfficial is only built insyslogthehook.
package main
import (
	log "github.com/sirupsen/logrus"
	"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
	logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
	"log/syslog"
)

func init(a) {

	// Use the Airbrake hook to report errors that have Error severity or above to
	// an exception tracker. You can create custom hooks, see the Hooks section.
	log.AddHook(airbrake.NewHook(123."xyz"."production"))

	hook, err := logrus_syslog.NewSyslogHook("udp"."localhost:514", syslog.LOG_INFO, "")
	iferr ! =nil {
		log.Error("Unable to connect to local syslog daemon")}else {
		log.AddHook(hook)
	}
}
Copy the code

6 Save logs to a file

How do we save logs to local files? As you saw in the previous example, you can use the SetOutput function to set where to save the log. Here’s how to save the log to a file.

package main

import (
	"github.com/sirupsen/logrus"
	"os"
)

Logrus provides the New() function to create an instance of Logrus.
Any number of Logrus instances can be created in the project.
var log = logrus.New()

func main(a) {
	// Open a log file
	file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err == nil {
		// Set the log output to a file
		log.SetOutput(file)
	} else {
		log.Info("Failed to open log file, default output to stderr")}// Prints logs
	log.Debug("Debug Information")
	log.Info("Warning message")
	log.Warn("Warning Message")
	log.Error("Error message")}Copy the code

7 Record the function name

If you want to add the name of the called function as a field, do the following:

log.SetReportCaller(true)
Copy the code

This will add the caller to method, as shown below:

{"name":"Tom"."level":"fatal"."method":"github.com/sirupsen/arcticcreatures.migrate"."msg":"a penguin swims by"."time":"The 2020-03-07 23:57:38. EDT, 562543129-0400"}
Copy the code

8 Set the log level

You can set the logging level on the Logger, and it will only log entries with any content of this level or higher.

// Record info and above levels (WARN, error, fatal, panic)
log.SetLevel(log.InfoLevel)
Copy the code

If your program supports debug or environment variable mode, setting log.Level = logrus.DebugLevel can be helpful.

9 Log local file splitting

Logrus itself does not have log local file splitting, but we can use file-rotatelogs for log local file splitting. Every time we write to the log, Logrus calls file-rotatelogs to determine whether the log should be shred. There are many examples of local log file splitting on the web, but I won’t go into detail here

package main

import (
	"github.com/lestrrat-go/file-rotatelogs"
	"github.com/pkg/errors"
	"github.com/rifflock/lfshook"
	log "github.com/sirupsen/logrus"
	"path"
	"time"
)

// It is recommended to use this one
func ConfigLocalFilesystemLogger(logPath string, logFileName string, maxAge time.Duration, rotationTime time.Duration) {
	baseLogPath := path.Join(logPath, logFileName)
	writer, err := rotatelogs.New(
		baseLogPath+"-%Y%m%d%H%M.log"./ / rotatelogs WithLinkName (baseLogPath), / / generated soft chain, pointing to the latest log file
		rotatelogs.WithMaxAge(maxAge),             // Maximum file retention time
		rotatelogs.WithRotationTime(rotationTime), // Log cutting interval
	)
	iferr ! =nil {
		log.Errorf("config local file system logger error. %+v", errors.WithStack(err))
	}
	lfHook := lfshook.NewHook(lfshook.WriterMap{
		log.DebugLevel: writer, // Set different output destinations for different levels
		log.InfoLevel:  writer,
		log.WarnLevel:  writer,
		log.ErrorLevel: writer,
		log.FatalLevel: writer,
		log.PanicLevel: writer,
	}, &log.TextFormatter{DisableColors: true})
	log.SetReportCaller(true) // Put the function name and the number of lines in the log
	log.AddHook(lfHook)
}

// Cut logs and clear expired logs
func ConfigLocalFilesystemLogger1(filePath string) {
	writer, err := rotatelogs.New(
		filePath+"-%Y%m%d%H%M.log",
		rotatelogs.WithLinkName(filePath),           // Generate a soft chain pointing to the latest log file
		rotatelogs.WithMaxAge(time.Second*60*3),     // Maximum file retention time
		rotatelogs.WithRotationTime(time.Second*60), // Log cutting interval
	)
	iferr ! =nil {
		log.Fatal("Init log failed, err:", err)
	}
	log.SetReportCaller(true) // Put the function name and the number of lines in the log
	log.SetOutput(writer)
	log.SetLevel(log.InfoLevel)
}

func main(a) {
	//ConfigLocalFilesystemLogger1("log")
	ConfigLocalFilesystemLogger("D:/benben"."sentalog", time.Second*60*3, time.Second*60)
	for {
        log.Debug("Debug Information")
        log.Info("Warning message")
        log.Warn("Warning Message")
        log.Error("Error message")
		time.Sleep(500 * time.Millisecond)
	}
}
Copy the code

10 Other precautions

10.1 Fatal processing

Like many logging frameworks, Logrus’ Fatal series of functions executes os.exit (1). But logrus provide can register one or more fatal handler function interface logrus. RegisterExitHandler (handler func () {}), let logrus in performing OS. Exit (1) before the corresponding processing. Fatal handlers can call some resource release API, etc., in the event of a system failure to shut down the application properly.

10.2 Thread Security

By default, Logrus’ apis are thread-safe, with mutex to protect concurrent writes. Mutex works when hooks are called or logs are written. If locks are not needed, they can be turned off by calling logger.setnolock (). Logrus mutex can be turned off when:

  • There is no sethookOr all of themhookBoth are thread-safe implementations.
  • Write the log tologger.OutIs already thread-safe, such aslogger.OutHas been locked, or when writing a file, the file isO_APPENDAnd each write operation is less than 4K.