Skip to content
This repository has been archived by the owner on Oct 2, 2022. It is now read-only.

Commit

Permalink
0.9.10: Moved to struct-based logging
Browse files Browse the repository at this point in the history
- Added a new error type called `Message` as a preferred way of generating errors.
- Removed error methods, such as `Noticee`, etc
- Added labels to messages and loggers
  • Loading branch information
Janos Pasztor committed Feb 19, 2021
1 parent 4c0a27b commit ca8832c
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 87 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog

## 0.9.10: Moved to struct-based logging

- Added a new error type called `Message` as a preferred way of generating errors.
- Removed error methods, such as `Noticee`, etc
- Added labels to messages and loggers

## 0.9.9: Added test logger

This release adds a test logger that helps with logging for tests.
Expand Down Expand Up @@ -116,5 +122,5 @@ p := pipeline.NewLoggerPipeline(minimumLogLevel, logFormatter, writer)
p.Warning("test")
```

This will create a pipeline that writes log messages to the standard output in newline-delimited JSON format. You can, of course, also implement your own log formatter by implementing the interface in [formatter.go](formatter.go).
This will create a pipeline that writes log messages to the standard output in newline-delimited JSON format.

8 changes: 8 additions & 0 deletions CODES.md
@@ -0,0 +1,8 @@
# Error/message codes

| Code | Explanation |
|------|-------------|
| `E_LOG_WRITE_FAILED` | ContainerSSH failed to write to the log output. This is fatal error and will cause ContainerSSH to stop. |
| `E_LOG_ROTATE_FAILED` | ContainerSSH failed to close and reopen the logs. This is a fatal error and will cause ContainerSSH to stop. |
| `E_LOG_FILE_OPEN_FAILED` | ContainerSSH failed to open a log file. This is a fatal error and will cause ContainerSSH to stop. |
| `E_UNKNOWN_ERROR` | This is a generic error without any specificity. Please report this as a bug. |
56 changes: 32 additions & 24 deletions README.md
Expand Up @@ -20,12 +20,12 @@ The main `Message` structure has several properties: a unique error code, a user

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

### `log.NewMessage()`
### `log.UserMessage()`

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

```go
msg := log.NewMessage(
msg := log.UserMessage(
"E_SOME_ERROR_CODE",
"Dear user, an internal error happened.",
"Details about the error (%s)",
Expand All @@ -38,24 +38,24 @@ msg := log.NewMessage(
- 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.Error()`
### `log.NewMessage()`

The `log.Error()` method is a simplified version of `log.NewMessage()` without the user-facing message. The user-facing message will always be `Internal Error.`. The method signature is the following:
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:

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

### `log.Wrap()`
### `log.WrapUser()`

The `log.Wrap()` method can be used to create a wrapped error. It automatically appends the original error message to the administrator-facing message. The function signature is the following:
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:

```go
msg := log.Wrap(
msg := log.WrapUser(
originalErr,
"E_SOME_CODE",
"Dear user, some error happened."
Expand All @@ -65,12 +65,12 @@ msg := log.Wrap(
)
```

### `log.WrapError()`
### `log.Wrap()`

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

```go
msg := log.WrapError(
msg := log.Wrap(
originalErr,
"E_SOME_CODE",
"Dear admin, an error happened. %s" +
Expand All @@ -97,18 +97,26 @@ As mentioned before, the `Message` interface implements the `error` interface, s

## Logging

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

- `logger.Debug(err)`
- `logger.Info(err)`
- `logger.Notice(err)`
- `logger.Warning(err)`
- `logger.Error(err)`
- `logger.Critical(err)`
- `logger.Alert(err)`
- `logger.Emergency(err)`

We also provide the following compatibility methods:
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.Debugf(format string, args ...interface{})`
- `logger.Info(message ...interface{})`
- `logger.Infof(format string, args ...interface{})`
- `logger.Notice(message ...interface{})`
- `logger.Noticef(format string, args ...interface{})`
- `logger.Warning(message ...interface{})`
- `logger.Warningf(format string, args ...interface{})`
- `logger.Error(message ...interface{})`
- `logger.Errorf(format string, args ...interface{})`
- `logger.Critical(message ...interface{})`
- `logger.Criticalf(format string, args ...interface{})`
- `logger.Alert(message ...interface{})`
- `logger.Alertf(format string, args ...interface{})`
- `logger.Emergency(message ...interface{})`
- `logger.Emergencyf(format string, args ...interface{})`

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

- `logger.Log(v ...interface{})`
- `logger.Logf(format string, v ...interface{})`
Expand Down
39 changes: 31 additions & 8 deletions logger.go
Expand Up @@ -17,21 +17,44 @@ type Logger interface {
WithLabel(labelName LabelName, labelValue LabelValue) Logger

// Debug logs a message at the debug level.
Debug(message error)
Debug(message ...interface{})
// Debugf logs a message at the debug level with a formatting string.
Debugf(format string, args ...interface{})

// Info logs a message at the info level.
Info(message error)
Info(message ...interface{})
// Infof logs a message at the info level with a formatting string.
Infof(format string, args ...interface{})

// Notice logs a message at the notice level.
Notice(message error)
Notice(message ...interface{})
// Noticef logs a message at the notice level with a formatting string.
Noticef(format string, args ...interface{})

// Warning logs a message at the warning level.
Warning(message error)
Warning(message ...interface{})
// Warningf logs a message at the warning level with a formatting string.
Warningf(format string, args ...interface{})

// Error logs a message at the error level.
Error(message error)
Error(message ...interface{})
// Errorf logs a message at the error level with a formatting string.
Errorf(format string, args ...interface{})

// Critical logs a message at the critical level.
Critical(message error)
Critical(message ...interface{})
// Criticalf logs a message at the critical level with a formatting string.
Criticalf(format string, args ...interface{})

// Alert logs a message at the alert level.
Alert(message error)
Alert(message ...interface{})
// Alertf logs a message at the alert level with a formatting string
Alertf(format string, args ...interface{})

// Emergency logs a message at the emergency level.
Emergency(message error)
Emergency(message ...interface{})
// Emergencyf logs a message at the emergency level with a formatting string.
Emergencyf(format string, message ...interface{})

// Log logs a number of objects or strings to the log.
Log(v ...interface{})
Expand Down
103 changes: 74 additions & 29 deletions logger_impl.go
Expand Up @@ -41,12 +41,25 @@ func (pipeline *logger) WithLabel(labelName LabelName, labelValue LabelValue) Lo

//region Format

func (pipeline *logger) write(level Level, err error) {
func (pipeline *logger) write(level Level, message ...interface{}) {
if pipeline.level >= level {
if len(message) == 0 {
return
}
var msg Message
var ok bool
if msg, ok = err.(Message); !ok {
msg = pipeline.wrapError(err)
if len(message) == 1 {
switch message[0].(type) {
case Message:
msg = message[0].(Message)
case error:
msg = pipeline.wrapError(message[0].(error))
case string:
msg = NewMessage(EUnknownError, message[0].(string))
default:
msg = NewMessage(EUnknownError, "%v", message[0])
}
} else {
msg = NewMessage(EUnknownError, "%v", message)
}

for label, value := range pipeline.labels {
Expand All @@ -59,8 +72,24 @@ func (pipeline *logger) write(level Level, err error) {
}
}

func (pipeline *logger) writef(level Level, format string, args ...interface{}) {
if pipeline.level >= level {
var msg Message

msg = NewMessage(EUnknownError, format, args...)

for label, value := range pipeline.labels {
msg = msg.Label(label, value)
}

if err := pipeline.writer.Write(level, msg); err != nil {
panic(err)
}
}
}

func (pipeline *logger) wrapError(err error) Message {
return WrapError(
return Wrap(
err,
EUnknownError,
"An unexpected error has happened.",
Expand All @@ -71,44 +100,60 @@ func (pipeline *logger) wrapError(err error) Message {

//region Messages

// Emergency writes a string message on the emergency level
func (pipeline *logger) Emergency(err error) {
pipeline.write(LevelEmergency, err)
func (pipeline *logger) Emergency(message ...interface{}) {
pipeline.write(LevelEmergency, message)
}
func (pipeline *logger) Emergencyf(format string, args ...interface{}) {
pipeline.writef(LevelEmergency, format, args)
}

// Alert writes a string message on the alert level
func (pipeline *logger) Alert(err error) {
pipeline.write(LevelAlert, err)
func (pipeline *logger) Alert(message ...interface{}) {
pipeline.write(LevelAlert, message)
}
func (pipeline *logger) Alertf(format string, args ...interface{}) {
pipeline.writef(LevelAlert, format, args)
}

// Critical writes a string message on the critical level
func (pipeline *logger) Critical(err error) {
pipeline.write(LevelCritical, err)
func (pipeline *logger) Critical(message ...interface{}) {
pipeline.write(LevelCritical, message)
}
func (pipeline *logger) Criticalf(format string, args ...interface{}) {
pipeline.writef(LevelCritical, format, args)
}

// Error writes a string message on the error level
func (pipeline *logger) Error(err error) {
pipeline.write(LevelError, err)
func (pipeline *logger) Error(message ...interface{}) {
pipeline.write(LevelError, message)
}
func (pipeline *logger) Errorf(format string, args ...interface{}) {
pipeline.writef(LevelError, format, args)
}

// Warning writes a string message on the warning level
func (pipeline *logger) Warning(err error) {
pipeline.write(LevelWarning, err)
func (pipeline *logger) Warning(message ...interface{}) {
pipeline.write(LevelWarning, message)
}
func (pipeline *logger) Warningf(format string, args ...interface{}) {
pipeline.writef(LevelWarning, format, args)
}

// Notice writes a string message on the notice level
func (pipeline *logger) Notice(err error) {
pipeline.write(LevelNotice, err)
func (pipeline *logger) Notice(message ...interface{}) {
pipeline.write(LevelNotice, message)
}
func (pipeline *logger) Noticef(format string, args ...interface{}) {
pipeline.writef(LevelNotice, format, args)
}

// Info writes a string message on the info level
func (pipeline *logger) Info(err error) {
pipeline.write(LevelInfo, err)
func (pipeline *logger) Info(message ...interface{}) {
pipeline.write(LevelInfo, message)
}
func (pipeline *logger) Infof(format string, args ...interface{}) {
pipeline.writef(LevelInfo, format, args)
}

// Debug writes a string message on the debug level
func (pipeline *logger) Debug(err error) {
pipeline.write(LevelDebug, err)
func (pipeline *logger) Debug(message ...interface{}) {
pipeline.write(LevelDebug, message)
}
func (pipeline *logger) Debugf(format string, args ...interface{}) {
pipeline.writef(LevelDebug, format, args)
}

//endregion
Expand Down
2 changes: 1 addition & 1 deletion logger_test.go
Expand Up @@ -36,7 +36,7 @@ func testLevel(t *testing.T, logLevel log.Level, writeLogLevel log.Level) {
Output: log.OutputStdout,
Stdout: &buf,
})
message := log.NewMessage("E_TEST", "test", "test")
message := log.UserMessage("E_TEST", "test", "test")
switch writeLogLevel {
case log.LevelDebug:
p.Debug(message)
Expand Down

0 comments on commit ca8832c

Please sign in to comment.