logger

package
v0.296.0 Latest Latest
Warning

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

Go to latest
Published: Apr 9, 2025 License: Apache-2.0 Imports: 8 Imported by: 4

README

package logger

Package logger represents logging as a global resource, as most application log is meant to happen to the same place. The logger package makes it convinent to write your application that does application logging, that doesn't get into your way in your tests. With logger, you can use context to add logging details to your call stack. logger is based on pkg/logging but it can be configured to use any third party logging library thourgh providing a Hijack function.

How to use

package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
	"go.llib.dev/frameless/pkg/logging"
)

func main() {
	ctx := context.Background()

	// You can add details to the context; thus, every logging call using this context will inherit the details.
	ctx = logging.ContextWith(ctx, logging.Fields{
		"foo": "bar",
		"baz": "qux",
	})

	// You can use your own Logger instance or the logger.Default logger instance if you plan to log to the STDOUT. 
	logger.Info(ctx, "foo", logging.Fields{
		"userID":    42,
		"accountID": 24,
	})
}

example output when snake case key format is defined (default):

{
  "account_id": 24,
  "baz": "qux",
  "foo": "bar",
  "level": "info",
  "message": "foo",
  "timestamp": "2023-04-02T16:00:00+02:00",
  "user_id": 42
}

example output when camel key format is defined:

{
  "accountID": 24,
  "baz": "qux",
  "foo": "bar",
  "level": "info",
  "message": "foo",
  "timestamp": "2023-04-02T16:00:00+02:00",
  "userID": 42
}

How to configure:

logging.Logger can be configured through its struct fields; please see the documentation for more details. To configure the default logger, simply configure it from your main.

package main

import "go.llib.dev/frameless/pkg/logger"

func main() {
	logger.Default.MessageKey = "msg"
	logger.Default.TimestampKey = "ts"
}

Key string case style consistency in your logs

Using the logger package can help you maintain historical consistency in your logging key style and avoid mixed string case styles. This is particularly useful since many log collecting systems rely on an append-only strategy, and any inconsistency could potentially cause issues with your alerting scripts that rely on certain keys. So, by using the logger package, you can ensure that your logging is clean and consistent, making it easier to manage and troubleshoot any issues that may arise.

The default key format is snake_case, but you can change it easily by supplying a formetter in the KeyFormatter Logger field.

package main

import (
	"go.llib.dev/frameless/pkg/logger"
	"go.llib.dev/frameless/pkg/stringkit"
)

func main() {
	logger.Default.KeyFormatter = stringkit.ToKebab
}

Security concerns

It is crucial to make conscious decisions about what we log from a security standpoint because logging too much information can potentially expose sensitive data about users. On the other hand, by logging what is necessary and relevant for operations, we can avoid security and compliance issues. Following this practice can reduce the attack surface of our system and limit the potential impact of a security breach. Additionally, logging too much information can make detecting and responding to security incidents more difficult, as the noise of unnecessary data can obscure the signal of actual threats.

The logger package has a safety mechanism to prevent exposure of sensitive data; it requires you to register any DTO or Entity struct type with the logging package before you can use it as a logging field.

package mydomain

import "go.llib.dev/frameless/pkg/logger"

type MyEntity struct {
	ID               string
	NonSensitiveData string
	SensitiveData    string
}

var _ = logger.RegisterFieldType(func(ent MyEntity) logging.LoggingDetail {
	return logging.Fields{
		"id":   ent.ID,
		"data": ent.NonSensitiveData,
	}
})

ENV based Configuration

You can set the default's loggers level with the following env values:

  • LOG_LEVEL
  • LOGGER_LEVEL
  • LOGGING_LEVEL
logging level env value env short format value
Debug Level debug d
Info Level info i
Warn Level warn w
Error Level error e
Fatal Level fatal f
Fatal Level critical c

Logging performance optimisation by using an async logging strategy

If your application requires high performance and uses a lot of concurrent actions, using an async logging strategy can provide the best of both worlds. This means the application can have great performance that is not hindered by logging calls, while also being able to observe and monitor its behavior effectively.

----------------- no concurrency heavy concurrency
sync logging 5550 ns/op 54930 ns/op
async logging 700.7 ns/op 1121 ns/op

tested on MacBook Pro with M1 when writing into a file

$ go test -bench BenchmarkLogger_AsyncLogging -run -

package main

import (
	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	defer logger.AsyncLogging()() // from here on, logging will be async

	logger.Info(nil, "this is logged asynchronously")
}

Helping the TDD Flow during Testing

