diff --git a/docs/code_formatting_rules.md b/docs/code_formatting_rules.md index 731bb6406..ad58745d7 100644 --- a/docs/code_formatting_rules.md +++ b/docs/code_formatting_rules.md @@ -178,6 +178,95 @@ be used for calls to formatting functions like `fmt.Errorf`, But not for statements that are important for the flow or logic of the code, like `require.NoErrorf()`. +#### Exceptions and additional styling for structured logging + +When making use of structured logging calls (there are any `btclog.Logger` +methods ending in `S`), a few different rules and exceptions apply. + +1) **Static messages:** Structured log calls take a `context.Context` as a first +parameter and a _static_ string as the second parameter (the `msg` parameter). +Formatted strings should ideally not be used for the construction of the `msg` +parameter. Instead, key-value pairs (or `slog` attributes) should be used to +provide additional variables to the log line. + +**WRONG** +```go +log.DebugS(ctx, fmt.Sprintf("User %d just spent %.8f to open a channel", userID, 0.0154)) +``` + +**RIGHT** +```go +log.InfoS(ctx, "Channel open performed", + slog.Int("user_id", userID), + btclog.Fmt("amount", "%.8f", 0.00154)) +``` + +2) **Key-value attributes**: The third parameter in any structured log method is +a variadic list of the `any` type but it is required that these are provided in +key-value pairs such that an associated `slog.Attr` variable can be created for +each key-value pair. The simplest way to specify this is to directly pass in the +key-value pairs as raw literals as follows: + +```go +log.InfoS(ctx, "Channel open performed", "user_id", userID, "amount", 0.00154) +``` +This does work, but it becomes easy to make a mistake and accidentally leave out +a value for each key provided leading to a nonsensical log line. To avoid this, +it is suggested to make use of the various `slog.Attr` helper functions as +follows: + +```go +log.InfoS(ctx, "Channel open performed", + slog.Int("user_id", userID), + btclog.Fmt("amount", "%.8f", 0.00154)) +``` + +3) **Line wrapping**: Structured log lines are an exception to the 80-character +line wrapping rule. This is so that the key-value pairs can be easily read and +reasoned about. If it is the case that there is only a single key-value pair +and the entire log line is still less than 80 characters, it is acceptable to +have the key-value pair on the same line as the log message. However, if there +are multiple key-value pairs, it is suggested to use the one line per key-value +pair format. Due to this suggestion, it is acceptable for any single key-value +pair line to exceed 80 characters for the sake of readability. + +**WRONG** +```go +// Example 1. +log.InfoS(ctx, "User connected", + "user_id", userID) + +// Example 2. +log.InfoS(ctx, "Channel open performed", "user_id", userID, + btclog.Fmt("amount", "%.8f", 0.00154), "channel_id", channelID) + +// Example 3. +log.InfoS(ctx, "Bytes received", + "user_id", userID, + btclog.Hex("peer_id", peerID.SerializeCompressed()), + btclog.Hex("message", []bytes{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }))) +``` + +**RIGHT** +```go +// Example 1. +log.InfoS(ctx, "User connected", "user_id", userID) + +// Example 2. +log.InfoS(ctx, "Channel open performed", + slog.Int("user_id", userID), + btclog.Fmt("amount", "%.8f", 0.00154), + slog.String("channel_id", channelID)) + +// Example 3. +log.InfoS(ctx, "Bytes received", + "user_id", userID, + btclog.Hex("peer_id", peerID.SerializeCompressed()), + btclog.Hex("message", []bytes{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}))) +``` + ### Wrapping long function definitions If one is forced to wrap lines of function arguments that exceed the