Logrus for Go: golang logrus, levels, JSON, files, and hooks

Golang logrus and go logrus: install github.com/sirupsen/logrus, logrus log levels with SetLevel, TextFormatter and JSONFormatter, io.MultiWriter, SetReportCaller, hooks, and how long go.sum lines relate to the module path.

Published

Updated

Read time 5 min read

Reviewed byDeepak Prasad

Logrus for Go: golang logrus, levels, JSON, files, and hooks

Logrus is the widely used structured logger behind searches for logrus, golang logrus, logrus golang, go logrus, and logrus go. It lives at github.com/sirupsen/logrus (sometimes written sirupsen logrus). This guide gives a golang log example, explains logrus log levels, shows file and console output, JSON formatting, caller metadata, hooks, and how that relates to processing large line-oriented logs. For changing the standard library logger’s format, see change default log format. For removing a module later, see remove installed package with go get.

Tested with Go 1.24 on Linux and github.com/sirupsen/logrus v1.9.3.


Using logrus in Go (golang logrus, go logrus)

Add the module and import path

From your module root:

bash
go get github.com/sirupsen/logrus
text
go: added github.com/sirupsen/logrus v1.9.3
go: added golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8

Use exactly this import path in every file:

go
import "github.com/sirupsen/logrus"

If your editor shows a long hex token next to the module name, that is almost always a go.sum checksum or a pseudo-version string from the proxy—not something you paste into import paths.

A minimal golang log example

go
package main

import "github.com/sirupsen/logrus"

func main() {
	logrus.Println("Hello, world!")
}
bash
go run .
text
time="2026-06-17T22:57:37+05:30" level=info msg="Hello, world!"

The default is a text formatter with time, level, and msg keys. That is a practical go logrus baseline before you tune formatters or outputs.

Logrus log levels and SetLevel

Logrus defines PanicLevel, FatalLevel, ErrorLevel, WarnLevel, InfoLevel, DebugLevel, and TraceLevel with numeric ranks 06 (panic is most severe; trace is the chattiest). A line is emitted when its level is numerically less than or equal to the configured level, so InfoLevel shows panic through info but hides debug and trace until you raise the threshold.

With the threshold at TraceLevel, every line below is emitted:

go
package main

import "github.com/sirupsen/logrus"

func main() {
	logrus.SetLevel(logrus.TraceLevel)
	logrus.Traceln("Trace Level")
	logrus.Debugln("Debug Level")
	logrus.Infoln("Info Level")
	logrus.Warningln("Warning Level")
	logrus.Errorln("Error Level")
}
text
time="2026-06-17T22:57:46+05:30" level=trace msg="Trace Level"
time="2026-06-17T22:57:46+05:30" level=debug msg="Debug Level"
time="2026-06-17T22:57:46+05:30" level=info msg="Info Level"
time="2026-06-17T22:57:46+05:30" level=warning msg="Warning Level"
time="2026-06-17T22:57:46+05:30" level=error msg="Error Level"

Raising the bar to DebugLevel drops trace lines:

go
package main

import "github.com/sirupsen/logrus"

func main() {
	logrus.SetLevel(logrus.DebugLevel)
	logrus.Traceln("Trace Level")
	logrus.Debugln("Debug Level")
	logrus.Infoln("Info Level")
}
text
time="2026-06-17T22:57:53+05:30" level=debug msg="Debug Level"
time="2026-06-17T22:57:53+05:30" level=info msg="Info Level"

Fatal and Panic helpers still log and then stop the process (os.Exit or panic); omit them in long-running demos unless you expect the exit.


Writers: stdout, stderr, files, and io.MultiWriter

SetOutput controls where entries go. The default is os.Stderr; os.Stdout is common for containers. Open a file with os.OpenFile and append flags, then assign SetOutput(f) or combine sinks:

go
package main

import (
	"io"
	"os"

	"github.com/sirupsen/logrus"
)

func main() {
	f, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	logrus.SetOutput(io.MultiWriter(os.Stdout, f))
	logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
	logrus.SetLevel(logrus.InfoLevel)
	logrus.Info("both stdout and file")
}
text
time="2026-06-17T22:58:00+05:30" level=info msg="both stdout and file"

The same line should appear in app.log. For JSON pipelines or log hosts, switch the formatter in the next section instead of changing writers.


