logger API

logger

package

API reference for the logger package.

S
struct

clefEntry

clefEntry is the JSON structure for a Compact Log Event Format (CLEF) line.
Fields are flattened at root level; @l is omitted for Information.

core/logger/clef_sink.go:20-24
type clefEntry struct

Fields

Name Type Description
Timestamp time.Time json:"@t"
Level string json:"@l,omitempty"
Message string json:"@m"
S
struct
Implements: Sink

CLEFSink

CLEFSink writes log entries as CLEF-compatible JSON lines.
Each line is a single JSON object with @t, @l (omitted for Information),
@m, and all structured fields flattened at the root level, matching the
format emitted by Serilog’s RenderedCompactJsonFormatter, so that Go and
C# service logs are queryable with the same field selectors in Grafana/Loki.

core/logger/clef_sink.go:31-33
type CLEFSink struct

Methods

Log
Method

Log writes e as a CLEF JSON line to the underlying writer.

Parameters

e Entry

Returns

error
func (*CLEFSink) Log(e Entry) error
{
	// Build a flat map so structured fields sit at the root level.
	m := make(map[string]any, len(e.Fields)+3)
	for k, v := range e.Fields {
		m[k] = v
	}

	m["@t"] = e.Time.UTC().Format(time.RFC3339Nano)
	m["@m"] = e.Msg

	// @l is omitted for Information per the CLEF spec.
	if lvl, ok := clefLevel[e.Level]; ok {
		m["@l"] = lvl
	}

	b, err := json.Marshal(m)
	if err != nil {
		return err
	}
	_, err = c.w.Write(append(b, '\n'))
	return err
}

Fields

Name Type Description
w io.Writer
F
function

NewCLEFSink

NewCLEFSink constructs a CLEFSink. When w is nil, os.Stdout is used.

Parameters

Returns

core/logger/clef_sink.go:36-41
func NewCLEFSink(w io.Writer) *CLEFSink

{
	if w == nil {
		w = os.Stdout
	}
	return &CLEFSink{w: w}
}
T
type

Level

Level represents log severity.

core/logger/logger.go:14-14
type Level int
S
struct

Field

Field is a single structured key/value pair.

core/logger/logger.go:49-52
type Field struct

Fields

Name Type Description
Key string json:"key"
Value interface{} json:"value"
S
struct

Entry

Entry is the log payload passed to sinks.

core/logger/logger.go:55-62
type Entry struct

Fields

Name Type Description
Level string json:"level"
Time time.Time json:"time"
Msg string json:"msg"
Fields map[string]interface{} json:"fields,omitempty"
TraceID string json:"trace_id,omitempty"
SpanID string json:"span_id,omitempty"
I
interface

Sink

Sink receives log entries for processing.
A sink that initiates logger shutdown must use Logger.CloseAsync.

core/logger/logger.go:66-68
type Sink interface

Methods

Log
Method

Parameters

e Entry

Returns

error
func Log(...)
I
interface

Logger

Logger is the public logging contract.

core/logger/logger.go:71-82
type Logger interface

Methods

With
Method

Parameters

fields ...Field

Returns

func With(...)
Debug
Method

Parameters

msg string
fields ...Field
func Debug(...)
Info
Method

Parameters

msg string
fields ...Field
func Info(...)
Warn
Method

Parameters

msg string
fields ...Field
func Warn(...)
Error
Method

Parameters

msg string
fields ...Field
func Error(...)
RegisterSink
Method

Parameters

s Sink
func RegisterSink(...)
SetLevel
Method

Parameters

l Level
func SetLevel(...)
CloseAsync
Method
func CloseAsync(...)
Shutdown
Method

Parameters

Returns

error
func Shutdown(...)
T
type

Option

Option configures the concrete logger on creation.

core/logger/logger.go:85-85
type Option func(*stdLogger)
S
struct

stdLogger

core/logger/logger.go:87-99
type stdLogger struct

Methods

RegisterSink
Method

Parameters

s Sink
func (*stdLogger) RegisterSink(s Sink)
{
	l.shared.mu.Lock()
	defer l.shared.mu.Unlock()
	l.shared.sinks = append(l.shared.sinks, s)
}
SetLevel
Method

SetLevel changes the log level at runtime.

Parameters

level Level
func (*stdLogger) SetLevel(level Level)
{
	l.shared.mu.Lock()
	defer l.shared.mu.Unlock()
	l.shared.level = level
}
With
Method

With returns a non-owning derived logger that shares sinks but has extra bound fields. Closing a derived logger does not close its parent.

Parameters

fields ...Field

Returns

func (*stdLogger) With(fields ...Field) Logger
{
	l.mu.RLock()
	defer l.mu.RUnlock()

	nextFields := make(map[string]interface{}, len(l.fields)+len(fields))
	for k, v := range l.fields {
		nextFields[k] = v
	}
	for _, f := range fields {
		nextFields[f.Key] = f.Value
	}

	child := &stdLogger{
		fields: nextFields,
		ctx:    l.ctx,
		async:  l.async,
		shared: l.shared,
		owner:  false,
	}
	return child
}
shouldLog
Method

