Categorygithub.com/containerssh/log
modulepackage
1.1.6
Repository: https://github.com/containerssh/log.git
Documentation: pkg.go.dev

# README

ContainerSSH - Launch Containers on Demand

ContainerSSH Logging Library

Go Report Card LGTM Alerts

This library provides internal logging for ContainerSSH. Its functionality is very similar to how syslog is structured.

⚠⚠⚠ Warning: This is a developer documentation. ⚠⚠⚠
The user documentation for ContainerSSH is located at containerssh.io.

Basic concept

This is not the logger you would expect from the go-log library. This library combines the Go errors and the log messages into one. In other words, the Message object can be used both as an error and a log message.

The main Message structure has several properties: a unique error code, a user-safe error message and a detailed error message for the system administrator. Additionally, it also may contain several key-value pairs called labels to convey additional information.

Creating a message

If you want to create a message you can use the following methods:

log.UserMessage()

The log.UserMessage method creates a new Message with a user-facing message structure as follows:

msg := log.UserMessage(
    "E_SOME_ERROR_CODE",
    "Dear user, an internal error happened.",
    "Details about the error (%s)",
    "Some string that will end up instead of %s."
)
  • The first parameter is an error code that is unique so people can identify the error. This error code should be documented so users can find it easily.
  • The second parameter is a string printed to the user. It does not support formatting characters.
  • The third parameter is a string that can be logged for the system administrator. It can contain fmt.Sprintf-style formatting characters.
  • All subsequent parameters are used to replace the formatting characters.

log.NewMessage()

The log.NewMessage() method is a simplified version of log.UserMessage() without the user-facing message. The user-facing message will always be Internal Error.. The method signature is the following:

msg := log.NewMessage(
    "E_SOME_ERROR_CODE",
    "Details about the error (%s)",
    "Some string that will end up instead of %s."
)

log.WrapUser()

The log.WrapUser() method can be used to create a wrapped error with a user-facing message. It automatically appends the original error message to the administrator-facing message. The function signature is the following:

msg := log.WrapUser(
    originalErr,
    "E_SOME_CODE",
    "Dear user, some error happened."
    "Dear admin, an error happened. %s" + 
        "The error message will be appended to this message."
    "This string will appear instead of %s in the admin-message."
)

log.Wrap()

Like the log.WrapUser() method the log.Wrap() will skip the user-visible error message and otherwise be identical to log.Wrap().

msg := log.Wrap(
    originalErr,
    "E_SOME_CODE",
    "Dear admin, an error happened. %s" + 
        "The error message will be appended to this message."
    "This string will appear instead of %s in the admin-message."
)

Adding labels to messages

Labels are useful for recording extra information with messages that can be indexed by the logging system. These labels may or may not be recorded by the logging backend. For example, the syslog output doesn't support recording labels due to size constraints. In other words, the message itself should contain enough information for an administrator to interpret the error.

You can add labels to messages like this:

msg.Label("labelName", "labelValue")

Hint: Label() calls can be chained.

Using messages

As mentioned before, the Message interface implements the error interface, so these messages can simply be returned like a normal error would.

Logging

This library also provides a Logger interface that can log all kinds of messages and errors, including the Message interface. It provides the following methods for logging:

  • logger.Debug(message ...interface{})
  • logger.Info(message ...interface{})
  • logger.Notice(message ...interface{})
  • logger.Warning(message ...interface{})
  • logger.Error(message ...interface{})
  • logger.Critical(message ...interface{})
  • logger.Alert(message ...interface{})
  • logger.Emergency(message ...interface{})

You are encouraged to pass a Message implementation to it.

We also provide the following compatibility methods which log at the info level.

  • logger.Log(v ...interface{})
  • logger.Logf(format string, v ...interface{})

We provide a method to create a child logger that has a different minimum log level. Messages below this level will be discarded:

newLogger := logger.WithLevel(log.LevelInfo)

We can also create a new logger copy with default labels added:

newLogger := logger.WithLabel("label name", "label value")

Finally, the logger also supports calling the Rotate() and Close() methods. Rotate() instructs the output to close all handles and reopen them to facilitate rotating logs. Close() permanently closes the writer.

Creating a logger

The Logger interface is intended for generic implementations. The default implementation can be created as follows:

logger, err := log.NewLogger(config)

Alternatively, you can also use the log.MustNewLogger method to skip having to deal with the error. (It will panic if an error happens.)

If you need a factory you can use the log.LoggerFactory interface and the log.NewLoggerFactory to create a factory you can pass around. The Make(config) method will make a new logger when needed.

Configuration

The configuration structure for the default logger implementation is contained in the log.Config structure.

Configuring the output format

