syplslog

package
v1.21.0 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package syplslog bridges sypl, and the standard library's structured logger, log/slog - in both directions, using only the standard library:

  • `NewHandler` adapts a sypl logger into a `slog.Handler`, so code using the slog API logs through sypl's pipeline (outputs, processors, formatters).
  • `Output` adapts an `*slog.Logger` into a sypl output, so a sypl logger forwards its processed messages - fields as attrs - to any slog handler.

Level mapping

slog levels are sparse integers (Debug=-4, Info=0, Warn=4, Error=8), designed to accommodate in-between levels. The bridge maps them as follows:

slog level              sypl level      note
----------------------  --------------  ----------------------------------
< LevelDebug (-4)       level.Trace     e.g.: syplslog.LevelTrace (-8)
[LevelDebug, LevelInfo) level.Debug
[LevelInfo, LevelWarn)  level.Info
[LevelWarn, LevelError) level.Warn
>= LevelError (8)       level.Error     incl. syplslog.LevelFatal (12)

In-between levels (e.g.: Info+2) map conservatively - DOWN to the nearest standard level - so they are MORE likely to be printed. The reverse - sypl to slog - mapping:

sypl level     slog level
-------------  ------------------------
level.Trace    syplslog.LevelTrace (-8)
level.Debug    slog.LevelDebug (-4)
level.Info     slog.LevelInfo (0)
level.Warn     slog.LevelWarn (4)
level.Error    slog.LevelError (8)
level.Fatal    syplslog.LevelFatal (12)
level.None     slog.LevelInfo (0)

`ToSyplLevel` NEVER yields `level.Fatal` - sypl exits the process on `Fatal`, which a logging bridge must never do.

NOTE: In sypl's ordering, Warn is MORE verbose than Info: an output @ `maxLevel` Info does not print Warn. The bridge is faithful to that.

Groups, and attrs

slog groups are flattened into sypl field keys as "group.key" - e.g.: attr "err" under groups "req", and "db" becomes the field "req.db.err". `LogValuer`s are resolved - a panicking `LogValue` is recovered, yielding an error value describing the panic. The record PC (source) is not forwarded.

Index

Examples

Constants

View Source
const (
	// LevelTrace is the slog level sypl's `Trace` maps to, and from. It sits
	// below `slog.LevelDebug`, mirroring the standard four-apart spacing.
	LevelTrace slog.Level = slog.LevelDebug - 4

	// LevelFatal is the slog level sypl's `Fatal` maps to. It sits above
	// `slog.LevelError`, mirroring the standard four-apart spacing.
	//
	// NOTE: The bridge only maps TO it - `ToSyplLevel` never yields
	// `level.Fatal`, as sypl exits the process on `Fatal`.
	LevelFatal slog.Level = slog.LevelError + 4
)

Bridge-specific slog levels, extending the standard ones so every sypl level - but `None` - has a distinct slog counterpart.

Variables

This section is empty.

Functions

func NewHandler

func NewHandler(l *sypl.Sypl, opts ...HandlerOption) slog.Handler

NewHandler is the `Handler` factory: a `slog.Handler` forwarding records to the given sypl logger - see `Handler.Handle` for the conversion rules, and the package documentation for the level mapping.

Example

ExampleNewHandler demonstrates the slog -> sypl direction: code using the standard slog API logs through a sypl logger.

package main

import (
	"log/slog"

	"github.com/thalesfsp/sypl"
	"github.com/thalesfsp/sypl/level"
	"github.com/thalesfsp/sypl/output"
	"github.com/thalesfsp/sypl/syplslog"
)

func main() {
	// A sypl logger printing to stdout @ Trace, and above.
	l := sypl.New("app", output.Console(level.Trace))

	// A standard *slog.Logger backed by the sypl logger.
	logger := slog.New(syplslog.NewHandler(l))

	logger.Info("Hello from slog!")

}
Output:
Hello from slog!

func Output

func Output(
	name string,
	sl *slog.Logger,
	maxLevel level.Level,
	processors ...processor.IProcessor,
) output.IOutput

Output is a built-in `output` factory: a sypl output forwarding processed messages to the given `*slog.Logger` - fields as attrs, and the sypl level mapped back to a slog level, per `ToSlogLevel`. It's built with `output.New`, so dispatch, and processor semantics are standard: max-level gating, `Force`, `Mute`, and `processors` all behave as in any other output.

