sypl

package module
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 26 Imported by: 0

README

sypl

Go

sypl provides a Simple Yet Powerful Logger built on top of the Golang sypl. A sypl logger can have many Outputs, and each Output is responsible for writing to a specified destination. Each Output can have multiple Processors, which run in isolation manipulating the log message. The order of execution is important, and is according to the registering (add) order. These features allow sypl to fit into many different logging flows.

Install

$ go get github.com/thalesfsp/sypl/v2

Logging to ElasticSearch? It's a separate module: $ go get github.com/thalesfsp/sypl/es/v2

Upgrading from v1? See MIGRATION-V2.md — three breaking changes, mostly mechanical.

Specific version

Example: $ go get github.com/thalesfsp/sypl/v2@v2.0.0

Usage

See example_test.go, and sypl_test.go file.

Highlights
  • Multi-output, multi-processor pipeline: route one message to console, files, Elasticsearch, buffers — each with its own level, processors, and formatter.
  • Hot path: opt-in fast gate (SetFastGate(true)) makes filtered-out levels cost ~zero allocations; lazy message identity; benchmarks in-repo.
  • Structured logging: With(fields) derived loggers, Infow-style key-value printers, context helpers with a pluggable tracing extractor, and a bidirectional log/slog bridge (slogtest-verified).
  • Reliability: output.Async buffered wrapper (drop policies, panic containment), Elasticsearch _bulk indexing, self-healing size-based file rotation, Flush/Close lifecycle with a time-bounded flush on Fatal, and an error handler for output write failures.
  • Operability: sampling, rate-limiting, and dedup processors; SYPL_LEVEL / SYPL_FILTER runtime env-var overrides; a Recorder output for test assertions.
Documentation

Run $ make doc or check out online.

How it works

A picture worth thousand words.

high-level-arch

Development

Check out CONTRIBUTION.

Release
  1. Update CHANGELOG accordingly.
  2. Once changes from MR are merged.
  3. Tag and release.

Roadmap

Check out CHANGELOG.

Documentation

Overview

Package sypl provides a Simple Yet Powerful Logger built on top of the Golang logger. A sypl logger can have many `Output`s, and each `Output` is responsible for writing to a specified destination. Each Output can have multiple `Processor`s, which run in isolation manipulating the log message. The order of execution is according to the registering order. The above features allow sypl to fit into many different logging flows and needs.

v2

Three breaking changes - see MIGRATION-V2.md for the full old→new tables:

  • The module path is github.com/thalesfsp/sypl/v2, and ElasticSearch support lives in the nested es module (github.com/thalesfsp/sypl/es/v2) - the core module carries no ElasticSearch dependency.
  • Levels follow the conventional order: None(0) Fatal(1) Error(2) Warn(3) Info(4) Debug(5) Trace(6) - `SetMaxLevel(level.Info)` now SHOWS warnings. Name-based lookups are unaffected.
  • Dead v1 APIs were removed (IMessage.SetContent/SetLevel, IOutput.GetProcessor/SetBuiltinLogger, IMeta.SetName, Sypl.AnyMaxLevel, debug.Match*).

In a application with many loggers, and child loggers, sometimes more fine control is needed, specially when debugging applications. Sypl offers two powerful ways to achieve that: `SYPL_FILTER`, and `SYPL_DEBUG` env vars.

`SYPL_FILTER` allows to specify the name(s) of the component(s) that should be logged, for example, for a given application with the following loggers: `svc`, `pv`, and `cm`, if a developer wants only to see `svc`, and `pv` logging, it's achieved just setting `SYPL_FILTER="svc,pv"`.

`SYPL_DEBUG` allows to specify the max level, for example, for a given application with the following loggers: `svc`, `pv`, and `cm`, if a developer sets:

  • `SYPL_DEBUG="debug"`: any application running using Sypl, any component, any output, will log messages bellow the `debug` level
  • `SYPL_DEBUG="console:debug"`: any application running using Sypl with an output called `console`, will log messages bellow the `debug` level
  • `SYPL_DEBUG="warn,console:debug"`: any application running using Sypl, any component, any output, will log messages bellow the `warn` level, AND any application running using Sypl with an output called `console`, will log messages bellow the `debug` level. NOTE that `warn` is specified first. Only for this case - global max level scope, it's a requirement! In this case -> `SYPL_DEBUG="console:debug,warn"`, `warn` will be discarded.
  • `SYPL_DEBUG="svc:console:debug"`: any application running using Sypl with a component called `svc` with an output called `console`, will log messages bellow the `debug` level
  • `SYPL_DEBUG="file:warn,svc:console:debug"`: any application running using Sypl with an output called `file` will log messages bellow the `warn` level, and any application running using Sypl with a component called `svc` with an output called `console` will log messages bellow the `debug`.

The possibilities are endless! Checkout the [`debugAndFilter`](example_test.go) for more.

Hot-path performance

Two OPT-IN mechanisms keep the cost of dropped messages near zero:

  • Fast level gate (`SetFastGate(true)`): option-less Print-family calls whose level no enabled output can write return BEFORE any message construction. Processors cannot resurrect a gated-out message - the same contract as slog/zap. Fatal is never gated, and the gate defers to the slow path while `SYPL_LEVEL`/`SYPL_FILTER` are set.
  • Lazy message identity: the message UUID, and the content-based hash are computed - and memoized - only when first read (e.g. by the JSON formatter). Per-output copies share the computation, observing one identity per message.

Additionally, a message going to a SINGLE output is written inline on the calling goroutine - multiple outputs keep the concurrent fan-out.

Structured logging conveniences

  • `With(fields)` returns a derived logger sharing the parent's outputs, with its own merged copy of the fields, and tags - reconfiguring one never leaks into the other.
  • `Infow`/`Debugw`/`Tracew`/`Warnw`/`Errorw`/`Fatalw`/`Logw` accept alternating key-value pairs, slog/zap-style - malformed pairs are tolerated, never panicking.
  • `NewContext`/`FromContext`/`FromContextOrDefault` carry a logger through a `context.Context`; `SetContextExtractor` + `PrintWithContext` (and the leveled `*WithContext` variants) pull structured fields out of one - sypl imports no tracing library, the application wires its own extractor.

