xylog

package
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2022 License: MIT Imports: 13 Imported by: 0

README

Introduction

Xylog provides flexible logging methods to the program.

This package is inspired by idea of Python logging.

Feature

The basic structs defined by the module, together with their functions, are listed below.

  1. Loggers expose the interface that application code directly uses.
  2. Handlers convert log records (created by loggers) to log messages, then send them to the Emitter.
  3. Emitters write log messages to appropriate destination.
  4. Filters provide a finer grained facility for determining which log records to output.
  5. Formatters specify the layout of log records in the final output.

Visit pkg.go.dev for more details.

Logger

Loggers should NEVER be instantiated directly, but always through the module-level function xylog.GetLogger(name). Multiple calls to GetLogger() with the same name will always return a reference to the same Logger object.

You can logs a message by using one of the following methods:

func (*Logger) Critical(msg string, a ...any)
func (*Logger) Error(msg string, a ...any)
func (*Logger) Fatal(msg string, a ...any)
func (*Logger) Warn(msg string, a ...any)
func (*Logger) Warning(msg string, a ...any)
func (*Logger) Info(msg string, a ...any)
func (*Logger) Debug(msg string, a ...any)
func (*Logger) Log(level int, msg string, a ...any)

To add Handler or Filter instances to the logger, call AddHandler or AddFilter methods.

To adjust the level, using SetLevel method.

Logging level

The numeric values of logging levels are given in the following table. These are primarily of interest if you want to define your own levels, and need them to have specific values relative to the predefined levels. If you define a level with the same numeric value, it overwrites the predefined value; the predefined name is lost.

Level Numeric value
CRITICAL 50
ERROR/FATAL 40
WARN/WARNING 30
INFO 20
DEBUG 10
NOTSET 0

Handler

Handler handles logging events. A Handler need to be instantiated with an Emitter instance rather than creating directly.

Any Handler with a not-empty name will be associated with its name. Calling NewHandler twice with the same name will cause a panic. If you want to create an anonymous Handler, call this function with an empty name.

To get an existed Handler, call GetHandler with its name.

Handler can use SetFormatter method to format the logging message.

Like Logger, Handler is also able to call AddFilter.

Emitter

Emitter instances write log messages to specified destination.

Two basic built-in Emitter instances are xylog.StdoutEmitter and xylog.StderrEmitter.

Formatter

Formatter instances are used to convert a LogRecord to text.

Formatter need to know how a LogRecord is constructed. They are responsible for converting a LogRecord to a string which can be interpreted by either a human or an external system.

TextFormatter is a built-in Formatter which uses logging macros to format the message.

MACROS DESCRIPTION
asctime Textual time when the LogRecord was created.
created Time when the LogRecord was created (time.Now().Unix() return value).
filename Filename portion of pathname.
funcname Function name logged the record.
levelname Text logging level for the message ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL").
levelno Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).
lineno Source line number where the logging call was issued.
message The logging message.
module The module called log method.
msecs Millisecond portion of the creation time.
name Name of the logger.
pathname Full pathname of the source file where the logging call was issued.
process Process ID.
relativeCreated Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded (typically at application startup time).

Filter

Filter instances are used to perform arbitrary filtering of LogRecord.

A Filter struct needs to define Format(LogRecord) method, which return true if it allows to log the LogRecord, and vice versa.

Filter can be used in both Handler and Logger.

Benchmark

op name time per op
GetSameLogger 180ns
GetRandomLogger 315ns
GetSameHandler 5ns
GetRandomHandler 17ns
TextFormatter 9352ns
LogWithoutHandler 31ns
LogWithOneHandler 9172ns

Example

Simple usage

var handler = xylog.NewHandler("xybor", xylog.StdoutEmitter)
handler.SetFormatter(xylog.NewTextFormmater("%(level)s %(message)s"))

var logger = xylog.GetLogger("xybor.service")
logger.AddHandler(handler)
logger.SetLevel(xylog.DEBUG)

logger.Debug("foo")

// Output:
// DEBUG foo

Get the existed Handler

// Get the handler of the first example.
var handler = xylog.GetHandler("xybor")
var logger = xylog.GetLogger("xybor.example")
logger.AddHandler(handler)
logger.Critical("foo foo")

// Output:
// CRITICAL foo foo

Filter definition

