Skip to content

Commit

Permalink
cli: add flag to specify log format (console, json)
Browse files Browse the repository at this point in the history
Signed-off-by: VirtualTam <virtualtam@flibidi.net>
  • Loading branch information
virtualtam committed Jan 22, 2024
1 parent 2fbf0da commit 6f98739
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 33 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ENV \
SPARKLEMUFFIN_LISTEN_ADDR="0.0.0.0:8080" \
SPARKLEMUFFIN_METRICS_LISTEN_ADDR="127.0.0.1:8081" \
SPARKLEMUFFIN_PUBLIC_ADDR="http://localhost:8080" \
SPARKLEMUFFIN_LOG_FORMAT="json" \
SPARKLEMUFFIN_LOG_LEVEL="info"

EXPOSE 8080
Expand Down
28 changes: 9 additions & 19 deletions cmd/sparklemuffin/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const (
var (
defaultLogLevelValue string = zerolog.LevelInfoValue
logLevelValue string
logFormat string

versionDetails *version.Details

Expand Down Expand Up @@ -84,8 +85,6 @@ func NewRootCommand() *cobra.Command {
return nil
}

var err error

// Configuration file lookup paths
home, err := os.UserHomeDir()
if err != nil {
Expand All @@ -105,15 +104,10 @@ func NewRootCommand() *cobra.Command {
}

// Global logger configuration
var logLevel zerolog.Level

if err := logLevel.UnmarshalText([]byte(logLevelValue)); err != nil {
log.Error().Err(err).Msg("invalid log level")
if err := config.SetupGlobalLogger(logFormat, logLevelValue); err != nil {
return err
}

zerolog.SetGlobalLevel(logLevel)

if configFileUsed := v.ConfigFileUsed(); configFileUsed != "" {
log.Info().Str("config_file", v.ConfigFileUsed()).Msg("configuration: using file")
} else {
Expand Down Expand Up @@ -175,23 +169,19 @@ func NewRootCommand() *cobra.Command {
},
}

var logLevelValues = []string{
zerolog.LevelTraceValue,
zerolog.LevelDebugValue,
zerolog.LevelInfoValue,
zerolog.LevelWarnValue,
zerolog.LevelErrorValue,
zerolog.LevelFatalValue,
zerolog.LevelPanicValue,
}

cmd.PersistentFlags().StringVar(
&logFormat,
"log-format",
config.LogFormatConsole,
fmt.Sprintf("Log format (%s, %s)", config.LogFormatJSON, config.LogFormatConsole),
)
cmd.PersistentFlags().StringVar(
&logLevelValue,
"log-level",
defaultLogLevelValue,
fmt.Sprintf(
"Log level (%s)",
strings.Join(logLevelValues, ", "),
strings.Join(config.LogLevelValues, ", "),
),
)

Expand Down
65 changes: 65 additions & 0 deletions cmd/sparklemuffin/config/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) VirtualTam
// SPDX-License-Identifier: MIT

package config

import (
"fmt"
"os"
"time"

"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)

const (
// Format log messages as pretty-printed key-value pairs.
LogFormatConsole = "console"

// Format log messages as JSON documents.
LogFormatJSON = "json"
)

var (
// Available logger levels.
LogLevelValues = []string{
zerolog.LevelTraceValue,
zerolog.LevelDebugValue,
zerolog.LevelInfoValue,
zerolog.LevelWarnValue,
zerolog.LevelErrorValue,
zerolog.LevelFatalValue,
zerolog.LevelPanicValue,
}
)

// SetupGlobalLogger configures the global zerolog.Logger.
func SetupGlobalLogger(logFormat string, logLevelValue string) error {
zerolog.SetGlobalLevel(zerolog.InfoLevel)

switch logFormat {
case LogFormatConsole:
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stderr,
TimeFormat: time.RFC3339,
})

case LogFormatJSON:
log.Logger = log.Output(os.Stderr)

default:
log.Error().Str("format", logFormat).Msg("log: invalid format")
return fmt.Errorf("log: invalid format %q", logFormat)
}

var logLevel zerolog.Level

if err := logLevel.UnmarshalText([]byte(logLevelValue)); err != nil {
log.Error().Err(err).Msg("invalid log level")
return err
}

zerolog.SetGlobalLevel(logLevel)

return nil
}
11 changes: 0 additions & 11 deletions cmd/sparklemuffin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,11 @@
package main

import (
"os"
"time"

"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/virtualtam/sparklemuffin/cmd/sparklemuffin/command"
)

func main() {
zerolog.SetGlobalLevel(zerolog.InfoLevel)
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stderr,
TimeFormat: time.RFC3339,
})

rootCommand := command.NewRootCommand()

commands := []*cobra.Command{
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ services:
condition: service_healthy
environment:
SPARKLEMUFFIN_PUBLIC_ADDR: https://sparklemuffin.domain.tld
SPARKLEMUFFIN_LOG_FORMAT: console
SPARKLEMUFFIN_LOG_LEVEL: debug
SPARKLEMUFFIN_METRICS_LISTEN_ADDR: "0.0.0.0:8081"
ports:
Expand Down
17 changes: 14 additions & 3 deletions docs/src/users/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
SparkleMuffin logs information on the standard error (stderr) stream,
using a structured log message format (JSON or logfmt).

The log level can be specified via [configuration](./configuration.md).

TODO: add command-line flag to configure the log format (JSON or logfmt).
The log format and level can be specified via [configuration](./configuration.md).

### Example logs: program startup
#### Format: `console`
```shell
2024-01-20T17:03:52+01:00 INF configuration: no file found config_paths=["/etc","/home/dev/.config","."]
2024-01-20T17:03:53+01:00 INF database: successfully created connection pool database_addr=localhost:15432 database_driver=pgx database_name=sparklemuffin
Expand All @@ -20,6 +19,18 @@ TODO: add command-line flag to configure the log format (JSON or logfmt).
2024-01-20T17:04:44+01:00 INF handle request duration_ms=7.072792 host=localhost:8080 method=GET path=/static/www.css remote_addr=127.0.0.1:51440 request_id=localhost.local/96bRV2ceWt-000002 size=5402 status=200
```

#### Format: `json`
```json
{"level":"info","config_paths":["/etc","/home/dev/.config","."],"time":"2024-01-22T22:56:30+01:00","message":"configuration: no file found"}
{"level":"info","database_driver":"pgx","database_addr":"localhost:15432","database_name":"sparklemuffin","time":"2024-01-22T22:56:30+01:00","message":"database: successfully created connection pool"}
{"level":"info","log_level":"info","version":"devel","time":"2024-01-22T22:56:30+01:00","message":"global: setting up services"}
{"level":"info","metrics_addr":"127.0.0.1:8081","time":"2024-01-22T22:56:30+01:00","message":"metrics: listening for HTTP requests"}
{"level":"info","http_addr":"0.0.0.0:8080","time":"2024-01-22T22:56:30+01:00","message":"sparklemuffin: listening for HTTP requests"}
{"level":"info","duration_ms":69.373458,"host":"localhost:8080","method":"GET","path":"/bookmarks","remote_addr":"127.0.0.1:50857","request_id":"localhost.local/dqPgOolnvF-000001","size":26808,"status":200,"time":"2024-01-22T22:57:12+01:00","message":"handle request"}
{"level":"info","duration_ms":5.552375,"host":"localhost:8080","method":"GET","path":"/static/awesomplete.css","remote_addr":"127.0.0.1:50857","request_id":"localhost.local/dqPgOolnvF-000002","size":236,"status":200,"time":"2024-01-22T22:57:12+01:00","message":"handle request"}
{"level":"info","duration_ms":12.052417,"host":"localhost:8080","method":"GET","path":"/static/easymde.css","remote_addr":"127.0.0.1:50860","request_id":"localhost.local/dqPgOolnvF-000003","size":1000,"status":200,"time":"2024-01-22T22:57:12+01:00","message":"handle request"}
```

## Prometheus Metrics
SparkleMuffin exposes [Prometheus metrics](https://prometheus.io/docs/concepts/metric_types/),
providing useful information that can be used for monitoring and alerting.
Expand Down

0 comments on commit 6f98739

Please sign in to comment.