Lifecycle

  • `SetErrorHandler` receives every output write error - wrapped with the failing output's name - instead of the default silent swallow.
  • `Flush`/`Close` walk the outputs in registration order, calling the ones implementing `interface{ Flush() error }`/`io.Closer`, and aggregate all errors via `errors.Join`. Fatal flushes (best-effort, time-bounded - a hung sink can't keep the process alive) before exiting.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrSyplNotInitialized = errors.New("sypl isn't initialized. Have you instantiated it?")

ErrSyplNotInitialized is returned when sypl isn't initialized.

Functions

func NewContext

func NewContext(ctx context.Context, l *Sypl) context.Context

NewContext returns a copy of `ctx` carrying `l`.

Types

type IBasePrinter

type IBasePrinter interface {
	// PrintMessage prints messages. It's a powerful option because it gives
	// full-control over the message. Use `New` to create the message.
	PrintMessage(messages ...message.IMessage) ISypl

	// PrintWithOptions is a more flexible way of printing, allowing to
	// specify a few message's options in a functional way. For full-control
	// over the message is possible via `PrintMessage`.
	PrintWithOptions(l level.Level, ct string, o ...OptionFunc) ISypl

	// PrintlnWithOptions is a more flexible way of printing, allowing to
	// specify a few message's options in a functional way. For full-control
	// over the message is possible via `PrintMessage`.
	PrintlnWithOptions(l level.Level, ct string, o ...OptionFunc) ISypl
}

IBasePrinter specifies the foundation for other printers.

type IBasicPrinter

type IBasicPrinter interface {
	// Print just prints.
	Print(l level.Level, args ...interface{}) ISypl

	// Printf prints according with the specified format.
	Printf(l level.Level, format string, args ...interface{}) ISypl

	// Println prints, also adding a new line to the end.
	Println(l level.Level, args ...interface{}) ISypl
}

IBasicPrinter specifies the basic printers.

type IConvenientPrinter

type IConvenientPrinter interface {
	// Printlnf prints according with the specified format, also adding a new
	// line to the end.
	Printlnf(l level.Level, format string, args ...interface{}) ISypl

	// PrintPretty prints data structures as JSON text.
	//
	// Notes:
	// - Only exported fields of the data structure will be printed.
	// - Message isn't processed.
	PrintPretty(l level.Level, data interface{}) ISypl

	// PrintlnPretty prints data structures as JSON text, also adding a new line
	// to the end.
	//
	// Notes:
	// - Only exported fields of the data structure will be printed.
	// - Message isn't processed.
	PrintlnPretty(l level.Level, data interface{}) ISypl

	// PrintMessagerPerOutput allows you to concurrently print messages, each
	// one, at the specified level and to the specified output.
	//
	// NOTE: If the named output doesn't exits, the message will not be printed.
	PrintMessagesToOutputs(messagesToOutputs ...MessageToOutput) ISypl

	// PrintMessagesToOutputsWithOptions allows you to concurrently print
	// messages, each one, at the specified level and to the specified output,
	// with options.
	//
	// NOTE: If the named output doesn't exits, the message will not be printed.
	PrintMessagesToOutputsWithOptions(o *options.Options, messagesToOutputs ...MessageToOutput) ISypl

	// PrintNewLine prints a new line. It always print, independent of the
	// level, and without any processing.
	PrintNewLine() ISypl
}

IConvenientPrinter specifies convenient printers.

type ILeveledPrinter

type ILeveledPrinter interface {
	// Fatal prints, and exit with os.Exit(1).
	Fatal(args ...interface{}) ISypl

	// Fatalf prints according with the format, and exit with os.Exit(1).
	Fatalf(format string, args ...interface{}) ISypl

	// Fatallnf prints according with the format, also adding a new line to the
	// end, and exit with os.Exit(1).
	Fatallnf(format string, args ...interface{}) ISypl

	// Fatalln prints, also adding a new line and the end, and exit with
	// os.Exit(1).
	Fatalln(args ...interface{}) ISypl

	// Error prints @ the Error level.
	Error(args ...interface{}) ISypl

	// Errorf prints according with the format @ the Error level.
	Errorf(format string, args ...interface{}) ISypl

	// Errorlnf prints according with the format @ the Error level, also adding
	// a new line to the end.
	Errorlnf(format string, args ...interface{}) ISypl

	// Errorln prints, also adding a new line to the end @ the Error level.
	Errorln(args ...interface{}) ISypl

	// Serror prints like Error, and returns an error with the non-processed
	// content.
	Serror(args ...interface{}) error

	// Serrorf prints like Errorf, and returns an error with the non-processed
	// content.
	Serrorf(format string, args ...interface{}) error

	// Serrorlnf prints like Errorlnf, and returns an error with the
	// non-processed content.
	Serrorlnf(format string, args ...interface{}) error

	// Serrorln prints like Errorln, and returns an error with the non-processed
	// content.
	Serrorln(args ...interface{}) error

	// Info prints @ the Info level.
	Info(args ...interface{}) ISypl

	// Infof prints according with the specified format @ the Info level.
	Infof(format string, args ...interface{}) ISypl

	// Infolnf prints according with the specified format @ the Info level, also
	// adding a new line to the end.
	Infolnf(format string, args ...interface{}) ISypl

	// Infoln prints, also adding a new line to the end @ the Info level.
	Infoln(args ...interface{}) ISypl

	// Warn prints @ the Warn level.
	Warn(args ...interface{}) ISypl

	// Warnf prints according with the specified format @ the Warn level.
	Warnf(format string, args ...interface{}) ISypl

	// Warnlnf prints according with the specified format @ the Warn level, also
	// adding a new line to the end.
	Warnlnf(format string, args ...interface{}) ISypl

	// Warnln prints, also adding a new line to the end @ the Warn level.
	Warnln(args ...interface{}) ISypl

	// Debug prints @ the Debug level.
	Debug(args ...interface{}) ISypl

	// Debugf prints according with the specified format @ the Debug level.
	Debugf(format string, args ...interface{}) ISypl

	// Debuglnf prints according with the specified format @ the Debug level,
	// also adding a new line to the end.
	Debuglnf(format string, args ...interface{}) ISypl

	// Debugln prints, also adding a new line to the end @ the Debug level.
	Debugln(args ...interface{}) ISypl

	// Trace prints @ the Trace level.
	Trace(args ...interface{}) ISypl

	// Tracef prints according with the specified format @ the Trace level.
	Tracef(format string, args ...interface{}) ISypl

	// Tracelnf prints according with the specified format @ the Trace level,
	// also adding a new line to the end.
	Tracelnf(format string, args ...interface{}) ISypl

	// Traceln prints, also adding a new line to the end @ the Trace level.
	Traceln(args ...interface{}) ISypl
}

ILeveledPrinter specifies the leveled printers.

type IPrinters

IPrinters is all available printers.

type ISypl

type ISypl interface {
	meta.IMeta
	IPrinters

	// GetDefaultIoWriterLevel returns the sypl status.
	GetDefaultIoWriterLevel() level.Level

	// SetDefaultIoWriterLevel sets the default io.Writer level.
	SetDefaultIoWriterLevel(l level.Level)

	// Breakpoint will stop execution waiting the user press "/n" ("enter") to
	// continue. It helps users doing log-to-console debug strategy. A message
	// with the breakpoint `name`, and PID of the process will be printed using
	// the `debug` level. Arbitrary `data` can optionally be set - if set, it'll
	// be printed. Errors are printed using the `error` level. Set logging level
	// to `trace` for more.
	Breakpoint(name string, data ...interface{}) ISypl

	// String interface.
	String() string

	// GetFields returns the global registered fields.
	GetFields() fields.Fields

	// SetFields sets global fields. Per-message fields has precedence over
	// global fields.
	SetFields(fields fields.Fields) ISypl

	// GetTags returns the global tags.
	GetTags() []string

	// SetTags sets the global tags.
	SetTags(tags ...string) ISypl

	// GetMaxLevel returns the `maxLevel` of all outputs.
	GetMaxLevel() map[string]level.Level

	// SetMaxLevel sets the `maxLevel` of all outputs.
	SetMaxLevel(l level.Level) ISypl

	// AddOutputs adds one or more outputs.
	AddOutputs(outputs ...output.IOutput) ISypl

	// GetOutput returns the registered output by its name. If not found, will be nil.
	GetOutput(name string) output.IOutput

	// SetOutputs sets one or more outputs. Use to update output(s).
	SetOutputs(outputs ...output.IOutput) ISypl

	// GetOutputs returns registered outputs.
	GetOutputs() []output.IOutput

	// GetOutputsNames returns the names of the registered outputs.
	GetOutputsNames() []string

	// New creates a child logger.
	New(name string) *Sypl

	// Writer implements the io.Writer interface. Message level will be the one set
	// via `SetIoWriterLevel`, default is `error`. It always returns `0, nil`.
	//
	// NOTE: This is a convenient method, if it doesn't fits your need, just
	// implement the way you need, as you would do.
	Write(p []byte) (n int, err error)
	// contains filtered or unexported methods
}

ISypl specified what a Sypl logger does.

type MessageToOutput

type MessageToOutput struct {
	// Content to be printed.
	Content string

	// Level of the message.
	Level level.Level

	// OutputName name of the output.
	OutputName string
}

MessageToOutput defines a `Message` to printed at the specified `Level`, and to the specified `Output`.

type OptionFunc

type OptionFunc func(m message.IMessage) message.IMessage

OptionFunc allows to specify message's options.

func WithField

func WithField(key string, value any) OptionFunc

WithField allows to set a field for the error.

func WithFields

func WithFields(fields fields.Fields) OptionFunc

WithFields add fields to a message.

func WithFlag

func WithFlag(f flag.Flag) OptionFunc

WithFlag set message's flag.

func WithID

func WithID(id string) OptionFunc

WithID set message's ID.

func WithOutputsNames

func WithOutputsNames(outputsNames ...string) OptionFunc

WithOutputsNames set message's output names.

func WithProcessorsNames

func WithProcessorsNames(processorsNames ...string) OptionFunc

WithProcessorsNames set message's processors names.

func WithTags

func WithTags(tags ...string) OptionFunc

WithTags add tags to a message.

type Sypl

type Sypl struct {
	// Name returns the logger name.
	//
	// NOTE: Exposed to deal with https://github.com/golang/go/issues/5819.
	Name string
	// contains filtered or unexported fields
}

Sypl logger definition.

func FromContext

func FromContext(ctx context.Context) (*Sypl, bool)

FromContext returns the `*Sypl` carried by `ctx`, and whether one was found. Tolerates a nil `ctx`, and a stored nil logger: (nil, false).

func FromContextOrDefault

func FromContextOrDefault(ctx context.Context, fallback *Sypl) *Sypl

FromContextOrDefault returns the `*Sypl` carried by `ctx`; `fallback` when none is found; and - when `fallback` is also nil - a fresh `NewDefault("sypl", level.Info)` logger, so the result is ALWAYS usable.

func New

func New(name string, outputs ...output.IOutput) *Sypl

New is the Sypl factory.

Example (Chained)

Chained is the chained example of creating, and setting up a `sypl` logger. It writes to both `stdout` and `stderr`.

package main

import (
	"os"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/message"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	// Creates logger, and name it.
	sypl.New("Testing Logger").
		// Creates a `Processor`. It will prefix all messages. It will only
		// prefix messages for this specific `Output`, and @ `Error` level.
		AddOutputs(output.New("Error", level.Error, os.Stdout).
			AddProcessors(func(prefix string) processor.IProcessor {
				return processor.New("Prefixer", func(message message.IMessage) error {
					if message.GetLevel() == level.Error {
						message.GetContent().SetProcessed(prefix + message.GetContent().GetProcessed())
					}

					return nil
				})
			}(sypltest.DefaultPrefixValue))).
		// Prints:
		// My Prefix - Test error message
		Println(level.Error, "Test error message")

}
Output:
My Prefix - Test error message
Example (ChainedUsingBuiltin)

ChainedUsingBuiltin is the chained example of creating, and setting up a `sypl` logger using built-in `Output`, and `Processor`. It writes to `stdout`, and `stderr`.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	// Creates logger, and name it.
	sypl.New("Testing Logger").
		// Adds an `Output`. In this case, called "Console" that will print to
		// `stdout` and max print level @ `Info`.
		//
		// Adds a `Processor`. It will prefix all messages.
		AddOutputs(output.Console(level.Info).AddProcessors(processor.Prefixer(sypltest.DefaultPrefixValue))).
		// Prints: My Prefix - Test info message
		Infoln("Test info message")

}
Output:
My Prefix - Test info message
Example (ChildLoggers)

