logos

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2022 License: MIT Imports: 24 Imported by: 14

README

Logos: like log4j, but for golang.

go.dev goreport build coverage stability-stable

Features

This project is a wrapper around the excellent logging framework zap.

  • Dependency
    • go.uber.org/zap for logging
    • github.com/elastic/go-ucfg for config logging system
  • Simple and Clean Interface
  • Config from file & env
  • Jobs & Timing events
  • One log manager for all logs
  • Hot config update from file or env
  • Appenders
    • Console, write to console
    • File, any log file
    • GelfUpd, greylog logger
    • RollingFile, rolling file writing & compress
  • Encoders
    • Console, colorful & formatting text for console
    • Gelf, gelf for greylog
    • Json, standard json encoder
  • Useful utility function
    • Setlevel(LogName string, level int, appender... string), hot update logger level
    • RedirectStdLog(), redirect standard log package
  • High Performance

How to use

Quick start
package main

import (
  
  "github.com/khorevaa/logos"
)

func main() {

  log := logos.New("<your-package-name>") // like github.com/khorevaa/logos
  log.Info("This is me first log. Hello world logging systems")


}

Setup config

Logos loads configuration file from system environment variable LOGOS_CONFIG_FILE. If the variable is unset, then Logos will try to load the configuration file from current work directory, the file name is "logos.yaml" or "logos.yml".

Additionally, the configuration is loaded from the environment variable LOGOS_CONFIG adn connects to the configuration obtained from the file and takes precedence over its data.

From file
appenders:
  console:
    - name: CONSOLE
      target: stdout
      encoder:
        console:
          
  file:
    - name: FILE
      file_name: /tmp/app.log
      encoder:
        json:
  gelf_udp:
    - name: GRAYLOG
      host: 127.0.0.1
      port: 12201
      compression_type: none
      encoder:
        gelf:
          key_value_pairs:
            - key: env
              value: ${ENV:dev}
            - key: app
              value: ${APPNAME:demo}
            - key: file
              value: app.log
  rolling_file:
    - name: GELF_FILE
      file_name: /tmp/app_gelf.log
      max_size: 100
      encoder:
        gelf:
          key_value_pairs:
            - key: env
              value: ${ENV:dev}
            - key: app
              value: ${APPNAME:demo}
            - key: file
              value: app.log
loggers:
  root:
    level: info
    appender_refs:
      - CONSOLE
  logger:
    - name: helloworld
      appender_refs:
        - CONSOLE
        - FILE
        - GELF_FILE
        - GRAYLOG
      level: debug      
From ENV

# Setup all logs level DEBUG
export LOGOS_CONFIG=loggers.root.level=debug

# Add new appender and setup it to logger
export LOGOS_CONFIG="appenders.console.0.name=CONSOLE_TEST;
appenders.console.0.target=stdout;
appenders.console.0.no_color=true;
appenders.console.0.encoder.console;
loggers.logger.0.add_caller=true;
loggers.logger.0.level=debug;
loggers.logger.0.name=github.com/khorevaa/logos;
loggers.logger.0.appender_refs.0=CONSOLE_TEST"


Json Writer

To log a machine-friendly, use json.

package main

import (
  "errors"
  "github.com/khorevaa/logos"
)

func main() {
	
	rawConfig := `
appenders:
  console:
    - name: CONSOLE
      target: stdout
      encoder:
        json:

loggers:
  root:
    level: info
    appender_refs:
      - CONSOLE
`
	
    logos.InitWithConfigContent(rawConfig)	
	
    log := logos.New("<your-package-name>") // like github.com/khorevaa/logos
    log.Info("This is me first log. Hello world logging systems")


}
Pretty Console Writer

To log a human-friendly, colorized output, use Console.

package main

import (
  "errors"
  "github.com/khorevaa/logos"
)

func main() {

  rawConfig := `
appenders:
  console:
    - name: CONSOLE
      target: stdout
      encoder:
        console:
          color_scheme:
            info_level: blue+b
            debug_level: green+b

loggers:
  root:
    level: debug
    appender_refs:
      - CONSOLE
`

  logos.InitWithConfigContent(rawConfig)

  log := logos.New("<your-package-name>") // like github.com/khorevaa/logos
  log.Info("This is me first log. Hello world logging systems")

  err := errors.New("log system error")
  log.Debug("This is me first error", logos.Any("err", err))

}

img.png

Note: pretty logging also works on windows console

High Performance

