rotatorr

package module
v0.0.0-...-cd2abbd Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2023 License: MIT Imports: 9 Imported by: 6

README

Rotatorr

Go App Log Rotation!

GoDoc Go Report Card MIT License discord

Description

Rotatorr provides a simple io.WriteCloser you can plug into the default log package. This interface handles log rotation while providing many features and overridable interfaces to customize the rotation experience. Inspired by Lumberjack. I wrote this because I wanted integer log files, and I figured why not fix a few of the things reported in the Lumberjack issues and pull requests.

Simple Usage

This example rotates logs once they reach 10mb. The backup log files have a time stamp written to their name.

log.SetOutput(rotatorr.NewMust(&rotatorr.Config{
	Filesize: 1024 * 1024 * 10, // 10 megabytes
	Filepath: "/var/log/service.log",
	Rotatorr: &timerotator.Layout{FileCount: 10},
}))
Advanced Usage

In the example above you can see that the Rotatorr interface is satisfied by *timerotator.Layout. The other built-in option is *introtator.Layout.

As a version 0 package, some of the interfaces are bound to change as we find bugs and make further improvements. Feedback and bug reports are welcomed and encouraged!

The time rotator puts time stamps in the backup log file names. The int rotator uses an integer (like logfile.1.log). Pick one and stick with it for best results. You may also enable compression by adding a callback to either rotator that calls the included compressor library. All the advanced examples are in godoc, or just check out the examples_test.go file in this repo and the example app that's included. Below you'll find the three main data structures you can provide to this package to make log file rotation work just the way you want.

Type: rotatorr.Config

All of the struct members are optional except the Rotatorr interface.

type Config struct {
	Filepath string        // Full path to log file. Set this, the default is lousy.
	FileMode os.FileMode   // POSIX mode for new files.
	DirMode  os.FileMode   // POSIX mode for new folders.
	Every    time.Duration // Maximum log file age. Rotate every hour or day, etc.
	FileSize int64         // Maximum log file size in bytes. Default is unlimited (no rotation).
	Rotatorr Rotatorr      // REQUIRED: Custom log Rotatorr. Use your own or one of the provided interfaces.
}
Type: introtator.Layout
  • Rotatorr interface.
type Layout struct {
	ArchiveDir string // Location where rotated backup logs are moved to.
	FileCount  int    // Maximum number of rotated log files.
	FileOrder  Order  // Control the order of the integer-named backup log files.
	PostRotate func(fileName, newFile string)
}
Type: timerotator.Layout
  • Rotatorr interface.
type Layout struct {
	ArchiveDir string        // Location where rotated backup logs are moved to.
	FileCount  int           // Maximum number of rotated log files.
	FileAge    time.Duration // Maximum age of rotated files.
	UseUTC     bool          // Sets the time zone to UTC when writing Time Formats (backup files).
	Format     string        // Format for Go Time. Used as the name.
	Joiner     string        // The string betwene the file name prefix and time stamp. Default: -
	PostRotate func(fileName, newFile string)
}

Documentation

Overview

Package rotatorr is a log rotation module designed to plug directly into a standard go logger. It provides an input interface to handle log rotation, naming, and compression. Three additional modules packages are included to facilitate backup-file naming in different formats, and log compression.

The New() methods return a simple io.WriteCloser that works with most log packages. This interface handles log rotation while providing many features and overridable interfaces to customize the rotation experience. Inspired by Lumberjack: https://github.com/natefinch/lumberjack.

Use this package if you write your own log file, and you're tired of your log file growing indefinitely. The included `introtatorr` and `timerotator` modules allow a variety of naming conventions for backup files. They also include options to delete old files based on age, count, or both.

https://pkg.go.dev/golift.io/rotatorr/introtator
https://pkg.go.dev/golift.io/rotatorr/timerotator
Example (Compressor)

This is a simple example that enables log compression. Enabling compression on "Ascending Integer" log files is not recommend because it's possible for a log to be rotated (renamed) while being compressed. This is best utilized on Descending Integer or Time-based log files. Of course, these are all interfaces you can override, so customize away! The called CompressPostRotate procedure runs a compression in the background, and prints a log message when it completes.

package main

import (
	"log"

	"golift.io/rotatorr"
	"golift.io/rotatorr/compressor"
	"golift.io/rotatorr/timerotator"
)

func main() {
	log.SetOutput(rotatorr.NewMust(&rotatorr.Config{
		Filepath: "/var/log/file.log",
		FileSize: 100 * 1024 * 1024, // 100 megabytes.
		Rotatorr: &timerotator.Layout{
			PostRotate: compressor.CompressPostRotate,
		},
	}))
}
Output:

Example (CompressorCaptureOutput)

Example_compressor_capture shows how to capture the response from a post-rotate compression so you can do whatever you want with it.

