@@ -5,12 +5,34 @@ import (
55 "io"
66 "os"
77 "path/filepath"
8+ "sync"
89 "time"
910
1011 "github.com/mirkobrombin/goup/internal/config"
12+ "github.com/muesli/termenv"
1113 "github.com/rs/zerolog"
1214)
1315
16+ var loggerBytePool = & byteSlicePool {
17+ pool : sync.Pool {
18+ New : func () interface {} {
19+ return make ([]byte , 8 * 1024 )
20+ },
21+ },
22+ }
23+
24+ type byteSlicePool struct {
25+ pool sync.Pool
26+ }
27+
28+ func (b * byteSlicePool ) Get () []byte {
29+ return b .pool .Get ().([]byte )
30+ }
31+
32+ func (b * byteSlicePool ) Put (buf []byte ) {
33+ b .pool .Put (buf )
34+ }
35+
1436// Fields is a map of string keys to arbitrary values, emulating logrus.Fields
1537// for compatibility with existing code.
1638type Fields map [string ]interface {}
@@ -36,6 +58,29 @@ func (l *Logger) WithFields(fields Fields) *Logger {
3658 }
3759}
3860
61+ // ColoredConsoleWriter wraps an io.Writer to print colored logs.
62+ func ColoredConsoleWriter (out io.Writer ) zerolog.ConsoleWriter {
63+ return zerolog.ConsoleWriter {
64+ Out : out ,
65+ TimeFormat : "15:04:05" ,
66+ FormatLevel : func (i interface {}) string {
67+ level := i .(string )
68+ switch level {
69+ case "info" :
70+ return termenv .String ("INFO" ).Foreground (termenv .ANSICyan ).String ()
71+ case "warn" :
72+ return termenv .String ("WARN" ).Foreground (termenv .ANSIYellow ).String ()
73+ case "error" :
74+ return termenv .String ("ERROR" ).Foreground (termenv .ANSIRed ).String ()
75+ case "debug" :
76+ return termenv .String ("DEBUG" ).Foreground (termenv .ANSIWhite ).String ()
77+ default :
78+ return level
79+ }
80+ },
81+ }
82+ }
83+
3984// Info logs a message at Info level.
4085func (l * Logger ) Info (msg string ) {
4186 l .base .Info ().Msg (msg )
@@ -56,7 +101,7 @@ func (l *Logger) Errorf(format string, args ...interface{}) {
56101 l .base .Error ().Msgf (format , args ... )
57102}
58103
59- // Debug logs a message at Debug level (not heavily used by default) .
104+ // Debug logs a message at Debug level.
60105func (l * Logger ) Debug (msg string ) {
61106 l .base .Debug ().Msg (msg )
62107}
@@ -76,7 +121,8 @@ func (l *Logger) Warnf(format string, args ...interface{}) {
76121 l .base .Warn ().Msgf (format , args ... )
77122}
78123
79- // NewLogger creates a new Logger that writes both to stdout and a site-specific file.
124+ // NewLogger creates a new Logger that writes JSON to files and colored
125+ // logs to stdout.
80126func NewLogger (identifier string , fields Fields ) (* Logger , error ) {
81127 logDir := filepath .Join (
82128 config .GetLogDir (),
@@ -95,14 +141,13 @@ func NewLogger(identifier string, fields Fields) (*Logger, error) {
95141 return nil , err
96142 }
97143
98- mw := io .MultiWriter (os .Stdout , file )
144+ multiWriter := io .MultiWriter (file , ColoredConsoleWriter ( os .Stdout ) )
99145
100- // Zerolog logger with time + multiwriter
101- base := zerolog .New (mw ).With ().Timestamp ().Logger ()
146+ base := zerolog .New (multiWriter ).With ().Timestamp ().Logger ()
102147
103148 l := & Logger {
104149 base : base ,
105- out : mw ,
150+ out : multiWriter ,
106151 }
107152
108153 if fields != nil {
@@ -112,7 +157,7 @@ func NewLogger(identifier string, fields Fields) (*Logger, error) {
112157 return l , nil
113158}
114159
115- // NewPluginLogger creates a plugin-specific log file (no stdout).
160+ // NewPluginLogger creates a plugin-specific log file (JSON format, no stdout).
116161func NewPluginLogger (siteDomain , pluginName string ) (* Logger , error ) {
117162 logDir := filepath .Join (
118163 config .GetLogDir (),
@@ -147,7 +192,9 @@ func (l *Logger) Writer() io.WriteCloser {
147192
148193 go func () {
149194 defer pr .Close ()
150- buf := make ([]byte , 1024 )
195+ buf := loggerBytePool .Get ()
196+ defer loggerBytePool .Put (buf )
197+
151198 var tmp []byte
152199
153200 for {
@@ -176,9 +223,7 @@ func (l *Logger) Writer() io.WriteCloser {
176223 }
177224 }()
178225
179- return & pipeWriteCloser {
180- pipeWriter : pw ,
181- }
226+ return & pipeWriteCloser {pipeWriter : pw }
182227}
183228
184229// pipeWriteCloser implements Write and Close delegating to a PipeWriter.
0 commit comments