Skip to content

Commit

Permalink
Improved logging by saving a logfile alongside console output
Browse files Browse the repository at this point in the history
  • Loading branch information
Nigel2392 committed May 5, 2024
1 parent a346048 commit 9212675
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 21 deletions.
14 changes: 13 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,15 @@ func main() {

logger.Setup(&logger.Logger{
Level: logger.InfoLevel,
Output: os.Stdout,
Prefix: "quickgo",
WrapPrefix: quickgo.ColoredLogWrapper,
})

logger.SetOutput(
logger.OutputAll,
quickgo.Logfile(os.Stdout),
)

flagSet.StringVar(&flagger.Project.Name, "name", "", "The name of the project.")
flagSet.StringVar(&flagger.Project.DelimLeft, "delim-left", "", "The left delimiter for the project templates.")
flagSet.StringVar(&flagger.Project.DelimRight, "delim-right", "", "The right delimiter for the project templates.")
Expand Down Expand Up @@ -171,6 +175,14 @@ func main() {
return strings.Compare(a.Name, b.Name)
})

if len(commands) == 0 {
fmt.Println(quickgo.Craft(
quickgo.CMD_Yellow,
"No commands found in the project.",
))
return
}

fmt.Println(
quickgo.Craft(
quickgo.CMD_Blue,
Expand Down
1 change: 1 addition & 0 deletions quickgo/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

const (
QUICKGO_DIR = ".quickgo" // The directory for QuickGo files, resides in the executable directory.
QUICKGO_LOG_NAME = "quickgo.log" // The log file name.
QUICKGO_CONFIG_NAME = "quickgo.yaml" // Config file for QuickGo, resides in the executable directory.
PROJECT_CONFIG_NAME = "quickgo.yaml" // Config file for the project, resides in the project (working) directory.
PROJECT_ZIP_NAME = "project.zip" // The name of the project zip file.
Expand Down
15 changes: 13 additions & 2 deletions quickgo/logger/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package logger
import "io"

var _globalLogger *Logger = &Logger{
Level: InfoLevel,
Output: io.Discard,
Level: InfoLevel,
OutputDebug: io.Discard,
OutputInfo: io.Discard,
OutputWarn: io.Discard,
OutputError: io.Discard,
}

func Global() *Logger {
Expand All @@ -23,6 +26,14 @@ func Setup(l *Logger) {
_globalLogger = l
}

func SetOutput(level LogLevel, w io.Writer) {
_globalLogger.SetOutput(level, w)
}

func Output(level LogLevel) io.Writer {
return _globalLogger.Output(level)
}

func SetLevel(level LogLevel) {
_globalLogger.Level = level
}
Expand Down
88 changes: 76 additions & 12 deletions quickgo/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"time"
)

type LogLevel int8
Expand All @@ -21,6 +22,8 @@ var levelMap = map[LogLevel]string{
}

const (
OutputAll LogLevel = -1

// DebugLevel is the lowest log level.
DebugLevel LogLevel = iota

Expand All @@ -41,9 +44,10 @@ type LogWriter struct {

func (lw *LogWriter) Write(p []byte) (n int, err error) {
if lw.Level >= lw.Logger.Level {
lw.Logger.writePrefix(lw.Level, lw.Logger.Output)
n, err = lw.Logger.Output.Write(p)
lw.Logger.writeSuffix(lw.Logger.Output)
var out = lw.Logger.Output(lw.Level)
lw.Logger.writePrefix(lw.Level, out)
n, err = out.Write(p)
lw.Logger.writeSuffix(out)
}
return
}
Expand All @@ -58,24 +62,79 @@ type Logger struct {
// Suffix is the suffix for each log message.
Suffix string

// Output is the output writer.
Output io.Writer
// Outputs for the log messages.
OutputDebug io.Writer
OutputInfo io.Writer
OutputWarn io.Writer
OutputError io.Writer

// WrapPrefix determines how the prefix should be wrapped
// based on the LogLevel.
WrapPrefix func(LogLevel, string) string
}

func (l *Logger) SetOutput(level LogLevel, w io.Writer) {
switch level {
case DebugLevel:
l.OutputDebug = w
case InfoLevel:
l.OutputInfo = w
case WarnLevel:
l.OutputWarn = w
case ErrorLevel:
l.OutputError = w
case OutputAll:
l.OutputDebug = w
l.OutputInfo = w
l.OutputWarn = w
l.OutputError = w
}
}

func (l *Logger) validateOutputs() {
if l.OutputDebug == nil {
l.OutputDebug = io.Discard
}
if l.OutputInfo == nil {
l.OutputInfo = l.OutputDebug
}
if l.OutputWarn == nil {
l.OutputWarn = l.OutputInfo
}
if l.OutputError == nil {
l.OutputError = l.OutputWarn
}
}

func (l *Logger) Output(level LogLevel) io.Writer {
l.validateOutputs()
switch level {
case DebugLevel:
return l.OutputDebug
case InfoLevel:
return l.OutputInfo
case WarnLevel:
return l.OutputWarn
case ErrorLevel:
return l.OutputError
}
return nil
}

func (l *Logger) SetLevel(level LogLevel) {
l.Level = level
}

func (l *Logger) Copy() *Logger {
return &Logger{
Level: l.Level,
Output: l.Output,
Prefix: l.Prefix,
WrapPrefix: l.WrapPrefix,
Level: l.Level,
Prefix: l.Prefix,
Suffix: l.Suffix,
WrapPrefix: l.WrapPrefix,
OutputDebug: l.OutputDebug,
OutputInfo: l.OutputInfo,
OutputWarn: l.OutputWarn,
OutputError: l.OutputError,
}
}

Expand Down Expand Up @@ -174,6 +233,10 @@ func (l *Logger) writePrefix(level LogLevel, w io.Writer) {
}

_, _ = b.Write([]byte(level.String()))
_, _ = b.Write([]byte(" / "))

var t = time.Now().Format("2006-01-02 15:04:05")
_, _ = b.Write([]byte(t))
_, _ = b.Write([]byte("]: "))

var prefix = b.String()
Expand All @@ -192,7 +255,8 @@ func (l *Logger) writeSuffix(w io.Writer) {
}

func (l *Logger) log(level LogLevel, args ...interface{}) {
if l.Output == nil {
var out = l.Output(level)
if out == nil {
return
}

Expand All @@ -206,11 +270,11 @@ func (l *Logger) log(level LogLevel, args ...interface{}) {
message = l.WrapPrefix(level, message)
}

_, _ = l.Output.Write(
_, _ = out.Write(
[]byte(message),
)

_, _ = l.Output.Write([]byte("\n"))
_, _ = out.Write([]byte("\n"))
}

func (l *Logger) logf(level LogLevel, format string, args ...interface{}) {
Expand Down
21 changes: 15 additions & 6 deletions quickgo/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ var (

func init() {
logger.Setup(&logger.Logger{
Level: logger.WarnLevel,
Output: outputBuffer,
Level: logger.WarnLevel,
OutputDebug: outputBuffer,
OutputInfo: outputBuffer,
OutputWarn: outputBuffer,
OutputError: outputBuffer,
})
}

Expand Down Expand Up @@ -62,8 +65,11 @@ func Difference(a, b string) (string, string, string) {
func TestLogWriter(t *testing.T) {
for _, test := range LoggerTestCases {
var log = &logger.Logger{
Level: test.LoggerLevel,
Output: &test.b,
Level: test.LoggerLevel,
OutputDebug: &test.b,
OutputInfo: &test.b,
OutputWarn: &test.b,
OutputError: &test.b,
}
var lw = log.Writer(test.Level)
test.b.Reset()
Expand All @@ -79,8 +85,11 @@ func TestLogWriter(t *testing.T) {
func TestLogger(t *testing.T) {
for _, test := range LoggerTestCases {
var log = &logger.Logger{
Level: test.LoggerLevel,
Output: &test.b,
Level: test.LoggerLevel,
OutputDebug: &test.b,
OutputInfo: &test.b,
OutputWarn: &test.b,
OutputError: &test.b,
}
test.b.Reset()

Expand Down
78 changes: 78 additions & 0 deletions quickgo/quickgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package quickgo
import (
"archive/zip"
"bytes"
"fmt"
"io"
"io/fs"
"maps"
"os"
"os/signal"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"

Expand All @@ -23,13 +26,70 @@ var (
cliApplication *App
)

type ansiStrippedWriter struct {
io.Writer
}

const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"

var re = regexp.MustCompile(ansi)

func StripANSI(str string) string {
return re.ReplaceAllString(str, "")
}

func (w *ansiStrippedWriter) Write(p []byte) (n int, err error) {
return w.Writer.Write([]byte(StripANSI(string(p))))
}

// Output the log file to the given writer.
// If the writer is nil, the log file will be written to the default location.
// This function may panic if the log file cannot be opened.
func Logfile(output io.Writer) io.Writer {
var (
logPath = GetQuickGoPath(config.QUICKGO_LOG_NAME)
err error
file *os.File
writer io.Writer
)

file, err = os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
panic(err)
}

if output != nil {
writer = io.MultiWriter(output, &ansiStrippedWriter{file})
} else {
writer = &ansiStrippedWriter{file}
}

go func() {
var c = make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c

logger.Infof("Closing log file %s", logPath)
err = file.Close()
if err != nil {
fmt.Printf("Failed to close log file %s\n", logPath)
os.Exit(1)
} else {
os.Exit(0)
}
}()

return writer
}

type (
App struct {
Config *config.QuickGo `yaml:"config"` // The configuration for QuickGo.
ProjectConfig *config.Project `yaml:"projectConfig"` // The configuration for the project.
Patterns []string `yaml:"patterns"` // The patterns for the templates.
AppFS fs.FS `yaml:"-"` // The file system for the app, resides in the userprofile home directory.
ProjectFS fs.FS `yaml:"-"` // The file system for the project, resides in the project (working) directory.
logfile io.Writer `yaml:"-"` // The log file.
}
)

Expand Down Expand Up @@ -110,6 +170,19 @@ func LoadApp() (*App, error) {
return app, nil
}

func (a *App) Logger(level logger.LogLevel, output io.Writer) *logger.Logger {
var lg = &logger.Logger{
Level: level,
}

lg.SetOutput(
logger.OutputAll,
Logfile(output),
)

return lg
}

// Load the project configuration from the current working directory.
func (a *App) LoadProjectConfig(directory string) (err error) {

Expand Down Expand Up @@ -249,6 +322,11 @@ func (a *App) WriteProject(proj *config.Project, directory string, raw bool) err

var path = filepath.Join(projectDir, b.String())

if a.ProjectConfig.IsExcluded(fl) {
logger.Debugf("Excluded %s", fl.GetPath())
return false, nil
}

switch f := fl.(type) {
case *quickfs.FSFile:
// Copy the file content to the new file.
Expand Down

0 comments on commit 9212675

Please sign in to comment.