Child loggers example.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/status"
)

func main() {
	// Creates logger, and name it.
	k8Logger := sypl.New("k8").
		AddOutputs(output.Console(level.Info).SetFormatter(formatter.Text()))

	k8Logger.Infoln("k8 connected")

	podLogger := k8Logger.New("pod")
	podLogger.Infoln("pod created")

	k8Logger.GetOutput("Console").SetStatus(status.Disabled)

	k8Logger.Infoln("k8 connected")
	podLogger.Infoln("pod created")

	// Prints:
	//
	// k8 connected component=k8 level=info timestamp=2021-08-11T09:24:13-07:00
	// pod created component=pod level=info timestamp=2021-08-11T09:24:13-07:00
}
Example (DebugAndFilter)

Logging filtering, and debug capability example.

package main

import (
	"os"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/shared"
)

func main() {
	// From any SYPL logger, bump all max levels to `info`
	// From any SYPL logger having a `console` output, bump max levels to `debug`
	// From a logger named `pod`, for its output called `Console`, bump max levels to `trace`
	// From a logger named `pv`, for its output called `o1`, bump max levels to `trace`
	os.Setenv(shared.LevelEnvVar, "info,console:debug,pod:console:trace,pv:o1:trace")
	defer os.Unsetenv(shared.LevelEnvVar)

	// From any SYPL logger, only print the following ones.
	os.Setenv(shared.FilterEnvVar, "pod,svc,vs,np,cm,pv")
	defer os.Unsetenv(shared.FilterEnvVar)

	// Will print, max level bumped to `trace` by `pod:console:trace`.
	sypl.New("pod").AddOutputs(output.Console(level.Error)).Traceln("pod created")

	// Will print, max level bumped to `debug`.
	sypl.New("svc").AddOutputs(output.Console(level.Error)).Debugln("svc created")

	// Will print, max level bumped to `debug`.
	sypl.New("vs").AddOutputs(output.Console(level.Error)).Debugln("vs created")

	// Will print, max level bumped to `debug`.
	sypl.New("np").AddOutputs(output.Console(level.Error)).Debugln("np created")

	// Will print, max level bumped to `debug`.
	sypl.New("cm").AddOutputs(output.Console(level.Error)).Debugln("cm created")

	sypl.New("pv").AddOutputs(
		// Will print, max level bumped to `trace` by `pv:o1:trace`.
		output.New("o1", level.Error, os.Stdout),
		// Will not print, max level bumped to `info` but less than `debug`.
		output.New("o2", level.Error, os.Stdout),
	).Traceln("pv created")

	// Will not print, max level bumped to `debug` but it's filtered out.
	sypl.New("dp").AddOutputs(output.Console(level.Error)).Debugln("dp created")

}
Output:
pod created
svc created
vs created
np created
cm created
pv created
Example (ErrorSimulator)

Simulates a problematic processor.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	// Creates logger, and name it.
	sypl.New(sypltest.DefaultComponentNameOutput).
		AddOutputs(output.Console(level.Info, processor.ErrorSimulator("Test"))).
		Infoln(sypltest.DefaultContentOutput)

	// Prints:
	//
	// 2021/08/10 20:00:56 [sypl] [Error] Output: "Console" Processor: "ErrorSimulator" Error: "Test" Original Message: "contentTest"
}
Example (Flags)

Flags example.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/flag"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	// Creates logger, and name it.
	sypl.New("Testing Logger", output.Console(level.Info, processor.Prefixer(sypltest.DefaultPrefixValue))).
		// Message will be processed, and printed independent of `Level`
		// restrictions.
		PrintlnWithOptions(level.Debug, sypltest.DefaultContentOutput, sypl.WithFlag(flag.Force)).

		// Message will be processed, but not printed.
		PrintlnWithOptions(level.Info, sypltest.DefaultContentOutput, sypl.WithFlag(flag.Mute)).

		// Message will not be processed, but printed.
		PrintlnWithOptions(level.Info, sypltest.DefaultContentOutput, sypl.WithFlag(flag.Skip)).

		// Should not print - restricted by level.
		Debugln(sypltest.DefaultContentOutput).

		// SkipAndForce message will not be processed, but will be printed
		// independent of `Level` restrictions.
		PrintlnWithOptions(level.Debug, sypltest.DefaultContentOutput, sypl.WithFlag(flag.SkipAndForce)).

		// Message will not be processed, neither printed.
		PrintlnWithOptions(level.Debug, sypltest.DefaultContentOutput, sypl.WithFlag(flag.SkipAndMute))

}
Output:
My Prefix - contentTest
contentTest
contentTest
Example (ForcePrintProcessor)

ForcePrint processor.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/flag"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	buf, o := output.SafeBuffer(level.Info, processor.Flagger(flag.Force))

	o.SetFormatter(formatter.Text())

	// Creates logger, and name it.
	sypl.New(sypltest.DefaultComponentNameOutput, o).Traceln(sypltest.DefaultContentOutput)

	fmt.Print(strings.Contains(buf.String(), "message=contentTest"))

}
Output:
true
Example (GlobalFields)

Global fields example.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/fields"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

