Skip to content

Commit 50d8b28

Browse files
authored
Add syslog logging support and refactor file logging (#237)
1 parent 162b57d commit 50d8b28

File tree

1 file changed

+92
-17
lines changed

1 file changed

+92
-17
lines changed

cmd/root.go

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import (
1111
"fmt"
1212
"io"
1313
"log/slog"
14+
"log/syslog"
1415
"net"
1516
"net/http"
1617
"os"
1718
"os/signal"
1819
"path/filepath"
20+
"runtime"
1921
"strings"
2022
"syscall"
2123
"time"
@@ -168,27 +170,31 @@ func initializeApplication(cmd *cobra.Command, args []string) error {
168170
os.Exit(1)
169171
}
170172
}
171-
// open log file in current directory
172-
gLogFile, err = os.OpenFile(common.AppName+".log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
173-
if err != nil {
174-
fmt.Printf("Error: failed to open log file: %v\n", err)
175-
os.Exit(1)
176-
}
177-
var logLevel slog.Leveler
178-
var logSource bool
173+
// configure logging
174+
var logOpts slog.HandlerOptions
179175
if flagDebug {
180-
logLevel = slog.LevelDebug
181-
logSource = true
176+
logOpts.Level = slog.LevelDebug
177+
logOpts.AddSource = true
182178
} else {
183-
logLevel = slog.LevelInfo
184-
logSource = false
179+
logOpts.Level = slog.LevelInfo
180+
logOpts.AddSource = false
185181
}
186-
opts := &slog.HandlerOptions{
187-
Level: logLevel,
188-
AddSource: logSource,
182+
if flagSyslog { // log to syslog
183+
handler, err := NewSyslogHandler(&logOpts)
184+
if err != nil {
185+
fmt.Printf("Error: failed to create syslog handler: %v\n", err)
186+
os.Exit(1)
187+
}
188+
slog.SetDefault(slog.New(handler))
189+
} else { // log to file
190+
// open log file in current directory
191+
gLogFile, err = os.OpenFile(common.AppName+".log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
192+
if err != nil {
193+
fmt.Printf("Error: failed to open log file: %v\n", err)
194+
os.Exit(1)
195+
}
196+
slog.SetDefault(slog.New(slog.NewTextHandler(gLogFile, &logOpts)))
189197
}
190-
logger := slog.New(slog.NewTextHandler(gLogFile, opts))
191-
slog.SetDefault(logger)
192198
slog.Info("Starting up", slog.String("app", common.AppName), slog.String("version", gVersion), slog.Int("PID", os.Getpid()), slog.String("arguments", strings.Join(os.Args, " ")))
193199
// verify requested local temp dir exists
194200
var localTempDir string
@@ -450,3 +456,72 @@ func getLatestManifest() (manifest, error) {
450456
// return latest version
451457
return latestManifest, nil
452458
}
459+
460+
// SyslogHandler is a slog.Handler that logs to syslog.
461+
type SyslogHandler struct {
462+
writer *syslog.Writer
463+
logLeveler slog.Leveler
464+
addSource bool
465+
}
466+
467+
func NewSyslogHandler(logOpts *slog.HandlerOptions) (*SyslogHandler, error) {
468+
writer, err := syslog.New(syslog.LOG_INFO|syslog.LOG_USER, filepath.Base(os.Args[0]))
469+
if err != nil {
470+
return nil, err
471+
}
472+
return &SyslogHandler{writer: writer, logLeveler: logOpts.Level, addSource: logOpts.AddSource}, nil
473+
}
474+
475+
func (h *SyslogHandler) Handle(ctx context.Context, r slog.Record) error {
476+
var msg string
477+
if r.PC != 0 && h.addSource {
478+
fs := runtime.CallersFrames([]uintptr{r.PC})
479+
f, _ := fs.Next()
480+
// get the file name with path relative to the current working directory + the last directory in the working directory
481+
filePath := f.File
482+
if strings.HasPrefix(filePath, "/") {
483+
wd, err := os.Getwd()
484+
if err == nil {
485+
filePath, err = filepath.Rel(wd, filePath)
486+
if err == nil {
487+
// last path element in working directory
488+
_, lastWd := filepath.Split(wd)
489+
filePath = filepath.Join(lastWd, filePath)
490+
} else {
491+
filePath = f.File
492+
}
493+
}
494+
}
495+
msg = fmt.Sprintf("level=%s source=%s:%d msg=\"%s\"", r.Level.String(), filePath, f.Line, r.Message)
496+
} else {
497+
msg = fmt.Sprintf("level=%s msg=\"%s\"", r.Level.String(), r.Message)
498+
}
499+
r.Attrs(func(attr slog.Attr) bool {
500+
msg += fmt.Sprintf(" %s=\"%s\"", attr.Key, attr.Value)
501+
return true
502+
})
503+
switch r.Level {
504+
case slog.LevelDebug:
505+
return h.writer.Debug(msg)
506+
case slog.LevelInfo:
507+
return h.writer.Info(msg)
508+
case slog.LevelWarn:
509+
return h.writer.Warning(msg)
510+
case slog.LevelError:
511+
return h.writer.Err(msg)
512+
default:
513+
return h.writer.Info(msg)
514+
}
515+
}
516+
517+
func (h *SyslogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
518+
return h
519+
}
520+
521+
func (h *SyslogHandler) WithGroup(name string) slog.Handler {
522+
return h
523+
}
524+
525+
func (h *SyslogHandler) Enabled(ctx context.Context, level slog.Level) bool {
526+
return level >= h.logLeveler.Level()
527+
}

0 commit comments

Comments
 (0)