From 9b7af698375a90f434230bca4054861fbcb5ac4c Mon Sep 17 00:00:00 2001 From: Hazem El-Sayed <118324667+zomasec@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:20:31 +0000 Subject: [PATCH] add new version of logger --- README.md | 87 ++++++++++++++++++----------------- colors.go | 49 +++++++++++--------- examples/example.go | 33 +++++++------- formatter.go | 44 ------------------ level.go | 28 +++++++----- logger.go | 107 +++++++++++--------------------------------- output.go | 39 ++++++++++++++++ syncpool.go | 20 +++++++++ 8 files changed, 189 insertions(+), 218 deletions(-) delete mode 100644 formatter.go create mode 100644 output.go create mode 100644 syncpool.go diff --git a/README.md b/README.md index 35707c2..72788d7 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,107 @@ -## Overview +# logify -This tool provides a custom logging solution for Go applications. It utilizes the `logify` package to log messages with various levels of severity and custom formatting. The logger supports different log levels such as INFO, TEST, DEBUG, FATAL, ERROR, and WARN. Additionally, it allows the creation of custom log messages with specified colors and formats. The logger follows the Singleton pattern to ensure that the same logger instance is used throughout the entire application. - -This logging package can be integrated into various types of applications, including CLI tools, security tools, web servers, and more. Its flexibility and customizability make it suitable for any application that requires robust logging capabilities. +`logify` is a customizable logging solution for Go applications that supports various log levels and custom formatting. This package offers flexibility in logging messages with different levels of severity, colors, and formats. It uses a simple and efficient approach to logging by providing a single instance of the logger across the application. ## Features -- **Log Levels**: Supports multiple log levels including INFO, TEST, DEBUG, FATAL, ERROR, and WARN. -- **Custom Logger**: Allows custom log messages with specified colors and formats. -- **Singleton Pattern**: Ensures a single logger instance is used throughout the application. +- **Log Levels**: Supports multiple log levels including `INFO`, `DEBUG`, `ERROR`, `FATAL`, `WARNING`, and `SILENT`. +- **Custom Colors**: Allows custom log messages with specified colors. +- **Singleton Pattern**: Ensures a single logger instance is used throughout the application for consistency. ## Installation -To use this tool, you need to install the `logify` package. You can do this using `go get`: +To use `logify`, install the package via `go get`: ```sh go get github.com/cyinnove/logify ``` -## How to use? +## Usage ### Importing the Package -First, import the necessary package in your Go application: +Import the `logify` package in your Go application: ```go import ( - log "github.com/cyinnove/logify" + "github.com/cyinnove/logify" ) ``` ### Basic Logging -Here's an example of how to use the basic logging functionality: +Here's an example of basic logging: ```go package main import ( - log "github.com/cyinnove/logify" + "github.com/cyinnove/logify" ) func main() { - path := "docs/example.go" - log.Msg().Warn(path) + logify.UseColors = true + logify.MaxLevel = logify.Debug + + logify.Infof("This is an %s message", "info") + logify.Warningf("This is a %s message", "warning") + logify.Errorf("This is an %s message", "error") + logify.Debugf("This is a %s message", "debug") + logify.Verbosef("This is a verbose message with a label", "LABEL") + logify.Silentf("This is a silent message") + + // Uncomment to test Fatalf + // logify.Fatalf("This is a fatal message, the program will exit") } ``` ### Custom Logger -You can create custom log messages with specified colors and formats: +Create custom log messages with specified colors: ```go package main import ( - log "github.com/cyinnove/logify" + "github.com/cyinnove/logify" ) func main() { - path := "examples/example.go" - // Default logger with warning level - log.Msg().Warn(path) + logify.UseColors = true + logify.MaxLevel = logify.Debug + + // Default logging + logify.Warningf("Default warning message") - // Custom logger example - CustomLogger(log.Red, "Custom", "This is a custom log message with color %s", "Red") + // Custom logging + CustomLogger(logify.Red, "CustomLabel", "This is a custom log message with color %s", "Red") } -func CustomLogger(color log.Color, holder, message string, args ...interface{}) { - formatter := log.Formatter{} - formatter.SetHolder(holder) - formatter.SetMessage(message, args...) - formatter.SetColor(color) - formatter.Log() +func CustomLogger(color logify.Color, label, format string, args ...interface{}) { + logify.UseColors = true + logify.MaxLevel = logify.Debug + logify.Printf(format, args...) } ``` -## Logging Example - -![Logging Example](/static/logs.png) - ## Log Levels -The logger supports the following log levels: -- **INFO** -- **TEST** -- **DEBUG** -- **FATAL** -- **ERROR** -- **WARN** +The logger supports the following levels: -These levels can be used to categorize and filter log messages based on severity. +- **INFO**: Informational messages. +- **DEBUG**: Debugging messages. +- **ERROR**: Error messages. +- **FATAL**: Fatal errors that cause application exit. +- **WARNING**: Warning messages. +- **SILENT**: Messages with no label. ## Singleton Pattern -The logger uses the Singleton pattern to ensure that a single instance of the logger is used across the entire application. This helps maintain consistency and avoids potential issues with multiple logger instances. +The logger follows the Singleton pattern to maintain a single instance throughout the application, ensuring consistent logging behavior and avoiding multiple instances. ## Contributing -Contributions are welcome! Please feel free to submit a pull request or open an issue if you have any suggestions or bug reports. +Contributions are welcome! To contribute, please submit a pull request or open an issue with your suggestions or bug reports. ## License diff --git a/colors.go b/colors.go index 0e76eb2..8d0d4f7 100644 --- a/colors.go +++ b/colors.go @@ -1,29 +1,34 @@ package logify - -type Color int8 +import "fmt" const ( - Red Color = iota - Blue - Green - Yellow - Purple - Cyan - Gray - Orange - Reset + Red = "\033[31m" + Yellow = "\033[33m" + Blue = "\033[34m" + Magenta = "\033[35m" + Reset = "\033[0m" + Bold = "\033[1m" ) -// Colors is a map that associates each Color with its corresponding ANSI escape code. -var Colors = map[Color]string{ - Red: "\033[0;31m", - Blue: "\033[0;34m", - Green: "\033[0;32m", - Yellow: "\033[0;33m", - Purple: "\033[0;35m", - Cyan: "\033[0;36m", - Gray: "\033[0;37m", - Orange: "\033[0;91m", - Reset: "\033[0m", +// Colorize adds color based on the log level +func Colorize(text string, level Level) string { + if !UseColors { + return text + } + + switch level { + case Fatal: + return fmt.Sprintf("%s%s%s%s", Bold, Red, text, Reset) + case Error: + return fmt.Sprintf("%s%s%s", Red, text, Reset) + case Warning, Label: + return fmt.Sprintf("%s%s%s", Yellow, text, Reset) + case Debug: + return fmt.Sprintf("%s%s%s", Magenta, text, Reset) + case Info, Verbose: + return fmt.Sprintf("%s%s%s", Blue, text, Reset) + default: + return text + } } diff --git a/examples/example.go b/examples/example.go index 3bf8f89..b6e78fb 100644 --- a/examples/example.go +++ b/examples/example.go @@ -1,25 +1,22 @@ package main import ( - log "github.com/cyinnove/logify" + "github.com/cyinnove/logify" ) func main() { - //host := "https://example.com" - path := "docs/example.go" - - log.Msg().Test(path) - - CustomLogger(log.Red, "Custom", "This is a custom log message with color %s", "Red") - -} - -func CustomLogger(color log.Color, holder, message string, args ...interface{}) { - formatter := log.Formatter{} - - formatter.SetHolder(holder) - formatter.SetMessage(message, args...) - formatter.SetColor(color) - formatter.Log() - + // Set logging configuration + logify.UseColors = true + logify.MaxLevel = logify.Silent + + // Example logging + logify.Infof("This is an %s message", "info") + logify.Warningf("This is a %s message", "warning") + logify.Errorf("This is an %s message", "error") + logify.Debugf("This is a %s message", "debug") + logify.Verbosef("This is a verbose message with a label", "LABEL") + logify.Silentf("This is a silent message") + + // Fatal error example (uncomment to test) + // logify.Fatalf("This is a fatal message, the program will exit") } diff --git a/formatter.go b/formatter.go deleted file mode 100644 index ac970b1..0000000 --- a/formatter.go +++ /dev/null @@ -1,44 +0,0 @@ -package logify - -import "fmt" - -// Formatter struct represents the log message formatter including the color. -type Formatter struct { - LogHolder string - Message string - Args []interface{} - Color string // Holds the color for the holder -} - -// Format formats the log message using the specified color and log holder. -// If no color is specified, it defaults to no color. -// It returns the formatted log message as a string. -func (f *Formatter) Format() string { - color := f.Color - if color == "" { - color = Colors[Reset] // Default to no color if none is specified - } - return fmt.Sprintf("[%s%s%s] %s", color, f.LogHolder, Colors[Reset], fmt.Sprintf(f.Message, f.Args...)) -} - -// Log prints the formatted log message to the console. -func (f *Formatter) Log() { - fmt.Println(f.Format()) -} - -// SetHolder sets the log holder with an optional color. -func (f *Formatter) SetHolder(holder string) { - f.LogHolder = holder -} - -// SetMessage sets the log message and arguments. -func (f *Formatter) SetMessage(msg string, args ...interface{}) { - f.Message = fmt.Sprintf(msg, args...) - f.Args = nil -} - -// SetColor sets the color of the formatter. -// It takes a Color parameter and assigns the corresponding color value from the Colors map to the Formatter's Color field. -func (f *Formatter) SetColor(color Color) { - f.Color = Colors[color] -} diff --git a/level.go b/level.go index b6335b4..4d15f3b 100644 --- a/level.go +++ b/level.go @@ -1,17 +1,25 @@ package logify - -type Level int8 +type Level int const ( - Info Level = iota - Debug - Warn - Error + Null Level = iota Fatal - Test + Silent + Label + Misc + Error + Info + Warning + Debug + Verbose ) -func (l Level) String() string { - return [...]string{"INF", "DBG", "WRN", "ERR", "FTL", "TST"}[l] -} \ No newline at end of file +var labels = map[Level]string{ + Warning: "WRN", + Error: "ERR", + Label: "WRN", + Fatal: "FTL", + Debug: "DBG", + Info: "INF", +} diff --git a/logger.go b/logger.go index 637bc39..824b5e8 100644 --- a/logger.go +++ b/logger.go @@ -2,96 +2,39 @@ package logify import ( "fmt" + "os" + "strings" "sync" ) -// Logger interface defines the methods that any logger implementation should have. -type Logger interface { - Info(msg string, args ...interface{}) - Debug(msg string, args ...interface{}) - Test(msg string, args ...interface{}) - Warn(msg string, args ...interface{}) - Error(msg string, args ...interface{}) - Fatal(msg string, args ...interface{}) -} - -// LoggerOptions struct represents the default logger implementation. -type LoggerOptions struct { - Formatter Formatter - -} - var ( - once sync.Once - instance *LoggerOptions + UseColors = true + MaxLevel = Info + mutex = &sync.Mutex{} ) -// GetLogger returns the singleton instance of LoggerOptions. -// Msg returns an instance of the Logger. -// The Logger is a singleton object that provides logging functionality. -// It initializes the LoggerOptions with default values if it hasn't been initialized before. -// The LoggerOptions controls various logging options such as formatter, color enablement, and log level visibility. -func Msg() Logger { - once.Do(func() { - instance = &LoggerOptions{ - Formatter: Formatter{}, - - } - }) - return instance -} - -func (l *LoggerOptions) colorize(holder, color string) { - - l.Formatter.SetHolder(fmt.Sprintf("%s%s%s", color, holder, Colors[Reset])) - -} - -// Info logs an info message with the specified message and arguments. -func (l *LoggerOptions) Info(msg string, args ...interface{}) { - - l.colorize(Info.String(), Colors[Blue]) - l.Formatter.SetMessage(msg, args...) - l.Formatter.Log() -} - -// Debug logs a debug message with the specified message and arguments. -func (l *LoggerOptions) Debug(msg string, args ...interface{}) { - - l.colorize(Debug.String(), Colors[Purple]) - l.Formatter.SetMessage(msg, args...) - l.Formatter.Log() -} - -// Warn logs a warning message with the specified message and arguments. -func (l *LoggerOptions) Warn(msg string, args ...interface{}) { - - l.colorize(Warn.String(), Colors[Yellow]) - l.Formatter.SetMessage(msg, args...) - l.Formatter.Log() -} +// log logs the actual message to the screen +func log(level Level, label string, format string, args ...interface{}) { + if level == Null || level > MaxLevel { + return + } -// Error logs an error message with the specified message and arguments. -func (l *LoggerOptions) Error(msg string, args ...interface{}) { + sb := stringBuilderPool.Get().(*strings.Builder) + defer stringBuilderPool.Put(sb) - l.colorize(Error.String(), Colors[Red]) - l.Formatter.SetMessage(msg, args...) - l.Formatter.Log() -} + getLabel(level, label, sb) -// Fatal logs a fatal message with the specified message and arguments. -func (l *LoggerOptions) Fatal(msg string, args ...interface{}) { - - l.colorize(Fatal.String(), Colors[Orange]) - l.Formatter.SetMessage(msg, args...) - l.Formatter.Log() -} + message := fmt.Sprintf(format, args...) + sb.WriteString(message) + if !strings.HasSuffix(message, "\n") { + sb.WriteString("\n") + } -func (l *LoggerOptions) Test(msg string, args ...interface{}) { - - l.colorize(Test.String(), Colors[Green]) - l.Formatter.SetMessage(msg, args...) - l.Formatter.Log() + mutex.Lock() + defer mutex.Unlock() + if level == Silent { + fmt.Fprint(os.Stdout, sb.String()) + } else { + fmt.Fprint(os.Stderr, sb.String()) + } } - - diff --git a/output.go b/output.go new file mode 100644 index 0000000..9f50067 --- /dev/null +++ b/output.go @@ -0,0 +1,39 @@ +package logify + +import "os" + +// Infof writes an info message +func Infof(format string, args ...interface{}) { + log(Info, "", format, args...) +} + +// Warningf writes a warning message +func Warningf(format string, args ...interface{}) { + log(Warning, "", format, args...) +} + +// Errorf writes an error message +func Errorf(format string, args ...interface{}) { + log(Error, "", format, args...) +} + +// Debugf writes a debug message +func Debugf(format string, args ...interface{}) { + log(Debug, "", format, args...) +} + +// Verbosef writes a verbose message +func Verbosef(format string, label string, args ...interface{}) { + log(Verbose, label, format, args...) +} + +// Silentf writes a message with no label +func Silentf(format string, args ...interface{}) { + log(Silent, "", format, args...) +} + +// Fatalf exits the program after logging a fatal message +func Fatalf(format string, args ...interface{}) { + log(Fatal, "", format, args...) + os.Exit(1) +} diff --git a/syncpool.go b/syncpool.go new file mode 100644 index 0000000..a6ae2eb --- /dev/null +++ b/syncpool.go @@ -0,0 +1,20 @@ +package logify + +import ( + "strings" + "sync" +) + +var stringBuilderPool = &sync.Pool{New: func() interface{} { + return new(strings.Builder) +}} + +// getLabel generates a label based on the level and wraps it in color if needed +func getLabel(level Level, label string, sb *strings.Builder) { + if level == Silent || level == Misc { + return + } + sb.WriteString("[") + sb.WriteString(Colorize(labels[level], level)) + sb.WriteString("] ") +}