// Checks if `src` has `texts`. If not, it prints a message.
//
//nolint:forbidigo
func stringContains(src string, texts ...string) bool {
	contains := true

	for _, text := range texts {
		if !strings.Contains(src, text) {
			contains = false

			fmt.Printf("Expected %s to contain %s\n", src, text)
		}
	}

	return contains
}

func main() {
	buf, o := output.SafeBuffer(level.Info)

	// Creates logger, and name it.
	l := sypl.New(sypltest.DefaultComponentNameOutput, o.SetFormatter(formatter.Text()))
	l.SetFields(fields.Fields{"a": 1})

	l.Infoln(sypltest.DefaultContentOutput) // a=1

	l.PrintWithOptions(level.Info, sypltest.DefaultContentOutput,
		sypl.WithFields(fields.Fields{"a": 2, "b": 3}))

	l.Infoln(sypltest.DefaultContentOutput) // a=1

	fmt.Println(stringContains(buf.String(), "contentTest", "a=1", "a=2", "b=3"))

}
Output:
true
Example (GlobalTags)

Global tags example.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

// Checks if `src` has `texts`. If not, it prints a message.
//
//nolint:forbidigo
func stringContains(src string, texts ...string) bool {
	contains := true

	for _, text := range texts {
		if !strings.Contains(src, text) {
			contains = false

			fmt.Printf("Expected %s to contain %s\n", src, text)
		}
	}

	return contains
}

func main() {
	buf, o := output.SafeBuffer(level.Info)

	// Creates logger, and name it.
	l := sypl.New(sypltest.DefaultComponentNameOutput, o.SetFormatter(formatter.JSONPretty()))
	l.SetTags("a", "b")

	l.Infoln(sypltest.DefaultContentOutput) // a=1

	l.PrintWithOptions(level.Info, sypltest.DefaultContentOutput,
		sypl.WithTags("a", "c"))

	l.Infoln(sypltest.DefaultContentOutput) // a=1

	fmt.Println(stringContains(buf.String(), "contentTest", "tags", "a", "b", "c"))

}
Output:
true
Example (GlobalTagsNonDup)

Global tags example - non-duplicated tags.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

// Checks if `src` has `texts`. If not, it prints a message.
//
//nolint:forbidigo
func stringContains(src string, texts ...string) bool {
	contains := true

	for _, text := range texts {
		if !strings.Contains(src, text) {
			contains = false

			fmt.Printf("Expected %s to contain %s\n", src, text)
		}
	}

	return contains
}

func main() {
	buf, o := output.SafeBuffer(level.Info)

	// Creates logger, and name it.
	l := sypl.New(sypltest.DefaultComponentNameOutput, o.SetFormatter(formatter.JSONPretty()))
	l.SetTags("a", "b")

	l.PrintWithOptions(level.Info, sypltest.DefaultContentOutput,
		sypl.WithTags("a", "a", "c"))

	bufStr := buf.String()

	fmt.Println(strings.Count(bufStr, "\"a\""), stringContains(bufStr, "contentTest", "tags", "a", "b", "c"))

}
Output:
1 true
Example (InlineUsingBuiltin)

inlineUsingBuiltin same as `ChainedUsingBuiltin` but using inline form.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	sypl.New("Testing Logger", output.Console(level.Info).
		AddProcessors(processor.Prefixer(sypltest.DefaultPrefixValue))).
		Infoln("Test info message")

}
Output:
My Prefix - Test info message
Example (IoWriter)

Sypl as io.Writer, including sub-loggers thru interfaces.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

// Checks if `src` has `texts`. If not, it prints a message.
//
//nolint:forbidigo
func stringContains(src string, texts ...string) bool {
	contains := true

	for _, text := range texts {
		if !strings.Contains(src, text) {
			contains = false

			fmt.Printf("Expected %s to contain %s\n", src, text)
		}
	}

	return contains
}

func main() {
	buf, o := output.SafeBuffer(level.Trace)

	// Creates logger, and name it.
	l := sypl.New(sypltest.DefaultComponentNameOutput, o.SetFormatter(formatter.Text()))

	l.SetDefaultIoWriterLevel(level.Info)

	if _, err := l.Write([]byte(sypltest.DefaultContentOutput + "1 \n")); err != nil {
		fmt.Println(false)
	}

	l.SetDefaultIoWriterLevel(level.Warn)

	if _, err := l.Write([]byte(sypltest.DefaultContentOutput + "2 \n")); err != nil {
		fmt.Println(false)
	}

	var m sypl.ISypl

	m = l.New("sub")

	m.SetDefaultIoWriterLevel(level.Debug)

	if _, err := m.Write([]byte(sypltest.DefaultContentOutput + "3 \n")); err != nil {
		fmt.Println(false)
	}

	m.SetDefaultIoWriterLevel(level.Trace)

	if _, err := m.Write([]byte(sypltest.DefaultContentOutput + "4 \n")); err != nil {
		fmt.Println(false)
	}

	// Buf content should be...:
	// component=componentNameTest output=buffer level=info timestamp=2022-02-22T09:58:22-08:00 message=contentTest1
	// component=componentNameTest output=buffer level=warn timestamp=2022-02-22T09:58:22-08:00 message=contentTest2
	// component=sub output=buffer level=debug timestamp=2022-02-22T09:58:22-08:00 message=contentTest3
	// component=sub output=buffer level=trace timestamp=2022-02-22T09:58:22-08:00 message=contentTest4

	fmt.Println(stringContains(buf.String(),
		"componentNameTest",
		"sub",
		"contentTest1",
		"contentTest2",
		"contentTest3",
		"contentTest4",
		"info",
		"warn",
		"debug",
		"trace",
	))

}
Output:
true
Example (JsonFormatter)

JSON formatter example.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/fields"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	buf, o := output.SafeBuffer(level.Info)
	o.SetFormatter(formatter.JSONPretty())

	// Creates logger, and name it.
	sypl.New(sypltest.DefaultComponentNameOutput).
		AddOutputs(o).
		PrintWithOptions(
			level.Info,
			sypltest.DefaultContentOutput,
			sypl.WithFields(fields.Fields{
				"field1": "value1",
				"field2": 1,
				"field3": true,
				"field4": []string{"1", "2"},
			}),
		)

	s := buf.String()

	fmt.Print(
		strings.Contains(s, `"component"`),
		strings.Contains(s, `"message"`),
		strings.Contains(s, `"field1"`),
		strings.Contains(s, `"field2"`),
		strings.Contains(s, `"field3"`),
		strings.Contains(s, `"field4"`),
		strings.Contains(s, `"level"`),
		strings.Contains(s, `"timestamp"`),
	)

	// Prints:
	//
	// {
	// 	"component": "componentNameTest",
	// 	"content": "contentTest",
	// 	"field1": "value1",
	// 	"field2": 1,
	// 	"field3": true,
	// 	"field4": [
	// 		"1",
	// 		"2"
	// 	],
	// 	"level": "info",
	// 	"timestamp": "2021-08-10T23:27:25-07:00"
	// }

}
Output:
true true true true true true true true
Example (NewDefault)

NewDefault output example.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
)

func main() {
	// Creates logger, and name it.
	sypl.NewDefault(sypltest.DefaultComponentNameOutput, level.Trace).
		Infoln(sypltest.DefaultContentOutput).
		Errorln("error message")

	// Prints:
	//
	// component=componentNameTest output=console level=info timestamp=2021-08-17T19:10:00-07:00 message=contentTest
	// component=componentNameTest output=console level=error timestamp=2021-08-17T19:10:00-07:00 message=error message
}
Example (NotChained)

