Skip to content

Writing libraries with Seelog

Pavel Korotkov edited this page Nov 27, 2013 · 15 revisions

Why bother?

You might ask: What's up with libraries! Nothing. You'd just better bear in mind that they are designed to be used in outer code, which may have its own logging policy. So, if you intend a library for external use, it's common practice to expose functions which allow to handily control log configuration of the library.

The most obvious features overwhelming majority of developers would like to see include:

  • Disable log from a library.
  • To be able to pass a custom logger to a library.
  • Force a library's logger to push its log messages to the application's log writers.
  • Use library's logger without importing Seelog package in external application.

So, how to write such a library?

To write a user- (developer)-friendly library, which uses Seelog inside, you may use the following template (it is convenient to put it in a separate file like 'log.go') and modify it as you need more options:

package yourlibrary 

import (
	seelog "github.com/cihub/seelog"
	"io"
	"errors"
)

var logger seelog.LoggerInterface

func init() {
	// Disable logger by default.
	DisableLog()
}

// DisableLog disables all library log output.
func DisableLog() {
	logger = seelog.Disabled
}

// UseLogger uses a specified seelog.LoggerInterface to output library log.
// Use this func if you are using Seelog logging system in your app.
func UseLogger(newLogger seelog.LoggerInterface) {
	logger = newLogger
}

// SetLogWriter uses a specified io.Writer to output library log.
// Use this func if you are not using Seelog logging system in your app.
func SetLogWriter(writer io.Writer) error {
	if writer == nil {
		return errors.New("Nil writer")
	}
	
	newLogger, err := seelog.LoggerFromWriterWithMinLevel(writer, seelog.TraceLvl)
	if err != nil {
		return err
	}
	
	UseLogger(newLogger)
	return nil
}

// Call this before app shutdown
func FlushLog() {
	logger.Flush()
}

Such technique allows users to do anything with your library log:

  • Leave library's logger disabled.
  • Replace it with a custom logger using 'yourLibrary.UseLogger(customLogger)' call.
  • Utilize library log without importing Seelog using 'yourLibrary.SetLogWriter(customWriter)' call.

Custom receivers

In version 'v2.5', a new feature was introduced: Custom receivers. Using custom receivers, you may connect Seelog to almost any other log system/log consumer. So if you are using Seelog v2.5 or above, the template above may be changed to something more flexible using seelog.LoggerFromWriterWithMinLevelAndFormat or even seelog.LoggerFromCustomReceiver.

Avoid using package level funcs in libraries

Asynchronous use of your library in external applications can result in an unpredictable behavior of package level funcs (Trace, Debug, ... , UseConfig, ReplaceConfig, Flush, ...) as they are designed to be only used in final applications. Use library-level logger variables instead (see, example above).

Demonstration

Demonstration and recommendations given above are collected in seelog-examples/library. There you'll find there packages which implement:

  • Library package,
  • App that uses it with Seelog features,
  • App that uses it without importing Seelog.