fdn API

fdn

package

API reference for the fdn package.

F
function

TestNewStructured_EmitsOneJSONObjectPerLine

Parameters

pkg/log/fdn/fdn_test.go:13-34
func TestNewStructured_EmitsOneJSONObjectPerLine(t *testing.T)

{
	var buf bytes.Buffer
	l := NewStructured(&buf)

	l.Info("started")
	l.Error("failed")

	lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
	if len(lines) != 2 {
		t.Fatalf("expected 2 lines, got %d: %q", len(lines), buf.String())
	}

	for _, line := range lines {
		var entry map[string]any
		if err := json.Unmarshal([]byte(line), &entry); err != nil {
			t.Fatalf("line is not JSON: %q (%v)", line, err)
		}
		if entry["msg"] == nil {
			t.Errorf("entry has no message: %q", line)
		}
	}
}
F
function

TestNewStructured_KeepsTheSuccessSeverity

Parameters

pkg/log/fdn/fdn_test.go:36-52
func TestNewStructured_KeepsTheSuccessSeverity(t *testing.T)

{
	var buf bytes.Buffer
	NewStructured(&buf).Success("done")

	var entry map[string]any
	if err := json.Unmarshal([]byte(strings.TrimSpace(buf.String())), &entry); err != nil {
		t.Fatalf("not JSON: %q (%v)", buf.String(), err)
	}

	fields, ok := entry["fields"].(map[string]any)
	if !ok {
		t.Fatalf("entry carries no fields: %q", buf.String())
	}
	if fields["status"] != "success" {
		t.Errorf("success severity lost, fields were %v", fields)
	}
}
F
function

TestWrap_RoundTrips

Parameters

pkg/log/fdn/fdn_test.go:54-75
func TestWrap_RoundTrips(t *testing.T)

{
	var buf bytes.Buffer
	inner := flogger.New(
		flogger.WithoutDefaultSink(),
		flogger.WithSink(flogger.NewConsoleSink(&buf)),
	)

	wrapped := Wrap(inner)
	wrapped.Info("hello")

	if !strings.Contains(buf.String(), "hello") {
		t.Errorf("message did not reach the wrapped logger, got %q", buf.String())
	}

	back, ok := Unwrap(wrapped)
	if !ok {
		t.Fatal("Unwrap failed on a logger this package produced")
	}
	if back != inner {
		t.Error("Unwrap returned a different logger")
	}
}
F
function

TestWrap_NilFallsBackToConsole

Parameters

pkg/log/fdn/fdn_test.go:77-81
func TestWrap_NilFallsBackToConsole(t *testing.T)

{
	if Wrap(nil) == nil {
		t.Error("Wrap(nil) must return a usable logger")
	}
}
S
struct

wrapped

wrapped adapts a Foundation logger to the CLI logging contract.

pkg/log/fdn/fdn.go:24-26
type wrapped struct

Methods

Info
Method

Parameters

format string
a ...any
func (*wrapped) Info(format string, a ...any)
{
	l.inner.Info(render(format, a...), status(log.LogLevelInfo))
}
Success
Method

Parameters

format string
a ...any
func (*wrapped) Success(format string, a ...any)
{
	l.inner.Info(render(format, a...), status(log.LogLevelSuccess))
}
Warning
Method

Parameters

format string
a ...any
func (*wrapped) Warning(format string, a ...any)
{
	l.inner.Warn(render(format, a...), status(log.LogLevelWarning))
}
Error
Method

Parameters

format string
a ...any
func (*wrapped) Error(format string, a ...any)
{
	l.inner.Error(render(format, a...), status(log.LogLevelError))
}

Fields

Name Type Description
inner flogger.Logger
F
function

Wrap

Wrap adapts an existing Foundation logger so it can be handed to
cli.WithLogger. Sinks, level and bound fields of the wrapped logger are kept.

Parameters

Returns

pkg/log/fdn/fdn.go:35-40
func Wrap(inner flogger.Logger) log.Logger

{
	if inner == nil {
		return log.New()
	}
	return &wrapped{inner: inner}
}

Example

fl := logger.New(logger.WithLevel(logger.DebugLevel))
cli.Run(app, cli.WithLogger(fdn.Wrap(fl)))
F
function

NewStructured

NewStructured returns a logger that emits one JSON object per line on w,
suitable for a service, a CI job, or anything that reads logs with a parser
rather than with eyes.

Parameters

Returns

pkg/log/fdn/fdn.go:49-55
func NewStructured(w io.Writer) log.Logger

{
	inner := flogger.New(
		flogger.WithoutDefaultSink(),
		flogger.WithSink(flogger.NewConsoleSink(w)),
	)
	return &wrapped{inner: inner}
}

Example

cli.Run(app, cli.WithLogger(fdn.NewStructured(os.Stdout)))
F
function

Unwrap

Unwrap returns the Foundation logger behind l, and false when l was not
produced by this package. Use it to add a sink or to shut the logger down.

Parameters

Returns

pkg/log/fdn/fdn.go:59-65
func Unwrap(l log.Logger) (flogger.Logger, bool)

{
	w, ok := l.(*wrapped)
	if !ok {
		return nil, false
	}
	return w.inner, true
}
F
function

render

render formats the message only when arguments were supplied, so that a
message containing a percent sign survives being logged on its own.

Parameters

format
string
a
...any

Returns

string
pkg/log/fdn/fdn.go:85-90
func render(format string, a ...any) string

{
	if len(a) == 0 {
		return format
	}
	return fmt.Sprintf(format, a...)
}
F
function

status

Parameters

level

Returns

pkg/log/fdn/fdn.go:92-94
func status(level log.LogLevel) flogger.Field

{
	return flogger.Field{Key: statusField, Value: level.String()}
}