// LoggerNameFilter only logs out records belongs to a specified logger.
type LoggerNameFilter struct {
    name string
}

func (f *LoggerNameFilter) Filter(r xylog.LogRecord) bool {
    return f.name == r.name
}

// Get the logger of the first example.
var logger = xylog.GetLogger("xybor.service")
logger.AddFilter(&LoggerNameFilter{"xybor.service.chat"})

logger.Debug("foo")
xylog.GetLogger("xybor.service.auth").Debug("auth foo")
xylog.GetLogger("xybor.service.chat").Debug("chat foo")

// Output:
// chat foo

Root logger

// A simple program with only one application area could use directly the root
// logger.
var handler = xylog.NewHandler("", xylog.StdoutEmitter)
xylog.SetLevel(xylog.DEBUG)
xylog.AddHandler(handler)

xylog.Debug("bar")

// Output:
// bar

Xyplatform log

// example.go
package foo

// To apply xyplatform standard logging, you must import xyplatform.
import (
    _ "github.com/xybor/xyplatform"
    "github.com/xybor/xyplatform/xylog"
)

// All loggers need to be started with the prefix "xybor.xyplatform."
var logger = xylog.GetLogger("xybor.xyplatform.foo")

func main() {
    logger.Debug("message=bar")
}

// Output:
// time=[time] source=example.go.main:13 level=DEBUG module=foo message=bar

Documentation

Overview

xylog is a logging module based on the design of python logging.

Example
package main

import (
	"os"

	"github.com/xybor/xyplatform/xylog"
)

func main() {
	// You can directly use xylog functions to log with the root logger.
	var handler = xylog.NewHandler("", xylog.NewStreamEmitter(os.Stdout))

	xylog.SetLevel(xylog.DEBUG)
	xylog.AddHandler(handler)
	xylog.Debug("foo")

	// Handlers in the root logger will affect to other logger, so in this
	// example, it should remove this handler from the root logger.
	xylog.RemoveHandler(handler)

}
Output:

foo

Index

Examples

Constants

View Source
const (
	CRITICAL = 50
	FATAL    = CRITICAL
	ERROR    = 40
	WARNING  = 30
	WARN     = WARNING
	INFO     = 20
	DEBUG    = 10
	NOTSET   = 0
)

Variables

View Source
var StderrEmitter = NewStreamEmitter(os.Stderr)

StderrEmitter is a shortcut of NewStreamEmitter(os.Stderr)

View Source
var StdoutEmitter = NewStreamEmitter(os.Stdout)

StdoutEmitter is a shortcut of NewStreamEmitter(os.Stdout)

Functions

func AddFilter

func AddFilter(f Filter)

AddFilter adds a specified filter to root logger.

func AddHandler

func AddHandler(h *Handler)

AddHandler adds a new handler to root logger.

func AddLevel

func AddLevel(level int, levelName string)

AddLevel associates a log level with name. It can overwrite other log levels. Default log levels:

NOTSET       0
DEBUG        10
INFO         20
WARN/WARNING 30
ERROR/FATAL  40
CRITICAL     50

func Critical

func Critical(msg string, a ...any)

Critical calls Log of root logger with CRITICAL level.

func Debug

func Debug(msg string, a ...any)

Debug calls Log of root logger with DEBUG level.

func Error

func Error(msg string, a ...any)

Error calls Log of root logger with ERROR level.

func Fatal

func Fatal(msg string, a ...any)

Fatal calls Log of root logger with FATAL level.

func Info

func Info(msg string, a ...any)

Info calls Log of root logger with INFO level.

func Log

func Log(level int, msg string, a ...any)

Log logs a message with a custom level by root logger.

func NewTextFormatter

func NewTextFormatter(f string) textFormatter

NewTextFormatter creates a textFormatter which uses LogRecord attributes to contribute logging string, e.g. %(message)s or %(levelno)d. See LogRecord for more details.

func RemoveFilter

func RemoveFilter(f Filter)

RemoveFilter removes an existed filter from root logger.

func RemoveHandler

func RemoveHandler(h *Handler)

RemoveHandler removes an existed handler from root logger.

func SetLevel

func SetLevel(level int)

SetLevel sets the new logging level for root logger.

func SetTimeLayout

func SetTimeLayout(layout string)

SetTimeLayout sets the time layout to print asctime. It is time.RFC3339Nano by default.