package main

import (
	"log"

	"golift.io/rotatorr"
	"golift.io/rotatorr/compressor"
	"golift.io/rotatorr/timerotator"
)

func main() {
	logger, err := rotatorr.New(&rotatorr.Config{
		Filepath: "/var/log/file.log",
		FileSize: 100 * 1024 * 1024, // 100 megabytes.
		Rotatorr: &timerotator.Layout{
			PostRotate: func(_, fileName string) {
				compressor.CompressBackground(fileName, func(report *compressor.Report) {
					if report.Error != nil {
						log.Printf("[Rotatorr] Error: %v", report.Error)
					} else {
						log.Printf("[Rotatorr] Compressed: %s -> %s", report.OldFile, report.NewFile)
					}
				})
			},
		},
	})
	if err != nil {
		panic(err)
	}

	log.SetOutput(logger)
}
Output:

Example (CompressorWithLog)

Example_compressor_log shows how to format a post-rotate compression log line.

package main

import (
	"log"

	"golift.io/rotatorr"
	"golift.io/rotatorr/compressor"
	"golift.io/rotatorr/timerotator"
)

func main() {
	post := func(_, fileName string) {
		printf := func(msg string, v ...interface{}) {
			log.Printf("[Rotatorr] %s", v...)
		}
		compressor.CompressBackgroundWithLog(fileName, printf)
	}

	log.SetOutput(rotatorr.NewMust(&rotatorr.Config{
		Filepath: "/var/log/file.log",
		FileSize: 10 * 1024 * 1024, // 10 megabytes.
		Rotatorr: &timerotator.Layout{PostRotate: post},
	}))
}
Output:

Example (Lumberjack)

This examples shows how to create backup logs files just like https://github.com/natefinch/lumberjack. This will rotate files at 100Mb and keep all old files (no cleanup). Backup log files are named with a time stamp. Compression isn't enabled.

package main

import (
	"log"

	"golift.io/rotatorr"
	"golift.io/rotatorr/timerotator"
)

func main() {
	log.SetOutput(rotatorr.NewMust(&rotatorr.Config{
		Filepath: "/var/log/file.log", // optional.
		FileSize: 100 * 1024 * 1024,   // 100 megabytes.
		DirMode:  0o755,               // world-readable.
		Rotatorr: &timerotator.Layout{
			FileAge:   0,    // keep all backup files (default).
			FileCount: 0,    // keep all backup files (default).
			UseUTC:    true, // not the default.
		},
	}))
}
Output:

Example (PostRotateLog)

This example demonstrates how to trigger an action after a file is rotated. All of the struct members for rotatorr.Config and timerotator.Layout are shown.

package main

import (
	"log"
	"time"

	"golift.io/rotatorr"
	"golift.io/rotatorr/timerotator"
)

func main() {
	const (
		TenMB  = 10 * 1024 * 1024
		OneDay = time.Hour * 24
		Month  = time.Hour * 24 * 30
		Keep   = 10
	)

	postRotate := func(fileName, newFile string) {
		// This must run in a go routine or a deadlock will occur when calling log.Printf.
		// If you're doing things besides logging, you do not need a go routine, but this
		// function blocks logs, so make it snappy.
		go func() {
			log.Printf("file rotated: %s -> %s", fileName, newFile)
		}()
	}

	rotator, err := rotatorr.New(&rotatorr.Config{
		Filepath: "/var/log/file.log", // not required, but recommended.
		FileSize: TenMB,               // 10 megabytes.
		FileMode: rotatorr.FileMode,   // default: 0600
		DirMode:  rotatorr.DirMode,    // default: 0750
		Every:    OneDay,              // rotate every day
		Rotatorr: &timerotator.Layout{ // required.
			FileCount:  Keep,                      // keep 10 files
			FileAge:    Month,                     // delete files older than 30 days
			Format:     timerotator.FormatDefault, // This is the default Time Format.
			ArchiveDir: "/var/log/archives",       // override backup log file location.
			PostRotate: postRotate,                // optional post-rotate function.
			UseUTC:     false,                     // default is false.
			Joiner:     "-",                       // prefix and time stamp separator.
			Filer:      nil,                       // use default: os.Remove
		},
	})
	if err != nil {
		panic(err)
	}

	log.SetOutput(rotator)
}
Output:

Index

Examples

Constants

View Source
const (
	FileMode os.FileMode = 0o600
	DirMode  os.FileMode = 0o750
)

These are the default directory and log file POSIX modes.

View Source
const DefaultMaxSize = 10 * 1024 * 1024

DefaultMaxSize is only used when Every and FileSize Config struct members are omitted.

Variables

View Source
var (
	ErrWriteTooLarge = fmt.Errorf("log msg length exceeds max file size")
	ErrNilInterface  = fmt.Errorf("nil Rotatorr interface provided")
)