NonChained is a non-chained example of creating, and setting up a `sypl` logger. It writes to a custom buffer.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/message"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	buf := new(strings.Builder)

	// Creates logger, and name it.
	testingLogger := sypl.New("Testing Logger")

	// Creates an `Output`. In this case, called "Console" that will print to
	// `stdout` and max print level @ `Info`.
	ConsoleToStdOut := output.New("Console", level.Info, buf)

	// Creates a `Processor`. It will prefix all messages.
	Prefixer := func(prefix string) processor.IProcessor {
		return processor.New("Prefixer", func(message message.IMessage) error {
			message.GetContent().SetProcessed(prefix + message.GetContent().GetProcessed())

			return nil
		})
	}

	// Adds `Processor` to `Output`.
	ConsoleToStdOut.AddProcessors(Prefixer(sypltest.DefaultPrefixValue))

	// Adds `Output` to logger.
	testingLogger.AddOutputs(ConsoleToStdOut)

	// Writes: "My Prefix - Test message"
	testingLogger.Print(level.Info, "Test info message")

	fmt.Println(buf.String())

}
Output:
My Prefix - Test info message
Example (PrintMessagesToOutputs)

PrintMessagesToOutputs example.

package main

import (
	"os"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	// Creates logger, and name it.
	sypl.New("pod").AddOutputs(
		output.New("Console 1", level.Trace, os.Stdout).SetFormatter(formatter.Text()),
		output.New("Console 2", level.Trace, os.Stdout).SetFormatter(formatter.Text()),
		output.New("Console 3", level.Trace, os.Stdout).SetFormatter(formatter.Text()),
	).PrintMessagesToOutputs(
		sypl.MessageToOutput{OutputName: "Console 1", Level: level.Info, Content: "Test 1\n"},
		sypl.MessageToOutput{OutputName: "Console 1", Level: level.Debug, Content: "Test 2\n"},
		sypl.MessageToOutput{OutputName: "Console 2", Level: level.Info, Content: "Test 3\n"},
		sypl.MessageToOutput{OutputName: "Console 4", Level: level.Info, Content: "Test 4\n"},
	)

	// Prints:
	//
	// output=Console 2 level=Info message=Test 3
	// output=Console 1 level=Debug message=Test 2
	// output=Console 1 level=Info message=Test 1
}
Example (PrintMessagesToOutputsWithOptions)

PrintMessagesToOutputsWithOptions example.

package main

import (
	"fmt"
	"os"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/fields"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/options"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	// Creates logger, and name it.
	l := sypl.New(sypltest.DefaultComponentNameOutput).AddOutputs(
		output.New("Console 1", level.Trace, os.Stdout).SetFormatter(formatter.Text()),
		output.New("Console 2", level.Trace, os.Stdout).SetFormatter(formatter.Text()),
	)

	l.PrintMessagesToOutputsWithOptions(&options.Options{
		Fields: fields.Fields{"1": 2},
	},
		sypl.MessageToOutput{Content: fmt.Sprintln(sypltest.DefaultContentOutput), Level: level.Info, OutputName: "Console 1"},
		sypl.MessageToOutput{Content: fmt.Sprintln(sypltest.DefaultContentOutput), Level: level.Warn, OutputName: "Console 2"},
	)

	// Prints:
	//
	// component=componentNameTest output=console 2 level=warn timestamp=2021-08-19T11:48:34-07:00 message=contentTest 1=2
	// component=componentNameTest output=console 1 level=info timestamp=2021-08-19T11:48:34-07:00 message=contentTest 1=2
}
Example (PrintNewLine)

PrintNewLine example.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	// Creates logger, and name it.
	l := sypl.New(sypltest.DefaultComponentNameOutput, output.Console(level.Info))

	l.Infoln(sypltest.DefaultContentOutput)
	l.PrintNewLine()
	l.Infoln(sypltest.DefaultContentOutput)

}
Output:
contentTest

contentTest
Example (PrintOnlyIfTagged)

PrintOnlyIfTagged output example.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	// Creates logger, and name it.
	sypl.NewDefault(sypltest.DefaultComponentNameOutput, level.Trace, processor.PrintOnlyIfTagged("testTag")).
		Infoln(sypltest.DefaultContentOutput).
		PrintlnWithOptions(
			level.Info,
			sypltest.DefaultContentOutput,
			sypl.WithTags("testTag"),
		)

	// Prints:
	//
	// component=componentNameTest output=console level=info timestamp=2021-08-17T19:19:57-07:00 message=contentTest
}
Example (PrintPretty)

PrintPretty example.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	type TestType struct {
		Key1 string
		Key2 int
	}

	sypl.New("Testing Logger", output.Console(level.Info)).PrintPretty(level.Info, &TestType{
		Key1: "text",
		Key2: 12,
	})

}
Output:
{
	"Key1": "text",
	"Key2": 12
}
Example (PrintWithOptions)

printWithOptions demonstrates `sypl` flexibility. `Options` enhances the usual `PrintX` methods allowing to specify flags, and tags.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/message"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
	"github.com/thalesfsp/sypl/v2/safebuffer"
)

func main() {
	// Creates logger, and name it.
	testingLogger := sypl.New("Testing Logger")

	// Creates 3 `Output`s, all called "Console" that will print to `stdout`, and
	// max print level @ `Info`.
	var c1buf safebuffer.Buffer
	Console1ToStdOut := output.New("Buffer 1", level.Info, &c1buf)

	var c2buf safebuffer.Buffer
	Console2ToStdOut := output.New("Buffer 2", level.Info, &c2buf)

	var c3buf safebuffer.Buffer
	Console3ToStdOut := output.New("Buffer 3", level.Info, &c3buf)

	// Creates a `Processor`. It will `prefix` all messages with the Output, and
	// Processor names.
	Prefixer := func() processor.IProcessor {
		return processor.New("Prefixer", func(message message.IMessage) error {
			prefix := fmt.Sprintf("Output: %s Processor: %s Content: ",
				message.GetOutputName(),
				message.GetProcessorName(),
			)

			message.GetContent().SetProcessed(prefix + message.GetContent().GetProcessed())

			return nil
		})
	}

	// Creates a `Processor`. It will `suffix` all messages with the specified
	// `tag`.
	SuffixBasedOnTag := func(tag string) processor.IProcessor {
		return processor.New("SuffixBasedOnTag", func(message message.IMessage) error {
			if message.ContainTag(tag) {
				message.GetContent().SetProcessed(message.GetContent().GetProcessed() + " - My Suffix")
			}

			return nil
		})
	}

	// Adds `Processor`s to `Output`s.
	Console1ToStdOut.AddProcessors(Prefixer(), SuffixBasedOnTag("SuffixIt"))
	Console2ToStdOut.AddProcessors(Prefixer(), SuffixBasedOnTag("SuffixIt"))
	Console3ToStdOut.AddProcessors(Prefixer(), SuffixBasedOnTag("SuffixIt"))

	// Adds all `Output`s to logger.
	testingLogger.AddOutputs(Console1ToStdOut, Console2ToStdOut, Console3ToStdOut)

	// Prints with prefix, without suffix.
	testingLogger.Print(level.Info, sypltest.DefaultContentOutput)

	fmt.Println(strings.EqualFold(c1buf.String(), "Output: Buffer 1 Processor: Prefixer Content: contentTest"))
	fmt.Println(strings.EqualFold(c2buf.String(), "Output: Buffer 2 Processor: Prefixer Content: contentTest"))
	fmt.Println(strings.EqualFold(c3buf.String(), "Output: Buffer 3 Processor: Prefixer Content: contentTest"))

	c1buf.Reset()
	c2buf.Reset()
	c3buf.Reset()

	// Prints with prefix, and suffix.
	testingLogger.PrintWithOptions(
		level.Info,
		sypltest.DefaultContentOutput,
		sypl.WithOutputsNames("Buffer 1"),
		sypl.WithProcessorsNames("Prefixer", "SuffixBasedOnTag"),
		sypl.WithTags("SuffixIt"),
	)

	fmt.Println(strings.EqualFold(c1buf.String(), "Output: Buffer 1 Processor: Prefixer Content: contentTest - My Suffix"))

}
Output:
true
true
true
true
Example (SerrorX)

Serror{f|lnf|ln} example.

package main