func Warn

func Warn(msg string, a ...any)

Warn calls Log of root logger with WARN level.

func Warning

func Warning(msg string, a ...any)

Warning calls Log of root logger with WARNING level.

Types

type Emitter added in v0.0.2

type Emitter interface {
	Emit(string)
}

Emitter instances dispatch logging events to specific destinations.

type Filter

type Filter interface {
	Filter(record LogRecord) bool
}

Filter instances are used to perform arbitrary filtering of LogRecord.

func GetFilters added in v0.0.2

func GetFilters() []Filter

GetFilters returns all filters of filterer.

type Formatter

type Formatter interface {
	Format(LogRecord) string
}

Formatter instances are used to convert a LogRecord to text.

Formatter need to know how a LogRecord is constructed. They are responsible for converting a LogRecord to a string which can be interpreted by either a human or an external system.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler handles logging events. Do NOT instantiated directly this struct.

Any Handler with a not-empty name will be associated with its name.

Example
package main

import (
	"fmt"

	"github.com/xybor/xyplatform/xylog"
)

func main() {
	// You can use a handler throughout program without storing it in global
	// scope. All handlers can be identified by their names.
	var handlerA = xylog.NewHandler("example", xylog.StdoutEmitter)
	var handlerB = xylog.GetHandler("example")
	if handlerA == handlerB {
		fmt.Println("handlerA == handlerB")
	} else {
		fmt.Println("handlerA != handlerB")
	}

	// In case name is an empty string, it totally is a fresh handler.
	var handlerC = xylog.NewHandler("", xylog.StdoutEmitter)
	var handlerD = xylog.NewHandler("", xylog.StdoutEmitter)
	if handlerC == handlerD {
		fmt.Println("handlerC == handlerD")
	} else {
		fmt.Println("handlerC != handlerD")
	}

}
Output:

handlerA == handlerB
handlerC != handlerD

func GetHandler added in v0.0.2

func GetHandler(name string) *Handler

GetHandler returns the handler associated with the name. If no handler found, returns nil.

func GetHandlers added in v0.0.2

func GetHandlers() []*Handler

GetHandlers returns all handlers of root logger.

func NewHandler added in v0.0.2

func NewHandler(name string, e Emitter) *Handler

NewHandler creates a Handler with a specified Emitter.

Any Handler with a not-empty name will be associated with its name. Calling NewHandler twice with the same name will cause a panic. If you want to create an anonymous Handler, call this function with an empty name.

func (*Handler) AddFilter added in v0.0.2

func (h *Handler) AddFilter(f Filter)

AddFilter adds a specified filter.

func (*Handler) GetFilters added in v0.0.2

func (h *Handler) GetFilters() []Filter

GetFilters returns all filters of filterer.

func (*Handler) RemoveFilter added in v0.0.2

func (h *Handler) RemoveFilter(f Filter)

RemoveFilter removes an existed filter.

func (*Handler) SetFormatter added in v0.0.2

func (h *Handler) SetFormatter(f Formatter)

SetFormatter sets the new formatter of handler.

func (*Handler) SetLevel added in v0.0.2

func (h *Handler) SetLevel(level int)

SetLevel sets the new logging level of handler. It is NOTSET by default.

type LogRecord

type LogRecord struct {
	// Textual time when the LogRecord was created.
	Asctime string `map:"asctime"`

	// Time when the LogRecord was created (time.Now().Unix() return value).
	Created int64 `map:"created"`

	// Filename portion of pathname.
	FileName string `map:"filename"`

	// Function name logged the record.
	FuncName string `map:"funcname"`

	// Text logging level for the message ("DEBUG", "INFO", "WARNING", "ERROR",
	// "CRITICAL").
	LevelName string `map:"levelname"`

	// Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR,
	// CRITICAL).
	LevelNo int `map:"levelno"`

	// Source line number where the logging call was issued.
	LineNo int `map:"lineno"`

	// The logging message.
	Message string `map:"message"`

	// The module called log method.
	Module string `map:"module"`

	// Millisecond portion of the creation time.
	Msecs int `map:"msecs"`

	// Name of the logger.
	Name string `map:"name"`

	// Full pathname of the source file where the logging call was issued.
	PathName string `map:"pathname"`

	// Process ID.
	Process int `map:"process"`

	// Time in milliseconds when the LogRecord was created, relative to the time
	// the logging module was loaded (typically at application startup time).
	RelativeCreated int64 `map:"relativeCreated"`
}