Notes:

  • The forwarding is implemented as the output's FORMATTER: do NOT replace it via `SetFormatter` - doing so breaks the bridge.
  • Messages flagged `Skip`, or `SkipAndForce` - e.g.: `PrintPretty`, `PrintNewLine` - bypass processing, and formatting by design: they are forwarded raw @ the `slog.LevelInfo` level, without fields.
  • A `Handle` error on the slog side is contained by the standard output error handling - logged to stderr - and the message is NOT re-forwarded.
Example

ExampleOutput demonstrates the sypl -> slog direction: a sypl logger forwarding processed messages - fields as attrs - to a slog handler.

package main

import (
	"log/slog"
	"os"

	"github.com/thalesfsp/sypl"
	"github.com/thalesfsp/sypl/fields"
	"github.com/thalesfsp/sypl/level"
	"github.com/thalesfsp/sypl/syplslog"
)

func main() {
	// A standard slog logger - time dropped for a deterministic example.
	sl := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
			if a.Key == slog.TimeKey && len(groups) == 0 {
				return slog.Attr{}
			}

			return a
		},
	}))

	// A sypl logger whose output forwards to the slog logger.
	l := sypl.New("app", syplslog.Output("slog", sl, level.Trace))

	l.PrintWithOptions(
		level.Info,
		"Hello from sypl!",
		sypl.WithFields(fields.Fields{"user": "thales"}),
	)

}
Output:
level=INFO msg="Hello from sypl!" user=thales

func ToSlogLevel

func ToSlogLevel(l level.Level) slog.Level

ToSlogLevel maps a sypl level to a slog level. See the package documentation for the mapping table. `None` - which sypl never prints, unless forced - and unknown levels map to `slog.LevelInfo`.

func ToSyplLevel

func ToSyplLevel(l slog.Level) level.Level

ToSyplLevel maps a slog level to a sypl level. See the package documentation for the mapping table. In-between levels map conservatively - down to the nearest standard level - so they are MORE likely to be printed. It never yields `level.Fatal` - sypl exits the process on `Fatal`.

Types

type Handler

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

Handler is a `slog.Handler` forwarding records to a sypl logger. See `NewHandler`.

func (*Handler) Enabled

func (h *Handler) Enabled(_ context.Context, l slog.Level) bool

Enabled reports whether the handler handles records at the given level: the level must be at, or above the optional floor - see `HandlerWithLevel` - and at least one ENABLED sypl output must accept the mapped sypl level.

NOTE: The `SYPL_LEVEL` env var - which overrides output max levels at write time - isn't consulted here.

func (*Handler) Handle

func (h *Handler) Handle(_ context.Context, r slog.Record) error

Handle converts the record into a sypl message, and prints it through the sypl logger:

  • The record message becomes the content. Each record is a single log line: a trailing linebreak is ensured - sypl strips it for processing, and restores it on write.
  • The record level is mapped per `ToSyplLevel`.
  • The record time is honored - a zero `Record.Time` is forwarded as the zero timestamp, meaning "no time".
  • Record attrs, and `WithAttrs`-accumulated attrs become fields: `LogValuer`s are resolved - a panicking `LogValue` is recovered by `slog.Value.Resolve`, yielding an error value describing the panic - and groups are flattened as "group.key".
  • The record PC (source) is not forwarded.

func (*Handler) WithAttrs

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a NEW handler whose attributes consist of both the receiver's attributes, and the arguments - resolved, and flattened under the currently open groups. The receiver is not modified.

func (*Handler) WithGroup

func (h *Handler) WithGroup(name string) slog.Handler

WithGroup returns a NEW handler with the given group appended to the receiver's existing groups: subsequent attribute keys are qualified as "group.key". If the name is empty, the receiver is returned. The receiver is not modified.

type HandlerOption

type HandlerOption func(*Handler)

HandlerOption allows to specify optional `Handler` configuration.

func HandlerWithLevel

func HandlerWithLevel(leveler slog.Leveler) HandlerOption

HandlerWithLevel sets the minimum (floor) slog level: records below it are discarded before reaching sypl. Without it, `Enabled` is governed by the sypl outputs alone.

Jump to

Keyboard shortcuts

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