1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-09-15 18:57:04 +00:00

Implement structured logging using log/slog package

This commit is contained in:
Frédéric Guillot 2023-09-24 16:32:09 -07:00
parent 54cb8fa028
commit c0e954f19d
77 changed files with 1868 additions and 892 deletions

View file

@ -8,6 +8,181 @@ import (
"testing"
)
func TestLogFileDefaultValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogFile() != defaultLogFile {
t.Fatalf(`Unexpected log file value, got %q`, opts.LogFile())
}
}
func TestLogFileWithCustomFilename(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_FILE", "foobar.log")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogFile() != "foobar.log" {
t.Fatalf(`Unexpected log file value, got %q`, opts.LogFile())
}
}
func TestLogFileWithEmptyValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_FILE", "")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogFile() != defaultLogFile {
t.Fatalf(`Unexpected log file value, got %q`, opts.LogFile())
}
}
func TestLogLevelDefaultValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogLevel() != defaultLogLevel {
t.Fatalf(`Unexpected log level value, got %q`, opts.LogLevel())
}
}
func TestLogLevelWithCustomValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_LEVEL", "warning")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogLevel() != "warning" {
t.Fatalf(`Unexpected log level value, got %q`, opts.LogLevel())
}
}
func TestLogLevelWithInvalidValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_LEVEL", "invalid")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogLevel() != defaultLogLevel {
t.Fatalf(`Unexpected log level value, got %q`, opts.LogLevel())
}
}
func TestLogDateTimeDefaultValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogDateTime() != defaultLogDateTime {
t.Fatalf(`Unexpected log date time value, got %v`, opts.LogDateTime())
}
}
func TestLogDateTimeWithCustomValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_DATETIME", "false")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogDateTime() != false {
t.Fatalf(`Unexpected log date time value, got %v`, opts.LogDateTime())
}
}
func TestLogDateTimeWithInvalidValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_DATETIME", "invalid")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogDateTime() != defaultLogDateTime {
t.Fatalf(`Unexpected log date time value, got %v`, opts.LogDateTime())
}
}
func TestLogFormatDefaultValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogFormat() != defaultLogFormat {
t.Fatalf(`Unexpected log format value, got %q`, opts.LogFormat())
}
}
func TestLogFormatWithCustomValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_FORMAT", "json")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogFormat() != "json" {
t.Fatalf(`Unexpected log format value, got %q`, opts.LogFormat())
}
}
func TestLogFormatWithInvalidValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_FORMAT", "invalid")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.LogFormat() != defaultLogFormat {
t.Fatalf(`Unexpected log format value, got %q`, opts.LogFormat())
}
}
func TestDebugModeOn(t *testing.T) {
os.Clearenv()
os.Setenv("DEBUG", "1")
@ -18,8 +193,8 @@ func TestDebugModeOn(t *testing.T) {
t.Fatalf(`Parsing failure: %v`, err)
}
if !opts.HasDebugMode() {
t.Fatalf(`Unexpected debug mode value, got "%v"`, opts.HasDebugMode())
if opts.LogLevel() != "debug" {
t.Fatalf(`Unexpected debug mode value, got %q`, opts.LogLevel())
}
}
@ -32,8 +207,8 @@ func TestDebugModeOff(t *testing.T) {
t.Fatalf(`Parsing failure: %v`, err)
}
if opts.HasDebugMode() {
t.Fatalf(`Unexpected debug mode value, got "%v"`, opts.HasDebugMode())
if opts.LogLevel() != "info" {
t.Fatalf(`Unexpected debug mode value, got %q`, opts.LogLevel())
}
}
@ -1544,8 +1719,8 @@ Invalid text
t.Errorf(`Parsing failure: %v`, err)
}
if opts.HasDebugMode() != true {
t.Errorf(`Unexpected debug mode value, got "%v"`, opts.HasDebugMode())
if opts.LogLevel() != "debug" {
t.Errorf(`Unexpected debug mode value, got %q`, opts.LogLevel())
}
expected := ">#1234"

View file

@ -15,7 +15,10 @@ import (
const (
defaultHTTPS = false
defaultLogFile = "stderr"
defaultLogDateTime = false
defaultLogFormat = "text"
defaultLogLevel = "info"
defaultHSTS = true
defaultHTTPService = true
defaultSchedulerService = true
@ -91,11 +94,13 @@ type Option struct {
// Options contains configuration options.
type Options struct {
HTTPS bool
logFile string
logDateTime bool
logFormat string
logLevel string
hsts bool
httpService bool
schedulerService bool
debug bool
serverTimingHeader bool
baseURL string
rootURL string
@ -165,11 +170,13 @@ func NewOptions() *Options {
return &Options{
HTTPS: defaultHTTPS,
logFile: defaultLogFile,
logDateTime: defaultLogDateTime,
logFormat: defaultLogFormat,
logLevel: defaultLogLevel,
hsts: defaultHSTS,
httpService: defaultHTTPService,
schedulerService: defaultSchedulerService,
debug: defaultDebug,
serverTimingHeader: defaultTiming,
baseURL: defaultBaseURL,
rootURL: defaultRootURL,
@ -231,11 +238,30 @@ func NewOptions() *Options {
}
}
func (o *Options) LogFile() string {
return o.logFile
}
// LogDateTime returns true if the date/time should be displayed in log messages.
func (o *Options) LogDateTime() bool {
return o.logDateTime
}
// LogFormat returns the log format.
func (o *Options) LogFormat() string {
return o.logFormat
}
// LogLevel returns the log level.
func (o *Options) LogLevel() string {
return o.logLevel
}
// SetLogLevel sets the log level.
func (o *Options) SetLogLevel(level string) {
o.logLevel = level
}
// HasMaintenanceMode returns true if maintenance mode is enabled.
func (o *Options) HasMaintenanceMode() bool {
return o.maintenanceMode
@ -246,11 +272,6 @@ func (o *Options) MaintenanceMessage() string {
return o.maintenanceMessage
}
// HasDebugMode returns true if debug mode is enabled.
func (o *Options) HasDebugMode() bool {
return o.debug
}
// HasServerTimingHeader returns true if server-timing headers enabled.
func (o *Options) HasServerTimingHeader() bool {
return o.serverTimingHeader
@ -593,7 +614,6 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
"DATABASE_MAX_CONNS": o.databaseMaxConns,
"DATABASE_MIN_CONNS": o.databaseMinConns,
"DATABASE_URL": redactSecretValue(o.databaseURL, redactSecret),
"DEBUG": o.debug,
"DISABLE_HSTS": !o.hsts,
"DISABLE_HTTP_SERVICE": !o.httpService,
"DISABLE_SCHEDULER_SERVICE": !o.schedulerService,
@ -609,7 +629,10 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
"INVIDIOUS_INSTANCE": o.invidiousInstance,
"KEY_FILE": o.certKeyFile,
"LISTEN_ADDR": o.listenAddr,
"LOG_FILE": o.logFile,
"LOG_DATE_TIME": o.logDateTime,
"LOG_FORMAT": o.logFormat,
"LOG_LEVEL": o.logLevel,
"MAINTENANCE_MESSAGE": o.maintenanceMessage,
"MAINTENANCE_MODE": o.maintenanceMode,
"METRICS_ALLOWED_NETWORKS": strings.Join(o.metricsAllowedNetworks, ","),

View file

@ -10,7 +10,7 @@ import (
"errors"
"fmt"
"io"
url_parser "net/url"
"net/url"
"os"
"strconv"
"strings"
@ -72,10 +72,25 @@ func (p *Parser) parseLines(lines []string) (err error) {
value := strings.TrimSpace(fields[1])
switch key {
case "LOG_FILE":
p.opts.logFile = parseString(value, defaultLogFile)
case "LOG_DATE_TIME":
p.opts.logDateTime = parseBool(value, defaultLogDateTime)
case "LOG_LEVEL":
parsedValue := parseString(value, defaultLogLevel)
if parsedValue == "debug" || parsedValue == "info" || parsedValue == "warning" || parsedValue == "error" {
p.opts.logLevel = parsedValue
}
case "LOG_FORMAT":
parsedValue := parseString(value, defaultLogFormat)
if parsedValue == "json" || parsedValue == "text" {
p.opts.logFormat = parsedValue
}
case "DEBUG":
p.opts.debug = parseBool(value, defaultDebug)
parsedValue := parseBool(value, defaultDebug)
if parsedValue {
p.opts.logLevel = "debug"
}
case "SERVER_TIMING_HEADER":
p.opts.serverTimingHeader = parseBool(value, defaultTiming)
case "BASE_URL":
@ -247,19 +262,19 @@ func parseBaseURL(value string) (string, string, string, error) {
value = value[:len(value)-1]
}
url, err := url_parser.Parse(value)
parsedURL, err := url.Parse(value)
if err != nil {
return "", "", "", fmt.Errorf("config: invalid BASE_URL: %w", err)
}
scheme := strings.ToLower(url.Scheme)
scheme := strings.ToLower(parsedURL.Scheme)
if scheme != "https" && scheme != "http" {
return "", "", "", errors.New("config: invalid BASE_URL: scheme must be http or https")
}
basePath := url.Path
url.Path = ""
return value, url.String(), basePath, nil
basePath := parsedURL.Path
parsedURL.Path = ""
return value, parsedURL.String(), basePath, nil
}
func parseBool(value string, fallback bool) bool {