README
¶
CommonLog
A common Go API for structured and unstructured logging with support for pluggable backends and sinks.
Supported backends (you can log to these APIs):
- simple (built-in textual, colorized backend; see below)
- klog (used by the Kubernetes client library)
- systemd journal
- zerolog
- slog (introduced in Go 1.21)
Supported sinks (you can capture logs from these APIs):
- Go built-in logging
- Go built-in structured logging
- hclog (used by many HashiCorp libraries)
Rationale
The main design goal is to unite your logging APIs and allow you to change its backend at startup. For example, you can choose to log to stderr by default or use journald when running as a systemd service. Sinks allow you to use your selected backend with imported 3rd-party libraries when they use different logging APIs. This design goal is inspired by SLF4J, and we must lament that the Go ecosystem ended up with the same logging challenges that have existed for years in the Java ecosystem.
A secondary design goal is to provide a home for a full-featured unstructured textual logging library, which we call the "simple" backend. It supports rich, customizable formatting, including ANSI coloring when logging to a terminal (even on Windows). So, CommonLog is useful if you're just looking for a straightforward logging solution right now that will not block you from using other backends in the future.
Note that efficiency and performance are not in themselves design goals for CommonLog, and indeed there is always some overhead involved in wrapper and sink implementations. For example, using zerolog directly involves no allocations, but using it via CommonLog will add allocations. To put it simply: if you want zero allocation you must use zerolog directly. Sinks can be especially inefficient because they may have to rely on capturing and parsing of log text. Programming is all about tradeoffs: CommonLog provides compatibility and flexibility at the cost of some efficiency and performance. However, as always, beware of premature optimization. How you are storing or transmitting your log messages is likely the biggest factor in your optimization narrative.
A FAQ is: Why not standardize on the built-in slog API? Slog indeed is a big step forward for Go, not only because it supports structured messages, but also because it decouples the handler, an interface, from the logger. This enables alternative backends, a feature tragically missing from Go's log library. Unfortunately, slog was introduced only in Go 1.21 and is thus not used by much go Go's pre-1.21 ecosystem of 3rd-party libraries. CommonLog supports slog both as a backend and as a sink, so you can easily mix the CommonLog API with slog API and handling.
Features
- Fine-grained control over verbosity via hierarchical log names. For example, "engine.parser.background"
inherits from "engine.parser", which in turn inherits from "engine". The empty name is the root of the
hierarchy. Each name's default verbosity is that of its parent, which you can then override with
commonlog.SetMaxLevel()
. - Support for call stack depth. This can be used by a backend (for example, by klog) to find out where in the code the logging happened.
- No need to create logger objects. The "true" API entrypoint is the global function
commonlog.NewMessage
, which you provide with a name and a level. The default logger type is just a convenient wrapper around it that provides the familiar unstructured functions. - The unstructured
commonlog.Logger
type is an interface, allowing you to more easily switch implementations per use without having to introduce a whole backend. For example, you can assign thecommonlog.MOCK_LOGGER
to disable a logger without changing the rest of your implementation. Compare with Go's built-inLogger
type, which frustratingly is a struct rather than an interface.
Basic Usage
The easiest way to plug in a backend is to anonymously import the correct sub-package into your program's main package:
import (
_ "github.com/tliron/commonlog/simple"
)
This should enable the backend with sensible defaults. Specifically it will log to stderr with verbosity at the "notice" max level.
Example of structured logging:
import (
"github.com/tliron/commonlog"
_ "github.com/tliron/commonlog/simple"
"github.com/tliron/kutil/util"
)
func main() {
if m := commonlog.NewErrorMessage(0, "engine", "parser"); m != nil {
m.Set("message", "Hello world!").Set("myfloat", 10.2).Send()
}
util.Exit(0)
}
Note that commonlog.NewMessage
will return nil if the message cannot be created, for example if
the message level is higher than the max level for that name, so you always need to check against nil.
Set
can accept any key and value, but two special keys are recognized by the API:
message
: The main description of the message. This is the key used by unstructured logging.scope
: An optional identifier that can be used to group messages, making them easier to filter (e.g. by grep on text). Backends may handle this specially. Unstructured backends may, for example, add it as a bracketed prefix for messages.
Also note that calling util.Exit(0)
to exit your program is not absolutely necessary, however
it's good practice because it makes sure to flush buffered log messages for some backends.
Unstructured logging is just a trivial case of structured logging in which only the message
key
is used. However, CommonLog provides a more familiar logging API:
import (
"github.com/tliron/commonlog"
_ "github.com/tliron/commonlog/simple"
"github.com/tliron/kutil/util"
)
var log = commonlog.GetLogger("engine.parser")
func main() {
log.Errorf("Hello %s!", "world")
util.Exit(0)
}
Use conditional logging to optimize for costly unstructured message creation, e.g.:
if log.AllowLevel(commonlog.Debug) {
log.Debugf("Status is: %s", getStatusFromDatabase())
}
The scope logger can be used to automatically set the "scope" key for another logger. It automatically detects nesting, in which case it appends the new scope separated by a ".", e.g.:
var log = commonlog.GetLogger("engine.parser")
var validationLog = commonlog.NewScopeLogger(log, "validation")
var syntaxLog = commonlog.NewScopeLogger(validationLog, "syntax")
func main() {
// Name is "engine.parser" and scope is "validation.syntax"
syntaxLog.Errorf("Hello %s!", "world")
...
}
Configuration
All backends can be configured via a common API to support writing to files or stderr. For example, to increase verbosity and write to a file:
func main() {
path := "myapp.log"
commonlog.Configure(1, &path) // nil path would write to stdout
...
}
Backends may also have their own (non-common) configuration APIs related to their specific features.
You can set the max level (verbosity) using either the global API or a logger. For example, here is a way to make all logging verbose by default, except for one name:
func init() {
commonlog.SetMaxLevel(commonlog.Debug) // the root
commonlog.SetMaxLevel(commonlog.Error, "engine", "parser")
}
Descendents of "engine.parser", e.g. "engine.parser.analysis", would inherit the "engine.parser" log levels rather than the root's. Here's the same effect using unstructured loggers:
var rootLog = commonlog.GetLogger("")
var parserLog = commonlog.GetLogger("engine.parser")
func init() {
rootLog.SetMaxLevel(commonlog.Debug)
parserLog.SetMaxLevel(commonlog.Error)
}
It's important to note that the configuration APIs are not thread safe. This includes
Configure()
and SetMaxLevel()
. Thus, make sure to get all your configuration done before
you start sending log messages. A good place for this is init()
or main()
functions.
Color
For the simple backend you must explicitly attempt to enable ANSI color if desired. Note that
if it's unsupported by the terminal then no ANSI codes will be sent (unless you force it via
terminal.EnableColor(true)
):
import (
"github.com/tliron/commonlog"
_ "github.com/tliron/commonlog/simple"
"github.com/tliron/kutil/terminal"
"github.com/tliron/kutil/util"
)
func main() {
cleanup, err := terminal.EnableColor(false)
util.FailOnError(err)
if cleanup != nil {
util.OnExitError(cleanup)
}
commonlog.GetLogger("engine.parser").Error("Hello world!") // errors are in red
util.Exit(0)
}
Documentation
¶
Index ¶
- func AllowLevel(level Level, name ...string) bool
- func CallAndLogError(f func() error, task string, log Logger)
- func Configure(verbosity int, path *string)
- func GetWriter() io.Writer
- func Initialize(verbosity int, path string)
- func SetBackend(backend_ Backend)
- func SetMaxLevel(level Level, name ...string)
- func SetMessageKeysAndValue(message Message, keysAndValues ...any)
- type Backend
- type BackendLogger
- func (self BackendLogger) AllowLevel(level Level) bool
- func (self BackendLogger) Critical(message string, keysAndValues ...any)
- func (self BackendLogger) Criticalf(format string, args ...any)
- func (self BackendLogger) Debug(message string, keysAndValues ...any)
- func (self BackendLogger) Debugf(format string, args ...any)
- func (self BackendLogger) Error(message string, keysAndValues ...any)
- func (self BackendLogger) Errorf(format string, args ...any)
- func (self BackendLogger) GetMaxLevel() Level
- func (self BackendLogger) Info(message string, keysAndValues ...any)
- func (self BackendLogger) Infof(format string, args ...any)
- func (self BackendLogger) Log(level Level, depth int, message string, keysAndValues ...any)
- func (self BackendLogger) Logf(level Level, depth int, format string, args ...any)
- func (self BackendLogger) NewMessage(level Level, depth int, keysAndValues ...any) Message
- func (self BackendLogger) Notice(message string, keysAndValues ...any)
- func (self BackendLogger) Noticef(format string, args ...any)
- func (self BackendLogger) SetMaxLevel(level Level)
- func (self BackendLogger) Warning(message string, keysAndValues ...any)
- func (self BackendLogger) Warningf(format string, args ...any)
- type Level
- type Logger
- type Message
- func NewCriticalMessage(depth int, name ...string) Message
- func NewDebugMessage(depth int, name ...string) Message
- func NewErrorMessage(depth int, name ...string) Message
- func NewInfoMessage(depth int, name ...string) Message
- func NewMessage(level Level, depth int, name ...string) Message
- func NewNoticeMessage(depth int, name ...string) Message
- func NewWarningMessage(depth int, name ...string) Message
- type MockLogger
- func (self MockLogger) AllowLevel(level Level) bool
- func (self MockLogger) Critical(message string, keysAndValues ...any)
- func (self MockLogger) Criticalf(format string, args ...any)
- func (self MockLogger) Debug(message string, keysAndValues ...any)
- func (self MockLogger) Debugf(format string, args ...any)
- func (self MockLogger) Error(message string, keysAndValues ...any)
- func (self MockLogger) Errorf(format string, args ...any)
- func (self MockLogger) GetMaxLevel() Level
- func (self MockLogger) Info(message string, keysAndValues ...any)
- func (self MockLogger) Infof(format string, args ...any)
- func (self MockLogger) Log(level Level, depth int, message string, keysAndValues ...any)
- func (self MockLogger) Logf(level Level, depth int, format string, args ...any)
- func (self MockLogger) NewMessage(level Level, depth int, keysAndValues ...any) Message
- func (self MockLogger) Notice(message string, keysAndValues ...any)
- func (self MockLogger) Noticef(format string, args ...any)
- func (self MockLogger) SetMaxLevel(level Level)
- func (self MockLogger) Warning(message string, keysAndValues ...any)
- func (self MockLogger) Warningf(format string, args ...any)
- type NameHierarchy
- type ScopeLogger
- func (self ScopeLogger) AllowLevel(level Level) bool
- func (self ScopeLogger) Critical(message string, keysAndValues ...any)
- func (self ScopeLogger) Criticalf(format string, args ...any)
- func (self ScopeLogger) Debug(message string, keysAndValues ...any)
- func (self ScopeLogger) Debugf(format string, args ...any)
- func (self ScopeLogger) Error(message string, keysAndValues ...any)
- func (self ScopeLogger) Errorf(format string, args ...any)
- func (self ScopeLogger) GetMaxLevel() Level
- func (self ScopeLogger) Info(message string, keysAndValues ...any)
- func (self ScopeLogger) Infof(format string, args ...any)
- func (self ScopeLogger) Log(level Level, depth int, message string, keysAndValues ...any)
- func (self ScopeLogger) Logf(level Level, depth int, format string, args ...any)
- func (self ScopeLogger) NewMessage(level Level, depth int, keysAndValues ...any) Message
- func (self ScopeLogger) Notice(message string, keysAndValues ...any)
- func (self ScopeLogger) Noticef(format string, args ...any)
- func (self ScopeLogger) SetMaxLevel(level Level)
- func (self ScopeLogger) Warning(message string, keysAndValues ...any)
- func (self ScopeLogger) Warningf(format string, args ...any)
- type SendUnstructuredMessageFunc
- type UnstructuredMessage
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AllowLevel ¶
func AllowLevel(level Level, name ...string) bool
Returns true if a level is loggable for the given name on the current backend.
Returns false if no backend was set.
func CallAndLogError ¶
func CallAndLogError(f func() error, task string, log Logger)
Convenience method to call a function and log the error, if returned, using [Logger.Errorf].
func Configure ¶
func Configure(verbosity int, path *string)
Configures the current backend. Verbosity is mapped to maximum loggable level as follows:
-4 and below: None -3: Critical -2: Error -1: Warning 0: Notice 1: Info 2 and above: Debug
Note that -4 (None) is a special case that is often optimized to turn off as much processing as possible.
No-op if no backend was set.
func GetWriter ¶
func GetWriter() io.Writer
Gets the current backend's io.Writer. Guaranteed to always return a valid non-nil value.
Can be io.Discard if unsupported by the backend or if no backend was set.
func Initialize ¶ added in v0.1.1
func Initialize(verbosity int, path string)
Convenience method to call Configure but automatically override the verbosity with -4 (None) if logging to stdout and terminal.Quiet is set to false.
func SetBackend ¶
func SetBackend(backend_ Backend)
Sets the current backend.
A nil backend will disable all logging (but the APIs would still not fail).
func SetMaxLevel ¶
func SetMaxLevel(level Level, name ...string)
Sets the maximum loggable level for the given name on the current backend. Will become the default maximum level for names deeper in the hierarchy unless explicitly set for them.
No-op if no backend was set.
func SetMessageKeysAndValue ¶ added in v0.2.3
func SetMessageKeysAndValue(message Message, keysAndValues ...any)
Calls [Message.Set] on a provided sequence of key-value pairs. Thus keysAndValues must have an even length.
Non-string keys are converted to strings using util.ToString.
Types ¶
type Backend ¶
type Backend interface {
// If "path" is nil will log to stdout, colorized if possible
// The default "verbosity" 0 will log criticals, errors, warnings, and notices.
// "verbosity" 1 will add infos. "verbosity" 2 will add debugs.
// Set "verbostiy" to -1 to disable the log.
Configure(verbosity int, path *string)
// Gets the backend's [io.Writer]. Can be nil if unsupported.
GetWriter() io.Writer
NewMessage(level Level, depth int, name ...string) Message
// Returns true if a level is loggable for the given name.
AllowLevel(level Level, name ...string) bool
// Sets the maximum loggable level for the given name. Will become the
// default maximum level for names deeper in the hierarchy unless
// explicitly set for them.
SetMaxLevel(level Level, name ...string)
// Gets the maximum loggable level for the given name.
GetMaxLevel(name ...string) Level
}
type BackendLogger ¶
type BackendLogger struct {
// contains filtered or unexported fields
}
Default Logger implementation that logs to the current backend set with SetBackend.
func NewBackendLogger ¶
func NewBackendLogger(name ...string) BackendLogger
func (BackendLogger) AllowLevel ¶
func (self BackendLogger) AllowLevel(level Level) bool
(Logger interface)
func (BackendLogger) Critical ¶
func (self BackendLogger) Critical(message string, keysAndValues ...any)
(Logger interface)
func (BackendLogger) Criticalf ¶
func (self BackendLogger) Criticalf(format string, args ...any)
(Logger interface)
func (BackendLogger) Debug ¶
func (self BackendLogger) Debug(message string, keysAndValues ...any)
(Logger interface)
func (BackendLogger) Debugf ¶
func (self BackendLogger) Debugf(format string, args ...any)
(Logger interface)
func (BackendLogger) Error ¶
func (self BackendLogger) Error(message string, keysAndValues ...any)
(Logger interface)
func (BackendLogger) Errorf ¶
func (self BackendLogger) Errorf(format string, args ...any)
(Logger interface)
func (BackendLogger) Info ¶
func (self BackendLogger) Info(message string, keysAndValues ...any)
(Logger interface)
func (BackendLogger) Infof ¶
func (self BackendLogger) Infof(format string, args ...any)
(Logger interface)
func (BackendLogger) Log ¶
func (self BackendLogger) Log(level Level, depth int, message string, keysAndValues ...any)
(Logger interface)
func (BackendLogger) Logf ¶
func (self BackendLogger) Logf(level Level, depth int, format string, args ...any)
(Logger interface)
func (BackendLogger) NewMessage ¶
func (self BackendLogger) NewMessage(level Level, depth int, keysAndValues ...any) Message
(Logger interface)
func (BackendLogger) Notice ¶
func (self BackendLogger) Notice(message string, keysAndValues ...any)
(Logger interface)
func (BackendLogger) Noticef ¶
func (self BackendLogger) Noticef(format string, args ...any)
(Logger interface)
func (BackendLogger) SetMaxLevel ¶
func (self BackendLogger) SetMaxLevel(level Level)
(Logger interface)
type Level ¶
type Level int
const (
None Level = 0
Critical Level = 1
Error Level = 2
Warning Level = 3
Notice Level = 4
Info Level = 5
Debug Level = 6
)
func GetMaxLevel ¶
func GetMaxLevel(name []string) Level
Gets the maximum loggable level for the given name on the current backend.
Returns None if no backend was set.
type Logger ¶
type Logger interface {
// Returns true if a level is loggable for this logger.
AllowLevel(level Level) bool
// Sets the maximum loggable level for this logger.
SetMaxLevel(level Level)
// Gets the maximum loggable level for this logger.
GetMaxLevel() Level
// Creates a new message for this logger. Will return nil if
// the level is not loggable.
//
// The depth argument is used for skipping frames in callstack
// logging, if supported.
NewMessage(level Level, depth int, keysAndValues ...any) Message
// Convenience method to create and send a message with at least
// the "message" key. Additional keys can be set by providing
// a sequence of key-value pairs.
Log(level Level, depth int, message string, keysAndValues ...any)
// Convenience method to create and send a message with just
// the "message" key, where the message is created via the format
// and args similarly to fmt.Printf.
Logf(level Level, depth int, format string, args ...any)
Critical(message string, keysAndValues ...any)
Criticalf(format string, args ...any)
Error(message string, keysAndValues ...any)
Errorf(format string, args ...any)
Warning(message string, keysAndValues ...any)
Warningf(format string, args ...any)
Notice(message string, keysAndValues ...any)
Noticef(format string, args ...any)
Info(message string, keysAndValues ...any)
Infof(format string, args ...any)
Debug(message string, keysAndValues ...any)
Debugf(format string, args ...any)
}
While NewMessage is the "true" API entry point, this interface enables a familiar logger API. Because it's an interface, references can easily replace the implementation, for example setting a reference to MOCK_LOGGER will disable the logger.
See GetLogger.
func GetLogger ¶
func GetLogger(path string) Logger
Creates a BackendLogger for the given path. The path is converted to a name using [string.Split] on ".".
func GetLoggerf ¶
func GetLoggerf(format string, values ...any) Logger
Calls GetLogger with fmt.Sprintf for the path.
type Message ¶
type Message interface {
// Sets a value on the message and returns the same message
// object.
//
// These keys are often specially supported:
//
// "message": the base text of the message
// "scope": the scope of the message
Set(key string, value any) Message
// Sends the message to the backend
Send()
}
func NewCriticalMessage ¶ added in v0.2.0
func NewCriticalMessage(depth int, name ...string) Message
Calls NewMessage with Critical level.
func NewDebugMessage ¶ added in v0.2.0
func NewDebugMessage(depth int, name ...string) Message
Calls NewMessage with Debug level.
func NewErrorMessage ¶ added in v0.2.0
func NewErrorMessage(depth int, name ...string) Message
Calls NewMessage with Error level.
func NewInfoMessage ¶ added in v0.2.0
func NewInfoMessage(depth int, name ...string) Message
Calls NewMessage with Info level.
func NewMessage ¶
func NewMessage(level Level, depth int, name ...string) Message
Creates a new message for the given name on the current backend. Will return nil if the level is not loggable for the name, is None, or if no backend was set.
The depth argument is used for skipping frames in callstack logging, if supported.
func NewNoticeMessage ¶ added in v0.2.0
func NewNoticeMessage(depth int, name ...string) Message
Calls NewMessage with Notice level.
func NewWarningMessage ¶ added in v0.2.0
func NewWarningMessage(depth int, name ...string) Message
Calls NewMessage with Warning level.
type MockLogger ¶
type MockLogger struct{}
Logger that does nothing.
var MOCK_LOGGER MockLogger
func (MockLogger) AllowLevel ¶
func (self MockLogger) AllowLevel(level Level) bool
(Logger interface)
func (MockLogger) Critical ¶
func (self MockLogger) Critical(message string, keysAndValues ...any)
(Logger interface)
func (MockLogger) Criticalf ¶
func (self MockLogger) Criticalf(format string, args ...any)
(Logger interface)
func (MockLogger) Debug ¶
func (self MockLogger) Debug(message string, keysAndValues ...any)
(Logger interface)
func (MockLogger) Debugf ¶
func (self MockLogger) Debugf(format string, args ...any)
(Logger interface)
func (MockLogger) Error ¶
func (self MockLogger) Error(message string, keysAndValues ...any)
(Logger interface)
func (MockLogger) Errorf ¶
func (self MockLogger) Errorf(format string, args ...any)
(Logger interface)
func (MockLogger) Info ¶
func (self MockLogger) Info(message string, keysAndValues ...any)
(Logger interface)
func (MockLogger) Infof ¶
func (self MockLogger) Infof(format string, args ...any)
(Logger interface)
func (MockLogger) Log ¶
func (self MockLogger) Log(level Level, depth int, message string, keysAndValues ...any)
(Logger interface)
func (MockLogger) Logf ¶
func (self MockLogger) Logf(level Level, depth int, format string, args ...any)
(Logger interface)
func (MockLogger) NewMessage ¶
func (self MockLogger) NewMessage(level Level, depth int, keysAndValues ...any) Message
(Logger interface)
func (MockLogger) Notice ¶
func (self MockLogger) Notice(message string, keysAndValues ...any)
(Logger interface)
func (MockLogger) Noticef ¶
func (self MockLogger) Noticef(format string, args ...any)
(Logger interface)
type NameHierarchy ¶ added in v0.2.0
type NameHierarchy struct {
// contains filtered or unexported fields
}
Convenience type for implementing maximum level per name in backends with support for inheritance.
func NewNameHierarchy ¶ added in v0.2.0
func NewNameHierarchy() *NameHierarchy
func (*NameHierarchy) AllowLevel ¶ added in v0.2.0
func (self *NameHierarchy) AllowLevel(level Level, name ...string) bool
func (*NameHierarchy) GetMaxLevel ¶ added in v0.2.0
func (self *NameHierarchy) GetMaxLevel(name ...string) Level
func (*NameHierarchy) SetMaxLevel ¶ added in v0.2.0
func (self *NameHierarchy) SetMaxLevel(level Level, name ...string)
type ScopeLogger ¶
type ScopeLogger struct {
// contains filtered or unexported fields
}
Wrapping Logger that calls [Message.Set] with a "scope" key on all messages. There is special support for nesting scope loggers such that a nested scope string is appended to the wrapped scope with a "." notation.
func NewScopeLogger ¶
func NewScopeLogger(logger Logger, scope string) ScopeLogger
func (ScopeLogger) AllowLevel ¶
func (self ScopeLogger) AllowLevel(level Level) bool
(Logger interface)
func (ScopeLogger) Critical ¶
func (self ScopeLogger) Critical(message string, keysAndValues ...any)
(Logger interface)
func (ScopeLogger) Criticalf ¶
func (self ScopeLogger) Criticalf(format string, args ...any)
(Logger interface)
func (ScopeLogger) Debug ¶
func (self ScopeLogger) Debug(message string, keysAndValues ...any)
(Logger interface)
func (ScopeLogger) Debugf ¶
func (self ScopeLogger) Debugf(format string, args ...any)
(Logger interface)
func (ScopeLogger) Error ¶
func (self ScopeLogger) Error(message string, keysAndValues ...any)
(Logger interface)
func (ScopeLogger) Errorf ¶
func (self ScopeLogger) Errorf(format string, args ...any)
(Logger interface)
func (ScopeLogger) Info ¶
func (self ScopeLogger) Info(message string, keysAndValues ...any)
(Logger interface)
func (ScopeLogger) Infof ¶
func (self ScopeLogger) Infof(format string, args ...any)
(Logger interface)
func (ScopeLogger) Log ¶
func (self ScopeLogger) Log(level Level, depth int, message string, keysAndValues ...any)
(Logger interface)
func (ScopeLogger) Logf ¶
func (self ScopeLogger) Logf(level Level, depth int, format string, args ...any)
(Logger interface)
func (ScopeLogger) NewMessage ¶
func (self ScopeLogger) NewMessage(level Level, depth int, keysAndValues ...any) Message
(Logger interface)
func (ScopeLogger) Notice ¶
func (self ScopeLogger) Notice(message string, keysAndValues ...any)
(Logger interface)
func (ScopeLogger) Noticef ¶
func (self ScopeLogger) Noticef(format string, args ...any)
(Logger interface)
func (ScopeLogger) SetMaxLevel ¶
func (self ScopeLogger) SetMaxLevel(level Level)
(Logger interface)
type SendUnstructuredMessageFunc ¶
type SendUnstructuredMessageFunc func(message string)
type UnstructuredMessage ¶
type UnstructuredMessage struct {
// contains filtered or unexported fields
}
Convenience type for implementing unstructured backends. Converts a structured message to an unstructured string.
func NewUnstructuredMessage ¶
func NewUnstructuredMessage(send SendUnstructuredMessageFunc) *UnstructuredMessage