import (
	"errors"
	"fmt"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	// Creates logger, and name it.
	testingLogger := sypl.New("Testing Logger", output.Console(level.Info))

	sErrorResult := testingLogger.Serror(sypltest.DefaultContentOutput)

	errExample := errors.New("Failed to reach something")
	sErrorfResult := testingLogger.Serrorf("Failed to do something, %s", errExample)
	sErrorlnfResult := testingLogger.Serrorlnf("Failed to do something, %s", errExample)
	sErrorlnResult := testingLogger.Serrorln(sypltest.DefaultContentOutput)

	fmt.Print(
		sErrorResult.Error() == sypltest.DefaultContentOutput,
		sErrorfResult.Error() == "Failed to do something, Failed to reach something",
		sErrorlnfResult.Error() == "Failed to do something, Failed to reach something"+"\n",
		sErrorlnResult.Error() == sypltest.DefaultContentOutput+"\n",
	)

}
Output:
contentTestFailed to do something, Failed to reach somethingFailed to do something, Failed to reach something
contentTest
true true true true
Example (StdErrOutputExample)

StdErr output example.

package main

import (
	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	// Creates logger, and name it.
	sypl.New(sypltest.DefaultComponentNameOutput, output.StdErr()).
		Infoln(sypltest.DefaultContentOutput).
		Errorln(sypltest.DefaultContentOutput)

	// Prints:
	//
	// contentTest
}
Example (Tagger)

Tagger processor.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

func main() {
	buf, o := output.SafeBuffer(level.Info, processor.Tagger("a", "b"))

	o.SetFormatter(formatter.Text())

	// Creates logger, and name it.
	sypl.New(sypltest.DefaultComponentNameOutput, o).Infoln(sypltest.DefaultContentOutput)

	fmt.Print(strings.Contains(buf.String(), "tags=[a, b]"))

}
Output:
true
Example (TextFormatter)

Text formatter example.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/fields"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	buf, o := output.SafeBuffer(level.Info)
	o.SetFormatter(formatter.Text())

	// Creates logger, and name it.
	sypl.New(sypltest.DefaultComponentNameOutput).
		AddOutputs(o).
		PrintlnWithOptions(
			level.Info,
			sypltest.DefaultContentOutput,
			sypl.WithFields(fields.Fields{
				"field1": "value1",
				"field2": "value2",
				"field3": "value3",
			}),
		)

	s := buf.String()

	fmt.Print(
		strings.Contains(s, sypltest.DefaultContentOutput),
		strings.Contains(s, "field1=value1"),
		strings.Contains(s, "field2=value2"),
		strings.Contains(s, "field3=value3"),
		strings.Contains(s, "component="),
		strings.Contains(s, "level="),
		strings.Contains(s, "timestamp="),
	)

	// Prints:
	//
	// component=componentNameTest level=info field1=value1 field2=value2 field3=value3 timestamp=2021-08-10T22:50:36-07:00

}
Output:
true true true true true true true
Example (UpdateOutputsMaxLevel)

Updating outputs' max levels example.

package main

import (
	"fmt"
	"os"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	// Creates logger, and name it.
	l := sypl.New(sypltest.DefaultComponentNameOutput).AddOutputs(
		output.New("Console 1", level.Info, os.Stdout),
		output.New("Console 2", level.Debug, os.Stdout),
		output.New("Console 3", level.Trace, os.Stdout),
	)

	l.PrintlnWithOptions(
		level.Info,
		fmt.Sprint(l.GetMaxLevel()),
		sypl.WithOutputsNames("Console 1"),
	)

	l.SetMaxLevel(level.Info)

	l.PrintlnWithOptions(
		level.Info,
		fmt.Sprint(l.GetMaxLevel()),
		sypl.WithOutputsNames("Console 1"),
	)

}
Output:
map[Console 1:info Console 2:debug Console 3:trace]
map[Console 1:info Console 2:info Console 3:info]
Example (WithID_option)

WithID option.

package main

import (
	"fmt"
	"strings"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/formatter"
	"github.com/thalesfsp/sypl/v2/internal/sypltest"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/output"
)

func main() {
	buf, o := output.SafeBuffer(level.Trace)

	o.SetFormatter(formatter.JSON())

	// Creates logger, and name it.
	sypl.New(sypltest.DefaultComponentNameOutput, o).PrintWithOptions(level.Info, sypltest.DefaultContentOutput, sypl.WithID("idTest"))

	fmt.Print(strings.Contains(buf.String(), `"id":"idTest"`))

}
Output:
true

func NewDefault

func NewDefault(name string, maxLevel level.Level, processors ...processor.IProcessor) *Sypl

NewDefault creates a logger that covers most of all needs: - Writes message to `stdout` @ the specified `maxLevel` - Writes error messages only to `stderr` - Default io.Writer level is `none`. Explicitly change that using `SetDefaultIoWriterLevel` to suit your need.

NOTE: `processors` are applied to both outputs.

func (*Sypl) AddOutputs

func (sypl *Sypl) AddOutputs(outputs ...output.IOutput) ISypl

AddOutputs adds one or more outputs.

func (*Sypl) Breakpoint

func (sypl *Sypl) Breakpoint(name string, data ...interface{}) ISypl

Breakpoint will stop execution waiting the user press "/n" ("enter") to continue. It helps users doing log-to-console debug strategy. A message with the breakpoint `name`, and PID of the process will be printed using the `debug` level. Arbitrary `data` can optionally be set - if set, it'll be printed. Errors are printed using the `error` level. Set logging level to `trace` for more.

func (*Sypl) Close

func (sypl *Sypl) Close() error

Close closes every registered output implementing `io.Closer`, in registration order, aggregating all errors via `errors.Join`. Outputs lacking the capability are skipped.

NOTE: The outputs are snapshotted under the read lock, which is released BEFORE any Close call.

func (*Sypl) Debug

func (sypl *Sypl) Debug(args ...interface{}) ISypl

Debug prints @ the Debug level.

func (*Sypl) DebugWithContext

func (sypl *Sypl) DebugWithContext(ctx context.Context, args ...interface{}) ISypl

DebugWithContext prints @ the Debug level, merging extracted context fields. See `PrintWithContext`.

func (*Sypl) Debugf

func (sypl *Sypl) Debugf(format string, args ...interface{}) ISypl

Debugf prints according with the specified format @ the Debug level.

func (*Sypl) Debugln

func (sypl *Sypl) Debugln(args ...interface{}) ISypl

Debugln prints, also adding a new line to the end @ the Debug level.

func (*Sypl) Debuglnf

func (sypl *Sypl) Debuglnf(format string, args ...interface{}) ISypl

Debuglnf prints according with the specified format @ the Debug level, also adding a new line to the end.

func (*Sypl) Debugw

func (sypl *Sypl) Debugw(msg string, keysAndValues ...any)

Debugw prints @ the Debug level - key-value pairs become fields.

func (*Sypl) Error

func (sypl *Sypl) Error(args ...interface{}) ISypl

Error prints @ the Error level.

func (*Sypl) ErrorWithContext

func (sypl *Sypl) ErrorWithContext(ctx context.Context, args ...interface{}) ISypl

ErrorWithContext prints @ the Error level, merging extracted context fields. See `PrintWithContext`.

func (*Sypl) Errorf

func (sypl *Sypl) Errorf(format string, args ...interface{}) ISypl

Errorf prints according with the format @ the Error level.

func (*Sypl) Errorln

func (sypl *Sypl) Errorln(args ...interface{}) ISypl

Errorln prints, also adding a new line to the end @ the Error level.

func (*Sypl) Errorlnf

func (sypl *Sypl) Errorlnf(format string, args ...interface{}) ISypl

Errorlnf prints according with the format @ the Error level, also adding a new line to the end.

func (*Sypl) Errorw

func (sypl *Sypl) Errorw(msg string, keysAndValues ...any)

Errorw prints @ the Error level - key-value pairs become fields.

func (*Sypl) FastGateEnabled

func (sypl *Sypl) FastGateEnabled() bool

FastGateEnabled returns whether the fast level gate is enabled.

func (*Sypl) Fatal

func (sypl *Sypl) Fatal(args ...interface{}) ISypl

Fatal prints, and exit with os.Exit(1).

func (*Sypl) Fatalf