A quick and simple benchmark with zap/zerolog, which runs on github actions:

// go test -v -cpu=4 -run=none -bench=. -benchtime=10s -benchmem log_test.go
package main

import (
	"io/ioutil"
	"testing"

	"github.com/khorevaa/logos"
	"github.com/phuslu/log"
	"github.com/rs/zerolog"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var fakeMessage = "Test logging, but use a somewhat realistic message length. "

func BenchmarkLogos(b *testing.B) {

	const newConfig = `
appenders:
  console:
    - name: CONSOLE
      no_color: true
      target: discard
      encoder:
        console:
          disable_colors: true
loggers:
  root:
    level: info
    appender_refs:
      - CONSOLE
`
	err := logos.InitWithConfigContent(newConfig)
	if err != nil {
		panic(err)
	}

	logger := logos.New("benchmark")
	for i := 0; i < b.N; i++ {
		logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
	}
}

func BenchmarkLogosColor(b *testing.B) {

  const newConfig = `
appenders:
  console:
    - name: CONSOLE
      target: discard
      encoder:
        console:
loggers:
  root:
    level: info
    appender_refs:
      - CONSOLE
`
  err := logos.InitWithConfigContent(newConfig)
  if err != nil {
    //panic(err)
  }

  logger := logos.New("benchmark")
  for i := 0; i < b.N; i++ {
    logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
  }
}


func BenchmarkLogosJson(b *testing.B) {

  const newConfig = `
appenders:
  console:
    - name: CONSOLE
      target: discard
      encoder:
        json:
loggers:
  root:
    level: info
    appender_refs:
      - CONSOLE
`
  err := logos.InitWithConfigContent(newConfig)
  if err != nil {
    //panic(err)
  }

  logger := logos.New("benchmark")
  for i := 0; i < b.N; i++ {
    logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
  }
}

func BenchmarkZap(b *testing.B) {
	logger := zap.New(zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		zapcore.AddSync(ioutil.Discard),
		zapcore.InfoLevel,
	))
	for i := 0; i < b.N; i++ {
		logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
	}
}

func BenchmarkZeroLog(b *testing.B) {
	logger := zerolog.New(ioutil.Discard).With().Timestamp().Logger()
	for i := 0; i < b.N; i++ {
		logger.Info().Str("foo", "bar").Int("int", 42).Msg(fakeMessage)
	}
}

func BenchmarkPhusLog(b *testing.B) {
	logger := log.Logger{
		TimeFormat: "", // uses rfc3339 by default
		Writer:     log.IOWriter{ioutil.Discard},
	}
	for i := 0; i < b.N; i++ {
		logger.Info().Str("foo", "bar").Int("int", 42).Msg(fakeMessage)
	}
}



A Performance result as below, for daily benchmark results see github actions


Documentation

Index

Examples

Constants

View Source
const (

	// OffLevel
	OffLevel = zapcore.Level(-2)
	// DebugLevel logs are typically voluminous, and are usually disabled in
	// production.
	DebugLevel = zapcore.DebugLevel
	// InfoLevel is the default logging priority.
	InfoLevel = zapcore.InfoLevel
	// WarnLevel logs are more important than Info, but don't need individual
	// human review.
	WarnLevel = zapcore.WarnLevel
	// ErrorLevel logs are high-priority. If an application is running smoothly,
	// it shouldn't generate any error-level logs.
	ErrorLevel = zapcore.ErrorLevel
	// DPanicLevel logs are particularly important errors. In development the
	// logger panics after writing the message.
	DPanicLevel = zapcore.DPanicLevel
	// PanicLevel logs a message, then panics.
	PanicLevel = zapcore.PanicLevel
	// FatalLevel logs a message, then calls os.Exit(1).
	FatalLevel = zapcore.FatalLevel
)

Variables

View Source
var (
	ErrEnvConfigNotSet = errors.New("environment variable 'LOGOS_CONFIG' is not set")
)
View Source
var StackTraceLevelEnabler = zap.NewAtomicLevelAt(zapcore.PanicLevel)

Functions

func CancelRedirectStdLog added in v0.9.0

func CancelRedirectStdLog()

func InitWithConfigContent

func InitWithConfigContent(content string) error

func RedirectStdLog added in v0.9.0

func RedirectStdLog() func()

func SetLevel

func SetLevel(name string, level zapcore.Level, appender ...string)

func Sync

func Sync()

func ToCtx added in v0.10.0

func ToCtx(ctx context.Context, logger Logger) context.Context