The most important configuration is where your logs will end up:

log.Config{
    Output: log.OutputStdout,
}

The following options are possible:

  • log.OutputStdout logs to the standard output or any other io.Writer
  • log.OutputFile logs to a file on the local filesystem.
  • log.OutputSyslog logs to a syslog server using a UNIX or UDP socket.

Logging to stdout

If you set the Output option to log.OutputStdout the output will be written to the standard output in the format specified below (see "Changing the log format"). The destination can be overridden:

log.Config {
    Output: log.OutputStdout,
    Stdout: someOtherWriter,
}

Logging to a file

The file logger is configured as follows:

log.Config {
    Output: log.OutputFile,
    File: "/var/log/containerssh.log",
}

You can call the Rotate() method on the logger to close and reopen the file. This allows for log rotation.

Logging to syslog

The syslog logger writes to a syslog daemon. Typically, this is located on the /dev/log UNIX socket, but sending logs over UDP is also supported. TCP, encryption, and other advanced Syslog features are not supported, so adding a Syslog daemon on the local node is strongly recommended.

The configuration is the following:

log.Config{
    Output: log.OutputSyslog,
    Facility: log.FacilityStringAuth, // See facilities list below
    Tag: "ProgramName", // Add program name here
    Pid: false, // Change to true to add the PID to the Tag
    Hostname: "" // Change to override host name 
}

The following facilities are supported:

  • log.FacilityStringKern
  • log.FacilityStringUser
  • log.FacilityStringMail
  • log.FacilityStringDaemon
  • log.FacilityStringAuth
  • log.FacilityStringSyslog
  • log.FacilityStringLPR
  • log.FacilityStringNews
  • log.FacilityStringUUCP
  • log.FacilityStringCron
  • log.FacilityStringAuthPriv
  • log.FacilityStringFTP
  • log.FacilityStringNTP
  • log.FacilityStringLogAudit
  • log.FacilityStringLogAlert
  • log.FacilityStringClock
  • log.FacilityStringLocal0
  • log.FacilityStringLocal1
  • log.FacilityStringLocal2
  • log.FacilityStringLocal3
  • log.FacilityStringLocal4
  • log.FacilityStringLocal5
  • log.FacilityStringLocal6
  • log.FacilityStringLocal7

Changing the log format

We currently support two log formats: text and ljson. The format is applied for the stdout and file outputs and can be configured as follows:

log.Config {
    Format: log.FormatText|log.FormatLJSON,
}

The text format

The text format is structured as follows:

TIMESTAMP[TAB]LEVEL[TAB]MODULE[TAB]MESSAGE[NEWLINE]
  • TIMESTAMP is the timestamp of the message in RFC3339 format.
  • LEVEL is the level of the message (debug, info, notice, warning, error, critical, alert, emergency)
  • MODULE is the name of the module logged. May be empty.
  • MESSAGE is the text message or structured data logged.

This format is recommended for human consumption only.

The ljson format

This format logs in a newline-delimited JSON format. Each message has the following format:

{"timestamp": "TIMESTAMP", "level": "LEVEL", "module": "MODULE", "message": "MESSAGE", "details": "DETAILS"}
  • TIMESTAMP is the timestamp of the message in RFC3339 format.
  • LEVEL is the level of the message (debug, info, notice, warning, error, critical, alert, emergency)
  • MODULE is the name of the module logged. May be absent if not sent.
  • MESSAGE is the text message. May be absent if not set.
  • DETAILS is a structured log message. May be absent if not set.

Creating a logger for testing

You can create a logger for testing purposes that logs using the t *testing.T log facility:

logger := log.NewTestLogger(t)

You may also want to enable GitHub Actions logging by creating the following method:

func TestMain(m *testing.M) {
	log.RunTests(m)
}

Generating message code files

This package also includes a utility to generate and update a CODES.md from a codes.go file in your repository to create a documentation about message codes.

To use this utility your codes.go must contain message codes in individual const declarations with documentation, not as a const block. You can install this package as follows:

go get -u github.com/containerssh/log/cmd/containerssh-generate-codes

You can then execute containerssh-generate-codes to generate CODES.md. Optionally, you can pass the filenames as parameters:

containerssh-generate-codes source.go DESTINATION.md

We recommend creating a codes_doc.go file with the following content:

package yourpackage

//go:generate containerssh-generate-codes

This lets you generate CODES.md using go generate.

# Packages

No description provided by the author

# Functions