shouldLog reports whether the given level meets the logger's minimum threshold.

Parameters

level Level

Returns

bool
func (*stdLogger) shouldLog(level Level) bool
{
	l.shared.mu.RLock()
	defer l.shared.mu.RUnlock()
	return level >= l.shared.level
}
log
Method

log constructs an Entry and dispatches it to all registered sinks.

Parameters

level Level
msg string
fields ...Field
func (*stdLogger) log(level Level, msg string, fields ...Field)
{
	if !l.shouldLog(level) {
		return
	}

	entry := Entry{
		Level:  level.String(),
		Time:   time.Now().UTC(),
		Msg:    msg,
		Fields: map[string]interface{}{},
	}

	l.mu.RLock()
	for k, v := range l.fields {
		entry.Fields[k] = v
	}
	ctx := l.ctx
	l.mu.RUnlock()
	l.shared.mu.RLock()
	sinks := append([]Sink(nil), l.shared.sinks...)
	l.shared.mu.RUnlock()

	if ctx != nil {
		if tid, ok := ctx.Value("trace_id").(string); ok {
			entry.TraceID = tid
		}
		if sid, ok := ctx.Value("span_id").(string); ok {
			entry.SpanID = sid
		}
	}

	for _, f := range fields {
		entry.Fields[f.Key] = f.Value
	}

	if l.async != nil {
		l.async.submit(asyncItem{entry: entry, sinks: sinks})
		return
	}

	for _, sink := range sinks {
		_ = safeSinkLog(sink, entry)
	}
}
Close
Method

Close flushes pending asynchronous entries and stops the logger worker.

Returns

error
func (*stdLogger) Close() error
{
	if l.async == nil || !l.owner {
		return nil
	}
	l.async.startClose()
	<-l.async.done
	return nil
}
CloseAsync
Method

CloseAsync starts logger shutdown without waiting for sink callbacks. It is safe to call from a Sink.

func (*stdLogger) CloseAsync()
{
	if l.async == nil || !l.owner {
		return
	}
	l.async.startClose()
}
Shutdown
Method

Shutdown flushes pending asynchronous entries or returns when ctx is canceled. Sink callbacks must use CloseAsync instead.

Parameters

Returns