Types

type Field

type Field = zapcore.Field

Field is an alias for Field. Aliasing this type dramatically improves the navigability of this package's API documentation.

func Any

func Any(key string, value interface{}) Field

Any takes a key and an arbitrary value and chooses the best way to represent them as a field, falling back to a reflection-based approach only if necessary.

Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between them. To minimize surprises, []byte values are treated as binary blobs, byte values are treated as uint8, and runes are always treated as integers.

func Binary

func Binary(key string, val []byte) Field

Binary constructs a field that carries an opaque binary blob.

Binary data is serialized in an encoding-appropriate format. For example, zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text, use ByteString.

func Bool

func Bool(key string, val bool) Field

Bool constructs a field that carries a bool.

func Boolp

func Boolp(key string, val *bool) Field

Boolp constructs a field that carries a *bool. The returned Field will safely and explicitly represent `nil` when appropriate.

func ByteString

func ByteString(key string, val []byte) Field

ByteString constructs a field that carries UTF-8 encoded text as a []byte. To log opaque binary blobs (which aren't necessarily valid UTF-8), use Binary.

func Complex128

func Complex128(key string, val complex128) Field

Complex128 constructs a field that carries a complex number. Unlike most numeric fields, this costs an allocation (to convert the complex128 to interface{}).

func Complex128p

func Complex128p(key string, val *complex128) Field

Complex128p constructs a field that carries a *complex128. The returned Field will safely and explicitly represent `nil` when appropriate.

func Complex64

func Complex64(key string, val complex64) Field

Complex64 constructs a field that carries a complex number. Unlike most numeric fields, this costs an allocation (to convert the complex64 to interface{}).

func Complex64p

func Complex64p(key string, val *complex64) Field

Complex64p constructs a field that carries a *complex64. The returned Field will safely and explicitly represent `nil` when appropriate.

func Duration

func Duration(key string, val time.Duration) Field

Duration constructs a field with the given key and value. The encoder controls how the duration is serialized.

func Durationp

func Durationp(key string, val *time.Duration) Field

Durationp constructs a field that carries a *time.Duration. The returned Field will safely and explicitly represent `nil` when appropriate.

func Error

func Error(err error) Field

Error is shorthand for the common idiom NamedError("error", err).

func Errors

func Errors(key string, errs []error) Field

Errors constructs a field that carries a slice of errors.

func Float32

func Float32(key string, val float32) Field

Float32 constructs a field that carries a float32. The way the floating-point value is represented is encoder-dependent, so marshaling is necessarily lazy.

func Float32p

func Float32p(key string, val *float32) Field

Float32p constructs a field that carries a *float32. The returned Field will safely and explicitly represent `nil` when appropriate.

func Float64

func Float64(key string, val float64) Field

Float64 constructs a field that carries a float64. The way the floating-point value is represented is encoder-dependent, so marshaling is necessarily lazy.

func Float64p

func Float64p(key string, val *float64) Field

Float64p constructs a field that carries a *float64. The returned Field will safely and explicitly represent `nil` when appropriate.

func Int

func Int(key string, val int) Field

Int constructs a field with the given key and value.

func Int16

func Int16(key string, val int16) Field

Int16 constructs a field with the given key and value.

func Int16p

func Int16p(key string, val *int16) Field

Int16p constructs a field that carries a *int16. The returned Field will safely and explicitly represent `nil` when appropriate.

func Int32

func Int32(key string, val int32) Field

Int32 constructs a field with the given key and value.

func Int32p

func Int32p(key string, val *int32) Field

Int32p constructs a field that carries a *int32. The returned Field will safely and explicitly represent `nil` when appropriate.

func Int64

func Int64(key string, val int64) Field

Int64 constructs a field with the given key and value.

func Int64p

func Int64p(key string, val *int64) Field

Int64p constructs a field that carries a *int64. The returned Field will safely and explicitly represent `nil` when appropriate.

func Int8

func Int8(key string, val int8) Field

Int8 constructs a field with the given key and value.

func Int8p

func Int8p(key string, val *int8) Field

Int8p constructs a field that carries a *int8. The returned Field will safely and explicitly represent `nil` when appropriate.

func Intp

func Intp(key string, val *int) Field

Intp constructs a field that carries a *int. The returned Field will safely and explicitly represent `nil` when appropriate.

func NamedError

func NamedError(key string, err error) Field

NamedError constructs a field that lazily stores err.Error() under the provided key. Errors which also implement fmt.Formatter (like those produced by github.com/pkg/errors) will also have their verbose representation stored under key+"Verbose". If passed a nil error, the field is a no-op.

For the common case in which the key is simply "error", the Error function is shorter and less repetitive.

func Namespace

func Namespace(key string) Field

Namespace creates a named, isolated scope within the logger's context. All subsequent fields will be added to the new namespace.

This helps prevent key collisions when injecting loggers into sub-components or third-party libraries.

func Object

func Object(key string, val zapcore.ObjectMarshaler) Field

Object constructs a field with the given key and ObjectMarshaler. It provides a flexible, but still type-safe and efficient, way to add map- or struct-like user-defined types to the logging context. The struct's MarshalLogObject method is called lazily.

func Reflect

func Reflect(key string, val interface{}) Field

Reflect constructs a field with the given key and an arbitrary object. It uses an encoding-appropriate, reflection-based function to lazily serialize nearly any object into the logging context, but it's relatively slow and allocation-heavy. Outside tests, Any is always a better choice.

If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect includes the error message in the final log output.

func Skip

func Skip() Field

Skip constructs a no-op field, which is often useful when handling invalid inputs in other Field constructors.

func Stack

func Stack(key string) Field

Stack constructs a field that stores a stacktrace of the current goroutine under provided key. Keep in mind that taking a stacktrace is eager and expensive (relatively speaking); this function both makes an allocation and takes about two microseconds.

func StackSkip

func StackSkip(key string, skip int) Field

StackSkip constructs a field similarly to Stack, but also skips the given number of frames from the top of the stacktrace.

func String

func String(key string, val string) Field

String constructs a field with the given key and value.

func Stringer

func Stringer(key string, val fmt.Stringer) Field

Stringer constructs a field with the given key and the output of the value's String method. The Stringer's String method is called lazily.

func Stringp

func Stringp(key string, val *string) Field

Stringp constructs a field that carries a *string. The returned Field will safely and explicitly represent `nil` when appropriate.

func Time

func Time(key string, val time.Time) Field

Time constructs a Field with the given key and value. The encoder controls how the time is serialized.

func Timep

func Timep(key string, val *time.Time) Field

Timep constructs a field that carries a *time.Time. The returned Field will safely and explicitly represent `nil` when appropriate.

func Uint

func Uint(key string, val uint) Field

Uint constructs a field with the given key and value.

func Uint16

func Uint16(key string, val uint16) Field

Uint16 constructs a field with the given key and value.

func Uint16p

func Uint16p(key string, val *uint16) Field

Uint16p constructs a field that carries a *uint16. The returned Field will safely and explicitly represent `nil` when appropriate.

func Uint32

func Uint32(key string, val uint32) Field

Uint32 constructs a field with the given key and value.

func Uint32p

func Uint32p(key string, val *uint32) Field

Uint32p constructs a field that carries a *uint32. The returned Field will safely and explicitly represent `nil` when appropriate.

func Uint64

func Uint64(key string, val uint64) Field

Uint64 constructs a field with the given key and value.

func Uint64p

func Uint64p(key string, val *uint64) Field

Uint64p constructs a field that carries a *uint64. The returned Field will safely and explicitly represent `nil` when appropriate.

func Uint8

func Uint8(key string, val uint8) Field

Uint8 constructs a field with the given key and value.

func Uint8p

func Uint8p(key string, val *uint8) Field

Uint8p constructs a field that carries a *uint8. The returned Field will safely and explicitly represent `nil` when appropriate.

func Uintp

func Uintp(key string, val *uint) Field

Uintp constructs a field that carries a *uint. The returned Field will safely and explicitly represent `nil` when appropriate.

func Uintptr

func Uintptr(key string, val uintptr) Field

Uintptr constructs a field with the given key and value.

func Uintptrp

func Uintptrp(key string, val *uintptr) Field

Uintptrp constructs a field that carries a *uintptr. The returned Field will safely and explicitly represent `nil` when appropriate.

type Logger

type Logger interface {
	Named(s string) Logger

	With(fields ...Field) Logger

	Sync() error

	Sugar() SugaredLogger
	// contains filtered or unexported methods
}

func FromCtx added in v0.10.0

func FromCtx(ctx context.Context) Logger

func New

func New(name string) Logger
Example (Simple)
package main

import (
	"github.com/khorevaa/logos"

	log2 "log"
)

func main() {
	rawConfig := `
appenders:
 console:
   - name: CONSOLE
     target: stdout
     no_color: true
     encoder:
       console:
         disable_timestamp: true
         color_scheme:
           info_level: blue+b
           debug_level: green+b

loggers:
 root:
   level: debug
   appender_refs:
     - CONSOLE
`

	err := logos.InitWithConfigContent(rawConfig)
	if err != nil {
		panic(err)
	}

	//logos.CancelRedirectStdLog()

	log2.Println("1")
	log := logos.New("<your-package-name>") // like github.com/khorevaa/logos
	log.Info("This is me first log. Hello world logging systems")
	//cancel()
	//log2.Println("2")

}
Output:

INFO stdlog 1
INFO <your-package-name> This is me first log. Hello world logging systems
Example (With_color_scheme)
package main

import (
	"errors"
	"github.com/khorevaa/logos"
)

func main() {

	rawConfig := `
appenders:
  console:
    - name: CONSOLE
      target: stdout
      no_color: true
      encoder:
        console:
          disable_timestamp: true
          color_scheme:
            info_level: blue+b
            debug_level: green+b

loggers:
  root:
    level: debug
    appender_refs:
      - CONSOLE
`

	logos.InitWithConfigContent(rawConfig)

	log := logos.New("<your-package-name>") // like github.com/khorevaa/logos
	log.Info("This is me first log. Hello world logging systems")

	err := errors.New("log system error")
	log.Debug("This is me first error", logos.Any("err", err))

}
Output:

INFO <your-package-name> This is me first log. Hello world logging systems
DEBUG <your-package-name> This is me first error err=log system error
Example (With_config_json)
package main

import (
	"github.com/khorevaa/logos"
)

func main() {

	rawConfig := `
appenders:
  console:
    - name: CONSOLE
      target: stdout
      encoder:
        json:

loggers:
  root:
    level: info
    appender_refs:
      - CONSOLE
`

	logos.InitWithConfigContent(rawConfig)

	log := logos.New("<your-package-name>") // like github.com/khorevaa/logos
	log.Info("This is me first log. Hello world logging systems")

}
Output:

type SugaredLogger

type SugaredLogger interface {

	// Debugw logs a message with some additional context. The additional context
	// is added in the form of key-value pairs. The optimal way to write the value
	// to the log message will be inferred by the value's type. To explicitly
	// specify a type you can pass a Field such as logos.Stringer.
	Debugw(msg string, keysAndValues ...interface{})

	// Infow logs a message with some additional context. The additional context
	// is added in the form of key-value pairs. The optimal way to write the value
	// to the log message will be inferred by the value's type. To explicitly
	// specify a type you can pass a Field such as logos.Stringer.
	Infow(msg string, keysAndValues ...interface{})

	// Warnw logs a message with some additional context. The additional context
	// is added in the form of key-value pairs. The optimal way to write the value
	// to the log message will be inferred by the value's type. To explicitly
	// specify a type you can pass a Field such as logos.Stringer.
	Warnw(msg string, keysAndValues ...interface{})

	// Errorw logs a message with some additional context. The additional context
	// is added in the form of key-value pairs. The optimal way to write the value
	// to the log message will be inferred by the value's type. To explicitly
	// specify a type you can pass a Field such as logos.Stringer.
	Errorw(msg string, keysAndValues ...interface{})

	// Fatalw logs a message with some additional context, then calls os.Exit(1).
	// The additional context is added in the form of key-value pairs. The optimal
	// way to write the value to the log message will be inferred by the value's
	// type. To explicitly specify a type you can pass a Field such as
	// logos.Stringer.
	Fatalw(msg string, keysAndValues ...interface{})

	// Panicw logs a message with some additional context, then panics. The
	// additional context is added in the form of key-value pairs. The optimal way
	// to write the value to the log message will be inferred by the value's type.
	// To explicitly specify a type you can pass a Field such as logos.Stringer.
	Panicw(msg string, keysAndValues ...interface{})

	// DPanicw logs a message with some additional context. The logger panics only
	// in Development mode.  The additional context is added in the form of
	// key-value pairs. The optimal way to write the value to the log message will
	// be inferred by the value's type. To explicitly specify a type you can pass a
	// Field such as logos.Stringer.
	DPanicw(msg string, keysAndValues ...interface{})

	Sync() error

	Desugar() Logger
	// contains filtered or unexported methods
}

Directories

Path Synopsis
encoder
internal

Jump to

Keyboard shortcuts

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