Structured fields, JSON, and text tweaks

Attach fields once with WithFields, or use WithField for a single pair:

go
package main

import "github.com/sirupsen/logrus"

func main() {
	logrus.SetFormatter(&logrus.JSONFormatter{})
	logrus.WithFields(logrus.Fields{"user": "ann"}).Info("login")
}
text
{"level":"info","msg":"login","time":"2026-06-17T22:58:05+05:30","user":"ann"}

TextFormatter accepts TimestampFormat, DisableColors, FullTimestamp, and FieldMap if you need syslog-style keys while staying human-readable. Colors only make sense when stderr is a terminal; see TTY notes if you run under systemd. For live service logs on Linux hosts, journalctl is often paired with JSON logs.


Caller metadata and hooks

SetReportCaller(true) adds file and function hints (exact shape depends on the formatter):

go
package main

import "github.com/sirupsen/logrus"

func main() {
	logrus.SetReportCaller(true)
	logrus.SetLevel(logrus.InfoLevel)
	logrus.Info("with caller")
}
text
time="2026-06-17T22:58:11+05:30" level=info msg="with caller" func=main.main file="/tmp/logrusdemo/main.go:10"

Hooks implement logrus.Hook (Levels + Fire) to fan out certain levels—errors to a file, metrics, or tracing backends. Keep hook work fast; offload heavy I/O to a goroutine with backpressure if needed.

go
package main

import (
	"fmt"
	"os"

	"github.com/sirupsen/logrus"
)

type errorFileHook struct{ f *os.File }

func (h *errorFileHook) Levels() []logrus.Level {
	return []logrus.Level{logrus.ErrorLevel}
}

func (h *errorFileHook) Fire(e *logrus.Entry) error {
	_, err := fmt.Fprintf(h.f, "%s %s\n", e.Time.Format("2006-01-02 15:04:05"), e.Message)
	return err
}

func main() {
	logger := logrus.New()
	f, err := os.OpenFile("errors.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	logger.AddHook(&errorFileHook{f: f})
	logger.Error("persisted via hook")
}

Large log files, goroutines, and timestamp type message lines

Some searches describe a program that processes a large log file where each line looks like timestamp type message. That task is plain Go parsing: stream with bufio.Scanner, push lines into a buffered channel, and let a small worker pool split CPU-heavy work. Logrus does not parse arbitrary legacy files; use it inside that tool to log progress (Info every N lines), malformed-line Warn events, or final summaries. Keep Scanner’s default line cap in mind for unusually wide lines.

Sketch:

go
// Pseudocode shape: scanner -> jobs chan -> worker goroutines -> aggregate results.
// Use logrus.New() in each worker or pass a logger to avoid global contention if needed.

Summary

Logrus gives Go developers a familiar golang logrus path: add github.com/sirupsen/logrus, start with text output, tune logrus log levels through SetLevel, and graduate to WithFields plus JSONFormatter when log hosts expect JSON. io.MultiWriter covers console-plus-file sinks, SetReportCaller improves debuggability, and hooks isolate side effects per level. Long sirupsen/logrus strings with hex come from module metadata, not imports. For huge timestamp type message files, combine normal concurrent Go I/O with logrus for observability of the parser itself—not for interpreting someone else’s format magically.


References


Frequently Asked Questions

1. Why do I see sirupsen logrus followed by a long hex string?

That is usually a checksum line from go.sum or a pseudo-version from the module proxy, not a different import path. In code you always import github.com/sirupsen/logrus.

2. How do logrus log levels work with SetLevel?

Levels use numeric ranks from PanicLevel (0, most severe) through TraceLevel (6, most verbose). A line is emitted when its level is less than or equal to the configured level, so raising the level toward trace shows more lines; lowering it toward panic shows fewer.

3. Should I use logrus to parse a huge log file with goroutines?

Logrus is for emitting logs from your program. Parsing timestamp type message lines is normal Go text I/O (bufio, channels, a worker pool). You can still use logrus in the parser to report progress or errors.

4. How do I log to a file and the console?

Point the logger at io.MultiWriter(os.Stdout, file) or pass the same multi-writer to logrus.New().
Antony Shikubu

Systems Integration Engineer

Highly skilled software developer with expertise in Python, Golang, and AWS cloud services.