using the logger.LogWithTB helper method will turn your logs into structured testing log events making it easier to investigate unexpected behaviours using the DEBUG logs.

myfile.go

package mypkg

import (
	"go.llib.dev/frameless/pkg/logger"
)

func MyFunc() {
	logger.Debug(nil, "the logging message", logging.Field("bar", 24))
}

myfile_test.go

package mypkg_test

import (
	"testing"

	"mypkg"

	"go.llib.dev/frameless/pkg/logger"
)

func TestMyFunc(t *testing.T) {
	logger.LogWithTB(t) // logs are redirected to use *testing.T.Log  

	mypkg.MyFunc()
}

will produce the following output:

=== RUN   TestLogWithTB_spike
	myfile.go:11: the logging message | level:debug bar:24
	--- PASS: TestLogWithTB_spike (0.00s)
	PASS

Documentation

Overview

Example (WithDetails)
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
	"go.llib.dev/frameless/pkg/logging"
)

func main() {
	ctx := context.Background()
	logger.Info(ctx, "foo", logging.Fields{
		"userID":    42,
		"accountID": 24,
	})
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var LogWithTB = Testing

LogWithTB is backward compatibility.

Deprecated: use logger.Testing instead

Functions

func AsyncLogging

func AsyncLogging() func()
Example
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	ctx := context.Background()
	defer logger.AsyncLogging()()
	logger.Info(ctx, "this log message is written out asynchronously")
}
Output:

func Configure added in v0.218.0

func Configure(blk ConfigurationFunc) struct{}

func ContextWith deprecated

func ContextWith(ctx context.Context, lds ...logging.Detail) context.Context

ContextWith

Deprecated: functionality is moved to logging package, use it from there.

func Debug

func Debug(ctx context.Context, msg string, ds ...logging.Detail)
Example
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	ctx := context.Background()
	logger.Debug(ctx, "foo")
}
Output:

func ErrField deprecated

func ErrField(err error) logging.Detail

ErrField

Deprecated: functionality is moved to logging package, use it from there.

func Error

func Error(ctx context.Context, msg string, ds ...logging.Detail)
Example
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	ctx := context.Background()
	logger.Error(ctx, "foo")
}
Output:

func Fatal

func Fatal(ctx context.Context, msg string, ds ...logging.Detail)
Example
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	ctx := context.Background()
	logger.Fatal(ctx, "foo")
}
Output:

func Field deprecated

func Field(key string, value any) logging.Detail

Field creates a single key value pair based logging detail. It will enrich the log entry with a value in the key you gave.

Deprecated: functionality is moved to logging package, use it from there.

func Hijack added in v0.218.0

func Hijack(fn logging.HijackFunc)

func Info

func Info(ctx context.Context, msg string, ds ...logging.Detail)
Example
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	ctx := context.Background()
	logger.Info(ctx, "foo")
}
Output:

func Stub

func Stub(tb testingTB, optionalConfiguration ...ConfigurationFunc) logging.StubOutput

Stub the logger.Default and return the buffer where the logging output will be recorded. Stub will restore the logger.Default after the test. optionally, the stub logger can be further configured by passing a configuration function block

Example
package main

import (
	"context"
	"strings"
	"testing"

	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	var tb testing.TB
	buf := logger.Stub(tb) // stub will clean up after itself when the test is finished
	logger.Info(context.Background(), "foo")
	strings.Contains(buf.String(), "foo") // true
}
Output:

func Testing added in v0.218.0

func Testing(tb testingTB)

Testing pipes all application log generated during the test execution through the testing.TB's Log method. Testing meant to help debugging your application during your TDD flow.

Example
package main

import (
	"context"
	"testing"

	"go.llib.dev/frameless/pkg/logger"
	"go.llib.dev/frameless/pkg/logging"
)

func main() {
	var tb testing.TB

	logger.Testing(tb)

	// somewhere in your application
	logger.Debug(context.Background(), "the logging message", logging.Field("bar", 24))

}
Output:

func Warn

func Warn(ctx context.Context, msg string, ds ...logging.Detail)
Example
package main

import (
	"context"

	"go.llib.dev/frameless/pkg/logger"
)

func main() {
	ctx := context.Background()
	logger.Warn(ctx, "foo")
}
Output:

Types

type ConfigurationFunc added in v0.218.0

type ConfigurationFunc func(*logging.Logger)

type Fields deprecated

type Fields = logging.Fields

Fields is a collection of field that you can add to your loggig record. It will enrich the log entry with a value in the key you gave.

Deprecated: functionality is moved to logging package, use it from there.

Jump to

Keyboard shortcuts

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