func (sypl *Sypl) Fatalf(format string, args ...interface{}) ISypl

Fatalf prints according with the format, and exit with os.Exit(1).

func (*Sypl) Fatalln

func (sypl *Sypl) Fatalln(args ...interface{}) ISypl

Fatalln prints, also adding a new line and the end, and exit with os.Exit(1).

func (*Sypl) Fatallnf

func (sypl *Sypl) Fatallnf(format string, args ...interface{}) ISypl

Fatallnf prints according with the format, also adding a new line to the end, and exit with os.Exit(1).

func (*Sypl) Fatalw

func (sypl *Sypl) Fatalw(msg string, keysAndValues ...any)

Fatalw prints @ the Fatal level - key-value pairs become fields - then exits with os.Exit(1).

func (*Sypl) Flush

func (sypl *Sypl) Flush() error

Flush flushes every registered output implementing `interface{ Flush() error }`, in registration order, aggregating all errors via `errors.Join`. Outputs lacking the capability are skipped.

NOTE: The outputs are snapshotted under the read lock, which is released BEFORE any Flush call.

func (*Sypl) GetContextExtractor

func (sypl *Sypl) GetContextExtractor() func(ctx context.Context) fields.Fields

GetContextExtractor returns the registered context extractor - nil if none.

func (*Sypl) GetDefaultIoWriterLevel

func (sypl *Sypl) GetDefaultIoWriterLevel() level.Level

GetDefaultIoWriterLevel returns the sypl status.

func (*Sypl) GetErrorHandler

func (sypl *Sypl) GetErrorHandler() func(err error)

GetErrorHandler returns the registered error handler - nil if none.

func (*Sypl) GetFields

func (sypl *Sypl) GetFields() fields.Fields

GetFields returns the global structured fields.

func (*Sypl) GetMaxLevel

func (sypl *Sypl) GetMaxLevel() map[string]level.Level

GetMaxLevel returns the `maxLevel` of all outputs.

func (*Sypl) GetName

func (sypl *Sypl) GetName() string

GetName returns the sypl Name.

func (*Sypl) GetOutput

func (sypl *Sypl) GetOutput(name string) output.IOutput

GetOutput returns the registered output by its name. If not found, will be nil.

func (*Sypl) GetOutputs

func (sypl *Sypl) GetOutputs() []output.IOutput

GetOutputs returns registered outputs.

func (*Sypl) GetOutputsNames

func (sypl *Sypl) GetOutputsNames() []string

GetOutputsNames returns the names of the registered outputs.

func (*Sypl) GetStatus

func (sypl *Sypl) GetStatus() status.Status

GetStatus returns the sypl status.

func (*Sypl) GetTags

func (sypl *Sypl) GetTags() []string

GetTags returns the global tags.

func (*Sypl) Info

func (sypl *Sypl) Info(args ...interface{}) ISypl

Info prints @ the Info level.

func (*Sypl) InfoWithContext

func (sypl *Sypl) InfoWithContext(ctx context.Context, args ...interface{}) ISypl

InfoWithContext prints @ the Info level, merging extracted context fields. See `PrintWithContext`.

func (*Sypl) Infof

func (sypl *Sypl) Infof(format string, args ...interface{}) ISypl

Infof prints according with the specified format @ the Info level.

func (*Sypl) Infoln

func (sypl *Sypl) Infoln(args ...interface{}) ISypl

Infoln prints, also adding a new line to the end @ the Info level.

func (*Sypl) Infolnf

func (sypl *Sypl) Infolnf(format string, args ...interface{}) ISypl

Infolnf prints according with the specified format @ the Info level, also adding a new line to the end.

func (*Sypl) Infow

func (sypl *Sypl) Infow(msg string, keysAndValues ...any)

Infow prints @ the Info level - key-value pairs become fields.

func (*Sypl) Logw

func (sypl *Sypl) Logw(l level.Level, msg string, keysAndValues ...any)

Logw prints `msg` at the specified level, with the alternating key-value pairs as structured fields. See the package sugar notes for the malformed- input tolerance rules.

func (*Sypl) New

func (sypl *Sypl) New(name string) *Sypl

New creates a child logger. The child logger is an accurate, efficient and shallow copy of the parent logger. Changes to internals, such as the state of outputs, and processors, are reflected cross all other loggers.

func (*Sypl) Print

func (sypl *Sypl) Print(l level.Level, args ...interface{}) ISypl

Print just prints.

func (*Sypl) PrintMessage

func (sypl *Sypl) PrintMessage(messages ...message.IMessage) ISypl

PrintMessage prints messages. It's a powerful option because it gives full-control over the message. Use `New` to create the message. it gives full-control over the message. Use `New` to create the message.

func (*Sypl) PrintMessagesToOutputs

func (sypl *Sypl) PrintMessagesToOutputs(messagesToOutputs ...MessageToOutput) ISypl

PrintMessagesToOutputs allows you to concurrently print messages, each one, at the specified level and to the specified output.

NOTE: If the named output doesn't exits, the message will not be printed.

func (*Sypl) PrintMessagesToOutputsWithOptions

func (sypl *Sypl) PrintMessagesToOutputsWithOptions(
	o *options.Options,
	messagesToOutputs ...MessageToOutput,
) ISypl

PrintMessagesToOutputsWithOptions allows you to concurrently print messages, each one, at the specified level and to the specified output, with options.

NOTE: If the named output doesn't exits, the message will not be printed.

func (*Sypl) PrintNewLine

func (sypl *Sypl) PrintNewLine() ISypl

PrintNewLine prints a new line. It always print, independent of the level, and without any processing.

func (*Sypl) PrintPretty

func (sypl *Sypl) PrintPretty(l level.Level, data interface{}) ISypl

PrintPretty prints data structures as JSON text.

Notes: - Only exported fields of the data structure will be printed. - Message isn't processed.

func (*Sypl) PrintWithContext

func (sypl *Sypl) PrintWithContext(ctx context.Context, l level.Level, args ...interface{}) ISypl

PrintWithContext prints at the specified level; when a context extractor is registered (see `SetContextExtractor`), the extracted fields are merged into the message - message-level fields win on conflict.

func (*Sypl) PrintWithOptions

func (sypl *Sypl) PrintWithOptions(l level.Level, ct string, o ...OptionFunc) ISypl

PrintWithOptions is a more flexible way of printing, allowing to specify a few message's options in a functional way. For full-control over the message is possible via `PrintMessage`.

func (*Sypl) Printf

func (sypl *Sypl) Printf(l level.Level, format string, args ...interface{}) ISypl

Printf prints according with the specified format.

func (*Sypl) Println

func (sypl *Sypl) Println(l level.Level, args ...interface{}) ISypl

Println prints, also adding a new line to the end.

func (*Sypl) PrintlnPretty

func (sypl *Sypl) PrintlnPretty(l level.Level, data interface{}) ISypl

PrintlnPretty prints data structures as JSON text, also adding a new line to the end.

Notes: - Only exported fields of the data structure will be printed. - Message isn't processed.

func (*Sypl) PrintlnWithOptions

func (sypl *Sypl) PrintlnWithOptions(l level.Level, ct string, o ...OptionFunc) ISypl

PrintlnWithOptions is a more flexible way of printing, allowing to specify a few message's options in a functional way. For full-control over the message is possible via `PrintMessage`.

func (*Sypl) Printlnf

func (sypl *Sypl) Printlnf(l level.Level, format string, args ...interface{}) ISypl

Printlnf prints according with the specified format, also adding a new line to the end.

func (*Sypl) Serror

func (sypl *Sypl) Serror(args ...interface{}) error

Serror prints like Error, and returns an error with the non-processed content.

func (*Sypl) Serrorf

func (sypl *Sypl) Serrorf(format string, args ...interface{}) error

Serrorf prints like Errorf, and returns an error with the non-processed content.

func (*Sypl) Serrorln

func (sypl *Sypl) Serrorln(args ...interface{}) error

Serrorln prints like Errorln, and returns an error with the non-processed content.

func (*Sypl) Serrorlnf