Custom errors returned by this package.

Functions

This section is empty.

Types

type Config

type Config struct {
	Rotatorr Rotatorr      // REQUIRED: Custom log Rotatorr. Use your own or one of the provided interfaces.
	Filepath string        // Full path to log file. Set this, the default is lousy.
	FileMode os.FileMode   // POSIX mode for new files.
	DirMode  os.FileMode   // POSIX mode for new folders.
	Every    time.Duration // Maximum log file age. Rotate every hour or day, etc.
	FileSize int64         // Maximum log file size in bytes. Default is unlimited (no rotation).
}

Config is the data needed to create a new Log Rotatorr.

type Logger

type Logger struct {
	File        *os.File // The active open file. Useful for direct writing.
	Interface   Rotatorr // copied from config for brevity.
	filer.Filer          // overridable file system procedures.
	// contains filtered or unexported fields
}

Logger is what you get in return for providing a Config. Use this to set log output. You must obtain a Logger by calling one of the New() procedures.

func New

func New(config *Config) (*Logger, error)

New takes in your configuration and returns a Logger you can use with log.SetOutput(). The provided logger handles log rotation and dispatching post-actions like compression.

Example
package main

import (
	"log"

	"golift.io/rotatorr"
	"golift.io/rotatorr/introtator"
)

func main() {
	rotator, err := rotatorr.New(&rotatorr.Config{
		Filepath: "/var/log/service.log",
		Rotatorr: &introtator.Layout{FileCount: 10},
	})
	if err != nil {
		panic(err)
	}

	log.SetOutput(rotator)
}
Output:

func NewMust

func NewMust(config *Config) *Logger

NewMust takes in your configuration and returns a Logger you can use with log.SetOutput(). If an error occurs opening the log file, making log directories, or rotating files it is ignored (and retried later). Do not pass a Nil Rotatorr.

Example
package main

import (
	"log"

	"golift.io/rotatorr"
	"golift.io/rotatorr/timerotator"
)

func main() {
	log.SetOutput(rotatorr.NewMust(&rotatorr.Config{
		FileSize: 1024 * 1024 * 100, // 100 megabytes
		Filepath: "/var/log/service.log",
		Rotatorr: &timerotator.Layout{FileCount: 10},
	}))
}
Output:

func (*Logger) Close

func (l *Logger) Close() error

Close stops the go routines, closes the active log file session and all channels. If another Write() is sent, a panic will ensue.

func (*Logger) Rotate

func (l *Logger) Rotate() (int64, error)

Rotate forces the log to rotate immediately. Returns the size of the rotated log.

Example

Rotate a log on SIGHUP signal.

package main

import (
	"log"
	"os"
	"os/signal"
	"syscall"

	"golift.io/rotatorr"
	"golift.io/rotatorr/timerotator"
)

func main() {
	rotator := rotatorr.NewMust(&rotatorr.Config{
		Filepath: "/var/log/service.log",
		Rotatorr: &timerotator.Layout{FileCount: 10},
	})
	log.SetOutput(rotator)

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGHUP)

	go func() {
		<-sigc

		if _, err := rotator.Rotate(); err != nil {
			panic(err)
		}
	}()
}
Output:

func (*Logger) Write

func (l *Logger) Write(b []byte) (int, error)

Write sends data directly to the file. This satisfies the io.ReadCloser interface. You should generally not call this and instead pass *Logger into log.SetOutput().

type Rotatorr

type Rotatorr interface {
	// Rotate is called any time a file needs to be rotated.
	Rotate(fileName string) (newFile string, err error)
	// Post is called after rotation finishes and the new file is created/opened.
	// This is blocking, so if it does something like compress the rotated file,
	// it should run in a go routine and return immediately.
	Post(fileName, newFile string)

	// Dirs is called once on startup.
	// This should do any validation and return a list of directories to create.
	Dirs(fileName string) (dirPaths []string, err error)
}

Rotatorr allows passing in your own logic for file rotation. A couple working Rotatorr's are included with this library. Use those directly, or extend them with your own methods and interface.

Directories

Path Synopsis
cmd
Package compressor provides a simple interface used for a post-rotate Rotatorr hook that compresses files.
Package compressor provides a simple interface used for a post-rotate Rotatorr hook that compresses files.
Package filer is an interface used in the rotatorr subpackages.
Package filer is an interface used in the rotatorr subpackages.
Package introtator provides an interface for Rotatorr that renames backup log files with an incrementing integer in the name.
Package introtator provides an interface for Rotatorr that renames backup log files with an incrementing integer in the name.
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.
Package timerotator provides an interface for Rotatorr that renames backup log files with a time stamp in the name.
Package timerotator provides an interface for Rotatorr that renames backup log files with a time stamp in the name.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL