@@ -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