diff --git a/LICENSE b/LICENSE index 7b94222..1ce59b3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2019-2022 by Tai Groot +Copyright (C) 2019-2025 by Tai Groot Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. diff --git a/README.md b/README.md index 3442643..ba3ef48 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,69 @@ -log-socket -========== -`log-socket` is a drop-in replacement for Go's `log` package that allows for streaming of logs via WebSockets. +# Log Socket + +A real-time log viewer with WebSocket support, written in Go. This tool provides a web-based interface for viewing and filtering logs in real-time. + +## Features + +- Real-time log streaming via WebSocket +- Web-based log viewer with filtering capabilities +- Support for multiple log levels (TRACE, DEBUG, INFO, WARN, ERROR, PANIC, FATAL) +- Color-coded log levels for better visibility +- Auto-scrolling with toggle option +- Log download functionality +- Log clearing capability +- File source tracking for each log entry ## Installation -To install the library: -`go get github.com/taigrr/log-socket` -## Running -To run a demo of this library: -`go run main.go` +```bash +go install github.com/taigrr/log-socket@latest +``` -This demo will do a sample of every log type and push results to `0.0.0.0:8080`. Once running, you can open a browser and navigate to -`0.0.0.0:8080` to see an example implementation of how logs are streamed. +## Example Preview +1. Start the server: + + ```bash + log-socket + ``` + + By default, the server runs on `0.0.0.0:8080`. You can specify a different address using the `-addr` flag: + + ```bash + log-socket -addr localhost:8080 + ``` + +2. Open your browser and navigate to `http://localhost:8080` + + ![Log Socket Web Interface](browser/screenshot.png) + +## Logging Interface + +The package provides a comprehensive logging interface with the following methods: + +- `Trace/Tracef/Traceln`: For trace-level logging +- `Debug/Debugf/Debugln`: For debug-level logging +- `Info/Infof/Infoln`: For info-level logging +- `Notice/Noticef/Noticeln`: For notice-level logging +- `Warn/Warnf/Warnln`: For warning-level logging +- `Error/Errorf/Errorln`: For error-level logging +- `Panic/Panicf/Panicln`: For panic-level logging +- `Fatal/Fatalf/Fatalln`: For fatal-level logging + +## Web Interface Features + +- **Filtering**: Type in the search box to filter logs +- **Auto-scroll**: Toggle auto-scrolling with the checkbox +- **Download**: Save all logs as a JSON file +- **Clear**: Remove all logs from the viewer +- **Color Coding**: Different log levels are color-coded for easy identification + +## Dependencies + +- [gorilla/websocket](https://github.com/gorilla/websocket) for WebSocket support + +## Notes + +The web interface is not meant to be used as-is. +It functions perfectly well for some scenarios, but it is broken out into a different package intentionally, such that users can add their own as they see fit. +It's mostly here to provide an example of how to consume the websocket data and display it. diff --git a/browser/browser.go b/browser/browser.go index 27247dd..c6a02c4 100644 --- a/browser/browser.go +++ b/browser/browser.go @@ -7,6 +7,9 @@ import ( "strings" ) +//go:embed viewer.html +var webpage string + func LogSocketViewHandler(w http.ResponseWriter, r *http.Request) { wsResource := r.Host + r.URL.Path if r.TLS != nil { @@ -18,7 +21,4 @@ func LogSocketViewHandler(w http.ResponseWriter, r *http.Request) { homeTemplate.Execute(w, wsResource) } -//go:embed viewer.html -var webpage string - var homeTemplate = template.Must(template.New("").Parse(webpage)) diff --git a/browser/screenshot.png b/browser/screenshot.png new file mode 100644 index 0000000..debde75 Binary files /dev/null and b/browser/screenshot.png differ diff --git a/go.mod b/go.mod index 4da659a..d271708 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/taigrr/log-socket go 1.21 -require github.com/gorilla/websocket v1.5.0 +require github.com/gorilla/websocket v1.5.3 diff --git a/go.sum b/go.sum index e5a03d4..25a9fc4 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,2 @@ -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/log/log.go b/log/log.go index 71b5092..daaa339 100644 --- a/log/log.go +++ b/log/log.go @@ -116,7 +116,7 @@ func (c *Client) Get() Entry { } // Trace prints out logs on trace level -func Trace(args ...interface{}) { +func Trace(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -129,7 +129,7 @@ func Trace(args ...interface{}) { } // Formatted print for Trace -func Tracef(format string, args ...interface{}) { +func Tracef(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -142,7 +142,7 @@ func Tracef(format string, args ...interface{}) { } // Trace prints out logs on trace level with newline -func Traceln(args ...interface{}) { +func Traceln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -155,7 +155,7 @@ func Traceln(args ...interface{}) { } // Debug prints out logs on debug level -func Debug(args ...interface{}) { +func Debug(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -168,7 +168,7 @@ func Debug(args ...interface{}) { } // Formatted print for Debug -func Debugf(format string, args ...interface{}) { +func Debugf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -181,7 +181,7 @@ func Debugf(format string, args ...interface{}) { } // Debug prints out logs on debug level with a newline -func Debugln(args ...interface{}) { +func Debugln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -194,7 +194,7 @@ func Debugln(args ...interface{}) { } // Info prints out logs on info level -func Info(args ...interface{}) { +func Info(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -207,7 +207,7 @@ func Info(args ...interface{}) { } // Formatted print for Info -func Infof(format string, args ...interface{}) { +func Infof(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -220,7 +220,7 @@ func Infof(format string, args ...interface{}) { } // Info prints out logs on info level with a newline -func Infoln(args ...interface{}) { +func Infoln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -233,7 +233,7 @@ func Infoln(args ...interface{}) { } // Info prints out logs on info level -func Notice(args ...interface{}) { +func Notice(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -246,7 +246,7 @@ func Notice(args ...interface{}) { } // Formatted print for Info -func Noticef(format string, args ...interface{}) { +func Noticef(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -259,7 +259,7 @@ func Noticef(format string, args ...interface{}) { } // Info prints out logs on info level with a newline -func Noticeln(args ...interface{}) { +func Noticeln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -272,7 +272,7 @@ func Noticeln(args ...interface{}) { } // Warn prints out logs on warn level -func Warn(args ...interface{}) { +func Warn(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -285,7 +285,7 @@ func Warn(args ...interface{}) { } // Formatted print for Warn -func Warnf(format string, args ...interface{}) { +func Warnf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -298,7 +298,7 @@ func Warnf(format string, args ...interface{}) { } // Newline print for Warn -func Warnln(args ...interface{}) { +func Warnln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -311,7 +311,7 @@ func Warnln(args ...interface{}) { } // Error prints out logs on error level -func Error(args ...interface{}) { +func Error(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -324,7 +324,7 @@ func Error(args ...interface{}) { } // Formatted print for error -func Errorf(format string, args ...interface{}) { +func Errorf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -337,7 +337,7 @@ func Errorf(format string, args ...interface{}) { } // Error prints out logs on error level with a newline -func Errorln(args ...interface{}) { +func Errorln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -350,7 +350,7 @@ func Errorln(args ...interface{}) { } // Panic prints out logs on panic level -func Panic(args ...interface{}) { +func Panic(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -373,7 +373,7 @@ func Panic(args ...interface{}) { } // Formatted print for panic -func Panicf(format string, args ...interface{}) { +func Panicf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -395,7 +395,7 @@ func Panicf(format string, args ...interface{}) { panic(errors.New(output)) } -func Panicln(args ...interface{}) { +func Panicln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -418,7 +418,7 @@ func Panicln(args ...interface{}) { } // Fatal prints out logs on fatal level -func Fatal(args ...interface{}) { +func Fatal(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -433,7 +433,7 @@ func Fatal(args ...interface{}) { } // Formatted print for fatal -func Fatalf(format string, args ...interface{}) { +func Fatalf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -447,7 +447,7 @@ func Fatalf(format string, args ...interface{}) { os.Exit(1) } -func Fatalln(args ...interface{}) { +func Fatalln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -461,15 +461,15 @@ func Fatalln(args ...interface{}) { os.Exit(1) } -func Print(args ...interface{}) { +func Print(args ...any) { Info(args...) } -func Printf(format string, args ...interface{}) { +func Printf(format string, args ...any) { Infof(format, args...) } -func Println(args ...interface{}) { +func Println(args ...any) { Infoln(args...) } diff --git a/log/logger.go b/log/logger.go index 3ad26a0..28096c2 100644 --- a/log/logger.go +++ b/log/logger.go @@ -16,7 +16,7 @@ func (l *Logger) SetInfoDepth(depth int) { } // Trace prints out logs on trace level -func (l Logger) Trace(args ...interface{}) { +func (l Logger) Trace(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -29,7 +29,7 @@ func (l Logger) Trace(args ...interface{}) { } // Formatted print for Trace -func (l Logger) Tracef(format string, args ...interface{}) { +func (l Logger) Tracef(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -42,7 +42,7 @@ func (l Logger) Tracef(format string, args ...interface{}) { } // Trace prints out logs on trace level with newline -func (l Logger) Traceln(args ...interface{}) { +func (l Logger) Traceln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -55,7 +55,7 @@ func (l Logger) Traceln(args ...interface{}) { } // Debug prints out logs on debug level -func (l Logger) Debug(args ...interface{}) { +func (l Logger) Debug(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -68,7 +68,7 @@ func (l Logger) Debug(args ...interface{}) { } // Formatted print for Debug -func (l Logger) Debugf(format string, args ...interface{}) { +func (l Logger) Debugf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -81,7 +81,7 @@ func (l Logger) Debugf(format string, args ...interface{}) { } // Info prints out logs on info level -func (l Logger) Info(args ...interface{}) { +func (l Logger) Info(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -94,7 +94,7 @@ func (l Logger) Info(args ...interface{}) { } // Formatted print for Info -func (l Logger) Infof(format string, args ...interface{}) { +func (l Logger) Infof(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -107,7 +107,7 @@ func (l Logger) Infof(format string, args ...interface{}) { } // Info prints out logs on info level with newline -func (l Logger) Infoln(args ...interface{}) { +func (l Logger) Infoln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -120,7 +120,7 @@ func (l Logger) Infoln(args ...interface{}) { } // Notice prints out logs on notice level -func (l Logger) Notice(args ...interface{}) { +func (l Logger) Notice(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -133,7 +133,7 @@ func (l Logger) Notice(args ...interface{}) { } // Formatted print for Notice -func (l Logger) Noticef(format string, args ...interface{}) { +func (l Logger) Noticef(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -146,7 +146,7 @@ func (l Logger) Noticef(format string, args ...interface{}) { } // Notice prints out logs on notice level with newline -func (l Logger) Noticeln(args ...interface{}) { +func (l Logger) Noticeln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -159,7 +159,7 @@ func (l Logger) Noticeln(args ...interface{}) { } // Warn prints out logs on warn level -func (l Logger) Warn(args ...interface{}) { +func (l Logger) Warn(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -172,7 +172,7 @@ func (l Logger) Warn(args ...interface{}) { } // Formatted print for Warn -func (l Logger) Warnf(format string, args ...interface{}) { +func (l Logger) Warnf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -185,7 +185,7 @@ func (l Logger) Warnf(format string, args ...interface{}) { } // Warn prints out logs on warn level with a newline -func (l Logger) Warnln(args ...interface{}) { +func (l Logger) Warnln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -198,7 +198,7 @@ func (l Logger) Warnln(args ...interface{}) { } // Error prints out logs on error level -func (l Logger) Error(args ...interface{}) { +func (l Logger) Error(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -211,7 +211,7 @@ func (l Logger) Error(args ...interface{}) { } // Formatted print for error -func (l Logger) Errorf(format string, args ...interface{}) { +func (l Logger) Errorf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -224,7 +224,7 @@ func (l Logger) Errorf(format string, args ...interface{}) { } // Error prints out logs on error level with a new line -func (l Logger) Errorln(args ...interface{}) { +func (l Logger) Errorln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -237,7 +237,7 @@ func (l Logger) Errorln(args ...interface{}) { } // Panic prints out logs on panic level -func (l Logger) Panic(args ...interface{}) { +func (l Logger) Panic(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -260,7 +260,7 @@ func (l Logger) Panic(args ...interface{}) { } // Formatted print for panic -func (l Logger) Panicf(format string, args ...interface{}) { +func (l Logger) Panicf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -283,7 +283,7 @@ func (l Logger) Panicf(format string, args ...interface{}) { } // Panic prints out logs on panic level with a newline -func (l Logger) Panicln(args ...interface{}) { +func (l Logger) Panicln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -306,7 +306,7 @@ func (l Logger) Panicln(args ...interface{}) { } // Fatal prints out logs on fatal level -func (l Logger) Fatal(args ...interface{}) { +func (l Logger) Fatal(args ...any) { output := fmt.Sprint(args...) e := Entry{ Timestamp: time.Now(), @@ -321,7 +321,7 @@ func (l Logger) Fatal(args ...interface{}) { } // Formatted print for fatal -func (l Logger) Fatalf(format string, args ...interface{}) { +func (l Logger) Fatalf(format string, args ...any) { output := fmt.Sprintf(format, args...) e := Entry{ Timestamp: time.Now(), @@ -336,7 +336,7 @@ func (l Logger) Fatalf(format string, args ...interface{}) { } // Fatal prints fatal level with a new line -func (l Logger) Fatalln(args ...interface{}) { +func (l Logger) Fatalln(args ...any) { output := fmt.Sprintln(args...) e := Entry{ Timestamp: time.Now(), @@ -351,16 +351,16 @@ func (l Logger) Fatalln(args ...interface{}) { } // Handles print to info -func (l Logger) Print(args ...interface{}) { +func (l Logger) Print(args ...any) { l.Info(args...) } // Handles formatted print to info -func (l Logger) Printf(format string, args ...interface{}) { +func (l Logger) Printf(format string, args ...any) { l.Infof(format, args...) } // Handles print to info with new line -func (l Logger) Println(args ...interface{}) { +func (l Logger) Println(args ...any) { l.Infoln(args...) } diff --git a/log/types.go b/log/types.go index fed2ff1..460072c 100644 --- a/log/types.go +++ b/log/types.go @@ -2,9 +2,6 @@ package log import "time" -type LogWriter chan Entry -type Level int - const ( LTrace Level = iota LDebug @@ -16,20 +13,23 @@ const ( LFatal ) -type Client struct { - LogLevel Level `json:"level"` - writer LogWriter - initialized bool -} +type ( + LogWriter chan Entry + Level int -type Entry struct { - Timestamp time.Time `json:"timestamp"` - Output string `json:"output"` - File string `json:"file"` - Level string `json:"level"` - level Level -} - -type Logger struct { - FileInfoDepth int -} + Client struct { + LogLevel Level `json:"level"` + writer LogWriter + initialized bool + } + Entry struct { + Timestamp time.Time `json:"timestamp"` + Output string `json:"output"` + File string `json:"file"` + Level string `json:"level"` + level Level + } + Logger struct { + FileInfoDepth int + } +) diff --git a/ws/server.go b/ws/server.go index 6cfe5ec..6989aa7 100644 --- a/ws/server.go +++ b/ws/server.go @@ -8,8 +8,6 @@ import ( logger "github.com/taigrr/log-socket/log" ) -// var addr = flag.String("addr", "localhost:8080", "http service address") - var upgrader = websocket.Upgrader{} // use default options func LogSocketHandler(w http.ResponseWriter, r *http.Request) {