func (sypl *Sypl) Serrorlnf(format string, args ...interface{}) error

Serrorlnf prints like Errorlnf, and returns an error with the non-processed content.

func (*Sypl) SetContextExtractor

func (sypl *Sypl) SetContextExtractor(fn func(ctx context.Context) fields.Fields) *Sypl

SetContextExtractor sets the function `PrintWithContext` (and the leveled `*WithContext` variants) use to pull structured fields out of a context. The extracted fields are merged into the message as MESSAGE-level fields - so they take precedence over the logger's global fields on key conflict. A nil extractor (the default) makes the `*WithContext` printers behave exactly like their plain counterparts.

Example

ExampleSypl_SetContextExtractor demonstrates wiring a fake trace-id extractor: the `*WithContext` printers pull fields out of the context, and merge them into every message.

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/thalesfsp/sypl/v2"
	"github.com/thalesfsp/sypl/v2/fields"
	"github.com/thalesfsp/sypl/v2/level"
	"github.com/thalesfsp/sypl/v2/message"
	"github.com/thalesfsp/sypl/v2/output"
	"github.com/thalesfsp/sypl/v2/processor"
)

// exampleTraceKey is the application's own context key - sypl imports no
// tracing library; the application wires its own extractor.
type exampleTraceKey struct{}

// ExampleSypl_SetContextExtractor demonstrates wiring a fake trace-id
// extractor: the `*WithContext` printers pull fields out of the context, and
// merge them into every message.
func main() {
	// A processor that renders the extracted field, keeping the example
	// output deterministic.
	appendTraceID := processor.New("AppendTraceID", func(m message.IMessage) error {
		if v, ok := m.GetFields()["trace_id"]; ok {
			m.GetContent().SetProcessed(
				fmt.Sprintf("%s trace_id=%v", m.GetContent().GetProcessed(), v),
			)
		}

		return nil
	})

	l := sypl.New("api", output.New("Stdout", level.Info, os.Stdout, appendTraceID)).
		SetContextExtractor(func(ctx context.Context) fields.Fields {
			if traceID, ok := ctx.Value(exampleTraceKey{}).(string); ok {
				return fields.Fields{"trace_id": traceID}
			}

			return nil
		})

	ctx := context.WithValue(context.Background(), exampleTraceKey{}, "abc-123")

	l.InfoWithContext(ctx, "handling request")

}
Output:
handling request trace_id=abc-123

func (*Sypl) SetDefaultIoWriterLevel

func (sypl *Sypl) SetDefaultIoWriterLevel(l level.Level)

SetDefaultIoWriterLevel sets the default io.Writer level.

func (*Sypl) SetErrorHandler

func (sypl *Sypl) SetErrorHandler(h func(err error)) *Sypl

SetErrorHandler sets the handler invoked with each output write error. A nil handler restores the default behavior (errors silently swallowed).

func (*Sypl) SetFastGate

func (sypl *Sypl) SetFastGate(enabled bool) *Sypl

SetFastGate toggles the opt-in fast level gate. Default: disabled - zero behavior change.

func (*Sypl) SetFields

func (sypl *Sypl) SetFields(fields fields.Fields) ISypl

SetFields sets the global structured fields.

func (*Sypl) SetMaxLevel

func (sypl *Sypl) SetMaxLevel(l level.Level) ISypl

SetMaxLevel sets the `maxLevel` of all outputs.

func (*Sypl) SetOutputs

func (sypl *Sypl) SetOutputs(outputs ...output.IOutput) ISypl

SetOutputs sets one or more outputs. Use to update output(s).

func (*Sypl) SetStatus

func (sypl *Sypl) SetStatus(s status.Status)

SetStatus sets the sypl status.

func (*Sypl) SetTags

func (sypl *Sypl) SetTags(tags ...string) ISypl

SetTags adds the global tags.

func (Sypl) String

func (sypl Sypl) String() string

String interface implementation.

NOTE: Value receiver on purpose - a Sypl VALUE satisfies fmt.Stringer, as on master. The receiver copy itself is unsynchronized (also as on master): don't format a Sypl VALUE concurrently with reconfiguration - use the *Sypl, whose accessors lock.

func (*Sypl) Trace

func (sypl *Sypl) Trace(args ...interface{}) ISypl

Trace prints @ the Trace level.

func (*Sypl) Tracef

func (sypl *Sypl) Tracef(format string, args ...interface{}) ISypl

Tracef prints according with the specified format @ the Trace level.

func (*Sypl) Traceln

func (sypl *Sypl) Traceln(args ...interface{}) ISypl

Traceln prints, also adding a new line to the end @ the Trace level.

func (*Sypl) Tracelnf

func (sypl *Sypl) Tracelnf(format string, args ...interface{}) ISypl

Tracelnf prints according with the specified format @ the Trace level, also adding a new line to the end.

func (*Sypl) Tracew

func (sypl *Sypl) Tracew(msg string, keysAndValues ...any)

Tracew prints @ the Trace level - key-value pairs become fields.

func (*Sypl) Warn

func (sypl *Sypl) Warn(args ...interface{}) ISypl

Warn prints @ the Warn level.

func (*Sypl) WarnWithContext

func (sypl *Sypl) WarnWithContext(ctx context.Context, args ...interface{}) ISypl

WarnWithContext prints @ the Warn level, merging extracted context fields. See `PrintWithContext`.

func (*Sypl) Warnf

func (sypl *Sypl) Warnf(format string, args ...interface{}) ISypl

Warnf prints according with the specified format @ the Warn level.

func (*Sypl) Warnln

func (sypl *Sypl) Warnln(args ...interface{}) ISypl

Warnln prints, also adding a new line to the end @ the Warn level.

func (*Sypl) Warnlnf

func (sypl *Sypl) Warnlnf(format string, args ...interface{}) ISypl

Warnlnf prints according with the specified format @ the Warn level, also adding a new line to the end.

func (*Sypl) Warnw

func (sypl *Sypl) Warnw(msg string, keysAndValues ...any)

Warnw prints @ the Warn level - key-value pairs become fields.

func (*Sypl) With

func (sypl *Sypl) With(f fields.Fields) *Sypl

With returns a DERIVED logger: it shares the parent's output INSTANCES, but owns its mutex, and its own COPIES of the fields map - the parent's fields merged with `f`, `f` winning on key conflict - and of the tags slice. Reconfiguring the child's fields/tags never leaks into the parent, and vice versa (the containers are unshared - see the 2026-07-12 audit fix, commit 25dfacc).

The derived logger inherits Name, the default io.Writer level, status, the error handler, the context extractor, and the fast-gate setting. `f` may be nil, or empty - the child then simply inherits the parent's fields.

func (*Sypl) Write

func (sypl *Sypl) Write(p []byte) (int, error)

Writer implements the io.Writer interface. Message level will be the one set via `SetIoWriterLevel`, default is `none`. It always returns `0, nil`.

NOTE: This is a convenient method, if it doesn't fits your need, just implement the way you need.

Directories

Path Synopsis
internal
builtin
Package builtin is a minimal, mutex-guarded writer replacing Golang's built-in logger with only one behavioral change: it does NOT force-append a newline to the message.
Package builtin is a minimal, mutex-guarded writer replacing Golang's built-in logger with only one behavioral change: it does NOT force-append a newline to the message.
sypltest
Package sypltest carries test-only fixtures shared across the sypl modules' test suites.
Package sypltest carries test-only fixtures shared across the sypl modules' test suites.
Package output implements Sypl outputs: an output processes, and writes a message to its writer - anything implementing io.Writer.
Package output implements Sypl outputs: an output processes, and writes a message to its writer - anything implementing io.Writer.
Package processor provides the building blocks to create processors, which are used to process messages before they are printed.
Package processor provides the building blocks to create processors, which are used to process messages before they are printed.
Package syplslog bridges sypl, and the standard library's structured logger, log/slog - in both directions, using only the standard library:
Package syplslog bridges sypl, and the standard library's structured logger, log/slog - in both directions, using only the standard library:

Jump to

Keyboard shortcuts

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