error
func (*stdLogger) Shutdown(ctx context.Context) error
{
	if l.async == nil || !l.owner {
		return nil
	}
	if ctx == nil {
		ctx = context.Background()
	}
	l.async.startClose()
	select {
	case <-l.async.done:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}
Debug
Method

Debug logs at debug level.

Parameters

msg string
fields ...Field
func (*stdLogger) Debug(msg string, fields ...Field)
{ l.log(DebugLevel, msg, fields...) }
Info
Method

Info logs at info level.

Parameters

msg string
fields ...Field
func (*stdLogger) Info(msg string, fields ...Field)
{ l.log(InfoLevel, msg, fields...) }
Warn
Method

Warn logs at warn level.

Parameters

msg string
fields ...Field
func (*stdLogger) Warn(msg string, fields ...Field)
{ l.log(WarnLevel, msg, fields...) }
Error
Method

Error logs at error level.

Parameters

msg string
fields ...Field
func (*stdLogger) Error(msg string, fields ...Field)
{ l.log(ErrorLevel, msg, fields...) }

Fields

Name Type Description
mu sync.RWMutex
sinks []Sink
level Level
shared *loggerState
fields map[string]interface{}
ctx context.Context
async *asyncState
owner bool
asyncConfigured bool
asyncBuffer int
S
struct

loggerState

core/logger/logger.go:101-105
type loggerState struct

Fields

Name Type Description
mu sync.RWMutex
sinks []Sink
level Level
S
struct

asyncItem

core/logger/logger.go:107-110
type asyncItem struct

Fields

Name Type Description
entry Entry
sinks []Sink
S
struct

asyncState

core/logger/logger.go:112-120
type asyncState struct

Methods

process
Method
func (*asyncState) process()
{
	defer s.workerWG.Done()
	for item := range s.ch {
		for _, sink := range item.sinks {
			_ = safeSinkLog(sink, item.entry)
		}
	}
}
submit
Method

Parameters

item asyncItem
func (*asyncState) submit(item asyncItem)
{
	s.mu.Lock()
	if s.closed {
		s.mu.Unlock()
		return
	}
	s.submitWG.Add(1)
	s.mu.Unlock()

	send := func() {
		defer s.submitWG.Done()
		select {
		case s.ch <- item:
		default:
		}
	}
	send()
}
startClose
Method
func (*asyncState) startClose()
{
	s.closeOnce.Do(func() {
		s.mu.Lock()
		s.closed = true
		s.mu.Unlock()

		go func() {
			s.submitWG.Wait()
			close(s.ch)
			s.workerWG.Wait()
			close(s.done)
		}()
	})
}

Fields

Name Type Description
mu sync.Mutex
ch chan asyncItem
done chan struct{}
workerWG sync.WaitGroup
submitWG sync.WaitGroup
closeOnce sync.Once
closed bool
F
function

New

New constructs a logger with optional options.

Parameters

opts
...Option

Returns

core/logger/logger.go:132-156
func New(opts ...Option) Logger

{
	l := &stdLogger{
		level:  InfoLevel,
		fields: map[string]interface{}{},
		sinks:  []Sink{NewConsoleSink(nil)},
		owner:  true,
	}
	for _, o := range opts {
		o(l)
	}
	l.shared = &loggerState{
		sinks: append([]Sink(nil), l.sinks...),
		level: l.level,
	}
	if l.asyncConfigured {
		state := &asyncState{
			ch:   make(chan asyncItem, l.asyncBuffer),
			done: make(chan struct{}),
		}
		state.workerWG.Add(1)
		l.async = state
		go state.process()
	}
	return l
}

Example

log := logger.New(
	logger.WithLevel(logger.DebugLevel),
	logger.WithoutDefaultSink(),
	logger.WithSink(mySink),
)
log.Info("started", logger.Field{Key: "version", Value: "1.0"})
F
function

WithLevel

WithLevel sets the minimum level for emitted logs.

Parameters

level

Returns

core/logger/logger.go:159-161
func WithLevel(level Level) Option

{
	return func(l *stdLogger) { l.level = level }
}
F
function

WithSink

WithSink adds an initial sink.

Parameters

s

Returns

core/logger/logger.go:164-166
func WithSink(s Sink) Option

{
	return func(l *stdLogger) { l.sinks = append(l.sinks, s) }
}
F
function

WithoutDefaultSink

WithoutDefaultSink removes the default ConsoleSink added by New.
Use before WithSink to create a logger with only custom sinks:

logger.New(logger.WithoutDefaultSink(), logger.WithSink(clefSink))

Returns

core/logger/logger.go:172-174
func WithoutDefaultSink() Option

{
	return func(l *stdLogger) { l.sinks = nil }
}
F
function

WithFields

WithFields binds fields to the logger returned from New.

Parameters

fields
...Field

Returns

core/logger/logger.go:177-183
func WithFields(fields ...Field) Option

{
	return func(l *stdLogger) {
		for _, f := range fields {
			l.fields[f.Key] = f.Value
		}
	}
}
F
function

WithAsync

WithAsync enables asynchronous logging with a bounded queue.
Entries are dropped when the queue is full.

Parameters

bufSize
int

Returns

core/logger/logger.go:187-198
func WithAsync(bufSize int) Option

{
	return func(l *stdLogger) {
		if bufSize <= 0 {
			panic("logger: async buffer size must be positive")
		}
		if l.asyncConfigured {
			panic("logger: async logging is already configured")
		}
		l.asyncConfigured = true
		l.asyncBuffer = bufSize
	}
}
F
function

safeSinkLog

Parameters

sink
entry

Returns

err
error
core/logger/logger.go:209-219
func safeSinkLog(sink Sink, entry Entry) (err error)

{
	if sink == nil {
		return nil
	}
	defer func() {
		if recovered := recover(); recovered != nil {
			err = errors.New("logger: sink panic")
		}
	}()
	return sink.Log(entry)
}
F
function

WithContext

WithContext binds a context to the logger, allowing sinks
to extract trace/span IDs for distributed tracing.

Parameters

Returns

core/logger/logger.go:223-225
func WithContext(ctx context.Context) Option

{
	return func(l *stdLogger) { l.ctx = ctx }
}
F
function

RegisterSink

RegisterSink adds a sink at runtime.

Parameters

l
s
core/logger/logger.go:228-232
func RegisterSink(l Logger, s Sink)

{
	if sl, ok := l.(*stdLogger); ok {
		sl.RegisterSink(s)
	}
}
S
struct
Implements: Sink

ConsoleSink

ConsoleSink writes entries as compact JSON lines to an io.Writer.

core/logger/logger.go:408-410
type ConsoleSink struct

Methods

Log
Method

Log writes a structured entry to the underlying writer.

Parameters

e Entry

Returns

error
func (*ConsoleSink) Log(e Entry) error
{
	b, err := json.Marshal(e)
	if err != nil {
		return err
	}
	_, err = c.w.Write(append(b, '\n'))
	return err
}

Fields

Name Type Description
w io.Writer
F
function

NewConsoleSink

NewConsoleSink constructs a ConsoleSink.

Parameters

Returns

core/logger/logger.go:413-418
func NewConsoleSink(w io.Writer) *ConsoleSink

{
	if w == nil {
		w = os.Stdout
	}
	return &ConsoleSink{w: w}
}