A LogRecord instance represents an event being logged.

LogRecord instances are created every time something is logged. They contain all the information pertinent to the event being logged. The main information passed in is Message. The record also includes information as when the record was created or the source line where the logging call was made.

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger represents a single logging channel. A "logging channel" indicates an area of an application. Exactly how an "area" is defined is up to the application developer. Since an application can have any number of areas, logging channels are identified by a unique string. Application areas can be nested (e.g. an area of "input processing" might include sub-areas "read CSV files", "read XLS files" and "read Gnumeric files"). To cater for this natural nesting, channel names are organized into a namespace hierarchy where levels are separated by periods. So in the instance given above, channel names might be "input" for the upper level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels. There is no arbitrary limit to the depth of nesting.

func GetLogger

func GetLogger(name string) *Logger

GetLogger gets a logger with the specified name (channel name), creating it if it doesn't yet exist. This name is a dot-separated hierarchical name, such as "a", "a.b", "a.b.c" or similar.

Leave name as empty string to get the root logger.

Example
package main

import (
	"os"

	"github.com/xybor/xyplatform/xylog"
)

func main() {
	var handler = xylog.NewHandler("", xylog.NewStreamEmitter(os.Stdout))
	handler.SetFormatter(xylog.NewTextFormatter(
		"module=%(name)s level=%(levelname)s %(message)s"))

	var logger = xylog.GetLogger("example")
	logger.AddHandler(handler)
	logger.SetLevel(xylog.DEBUG)
	logger.Debug("foo %s", "bar")

}
Output:

module=example level=DEBUG foo bar

func (*Logger) AddFilter

func (lg *Logger) AddFilter(f Filter)

AddFilter adds a specified filter.

func (*Logger) AddHandler

func (lg *Logger) AddHandler(h *Handler)

AddHandler adds a new handler.

func (*Logger) Critical

func (lg *Logger) Critical(msg string, a ...any)

Critical calls Log with CRITICAL level.

func (*Logger) Debug

func (lg *Logger) Debug(msg string, a ...any)

Debug calls Log with DEBUG level.

func (*Logger) Error

func (lg *Logger) Error(msg string, a ...any)

Error calls Log with ERROR level.

func (*Logger) Fatal

func (lg *Logger) Fatal(msg string, a ...any)

Fatal calls Log with FATAL level.

func (*Logger) GetFilters added in v0.0.2

func (lg *Logger) GetFilters() []Filter

GetFilters returns all filters of filterer.

func (*Logger) GetHandlers added in v0.0.2

func (lg *Logger) GetHandlers() []*Handler

GetHandlers returns all handlers of logger.

func (*Logger) Info

func (lg *Logger) Info(msg string, a ...any)

Info calls Log with INFO level.

func (*Logger) Log

func (lg *Logger) Log(level int, msg string, a ...any)

Log logs a message with a custom level.

func (*Logger) RemoveFilter

func (lg *Logger) RemoveFilter(f Filter)

RemoveFilter removes an existed filter.

func (*Logger) RemoveHandler

func (lg *Logger) RemoveHandler(h *Handler)

RemoveHandler removes an existed handler.

func (*Logger) SetLevel

func (lg *Logger) SetLevel(level int)

SetLevel sets the new logging level. It also clears logging level cache of all loggers in program.

func (*Logger) Warn

func (lg *Logger) Warn(msg string, a ...any)

Warn calls Log with WARN level.

func (*Logger) Warning

func (lg *Logger) Warning(msg string, a ...any)

Warning calls Log with WARNING level.

type StreamEmitter added in v0.0.2

type StreamEmitter struct {
	// contains filtered or unexported fields
}

StreamEmitter writes logging message to a stream. Note that this class does not close the stream, as os.Stdout or os.Stderr may be used.

func NewStreamEmitter added in v0.0.2

func NewStreamEmitter(f io.Writer) *StreamEmitter

NewStreamEmitter creates a StreamEmitter which writes message to a stream (os.Stderr by default).

func (*StreamEmitter) Emit added in v0.0.2

func (e *StreamEmitter) Emit(msg string)

Emit will be called after a record was decided to log.

Jump to

Keyboard shortcuts

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