GenerateMessageCodeFiles generates the contents of the CODES.md file and returns them.
GetMessageCodes parses the specified go source file and returns a key-value mapping of message codes and their associated description or an error.
MustNewLogger is identical to NewLogger, except that it panics instead of returning an error.
MustWriteMessageCodesFile is identical to WriteMessageCodesFile but panics on error.
NewGoLogWriter creates an adapter for the go logger that writes using the Log method of the logger.
NewLogger creates a standard logger pipeline.
NewLoggerFactory create a pipeline logger factorygoland:noinspection GoUnusedExportedFunction.
NewMessage creates an internal error with only the explanation for the administrator inserted.
NewTestLogger creates a logger for testing purposes.goland:noinspection GoUnusedExportedFunction.
RunTests is a helper function that transforms the test output depending on the CI system running.
UserMessage constructs a Message.
Wrap creates a wrapped error with a specific Code and Explanation string.
WrapUser creates a Message wrapping an error with a user-facing message.
WriteMessageCodesFile generates and writes the CODES.md file.

# Constants

DestinationFile is writing the log messages to a file.
DestinationStdout is writing log messages to the standard output.
DestinationSyslog is writing log messages to syslog.
DestinationTest writes the logs to the *testing.T facility.
ContainerSSH failed to open the specified log file.
ContainerSSH cannot rotate the logs as requested because of an underlying error.
ContainerSSH cannot write to the specified log file.
This is an untyped error.
FacilityAuth are authentication messages.
FacilityAuthPriv are security/authorization messages.
FacilityClock are clock daemon messages.
FacilityCron are clock daemon messages.
FacilityDaemon are daemon messages.
FacilityFTP are FTP daemon messages.
FacilityKern are kernel messages.
FacilityLocal0 are locally administered messages.
FacilityLocal1 are locally administered messages.
FacilityLocal2 are locally administered messages.
FacilityLocal3 are locally administered messages.
FacilityLocal4 are locally administered messages.
FacilityLocal5 are locally administered messages.
FacilityLocal6 are locally administered messages.
FacilityLocal7 are locally administered messages.
FacilityLogAlert are log alert messages.
FacilityLogAudit are log audit messages.
FacilityLPR are printer messages.
FacilityMail are user mail log messages.
FacilityNews are news messages.
FacilityNTP are network time daemon messages.
FacilityStringAuth are authentication messages.
FacilityStringAuthPriv are security/authorization messages.
FacilityStringClock are clock daemon messages.
FacilityStringCron are clock daemon messages.
FacilityStringDaemon are daemon messages.
FacilityStringFTP are FTP daemon messages.
FacilityStringKern are kernel messages.
FacilityStringLocal0 are locally administered messages.
FacilityStringLocal1 are locally administered messages.
FacilityStringLocal2 are locally administered messages.
FacilityStringLocal3 are locally administered messages.
FacilityStringLocal4 are locally administered messages.
FacilityStringLocal5 are locally administered messages.
FacilityStringLocal6 are locally administered messages.
FacilityStringLocal7 are locally administered messages.
FacilityStringLogAlert are log alert messages.
FacilityStringLogAudit are log audit messages.
FacilityStringLPR are printer messages.
FacilityStringMail are user mail log messages.
FacilityStringNews are news messages.
FacilityStringNTP are network time daemon messages.
FacilityStringSyslog are syslog-specific messages.
FacilityStringUser are user level messages.
FacilityStringUUCP are UUCP subsystem messages.
FacilitySyslog are syslog-specific messages.
FacilityUser are user level messages.
FacilityUUCP are UUCP subsystem messages.
FormatLJSON is a newline-delimited JSON log format.
FormatText prints the logs as plain text.
Supported values for Level.
List of valid string values for log levels.
Supported values for Level.
List of valid string values for log levels.
Supported values for Level.
List of valid string values for log levels.
Supported values for Level.
List of valid string values for log levels.
Supported values for Level.
List of valid string values for log levels.
Supported values for Level.
List of valid string values for log levels.
Supported values for Level.
List of valid string values for log levels.
Supported values for Level.
List of valid string values for log levels.
This is message that should only be seen in unit and component tests, never in production.goland:noinspection GoUnusedConst.

# Structs

Config describes the logging settings.
SyslogConfig is the configuration for syslog logging.goland:noinspection GoVetStructTag.

# Interfaces

LabelValue is a string, int, bool, or float.
Logger The logger interface provides logging facilities on various levels.
LoggerFactory is a factory to create a logger on demand.
Message is a message structure for error reporting in ContainerSSH.
WrappingMessage is a message that wraps a different error.
Writer is a specialized writer to write a line of log messages.

# Type aliases

Destination is the output to write to.swagger:enum.
Priority.
FacilityString are facility names.
Format is the logging format to use.swagger:enum.
LabelName is a name for a Message label.
Labels is a map linking.
Level syslog-style log level identifiers.
LevelString is a type for supported log level strings.