diff --git a/color.go b/color.go new file mode 100644 index 0000000..52a68fd --- /dev/null +++ b/color.go @@ -0,0 +1,106 @@ +package log + +import "fmt" + +// ColorizeLevel assigns a specific color to each log level based on its string representation. +func ColorizeLevel(level Level) { + switch level.String() { + case "DEBUG": + PrintColor(DebugColor{}) + case "INFO": + PrintColor(InfoColor{}) + case "NOTICE": + PrintColor(NoticeColor{}) + case "WARN": + PrintColor(WarnColor{}) + case "ERROR": + PrintColor(ErrorColor{}) + case "PANIC": + PrintColor(PanicColor{}) + case "ALERT": + PrintColor(AlertColor{}) + case "FATAL": + PrintColor(FatalColor{}) + default: + PrintColor(DefaultColor{}) + } +} + +// Custom color types for each log level. +type DefaultColor struct{ value string } +type DebugColor struct{ value string } +type InfoColor struct{ value string } +type NoticeColor struct{ value string } +type WarnColor struct{ value string } +type ErrorColor struct{ value string } +type PanicColor struct{ value string } +type AlertColor struct{ value string } +type FatalColor struct{ value string } + +// Methods for each color type that returns the corresponding ANSI escape code. +// These methods implement the Color interface. + +// Set Debug Color +func (LevelColor DebugColor) getLevelColor() string { + LevelColor.value = "\033[36m" + return LevelColor.value +} + +// Set Info Color +func (LevelColor InfoColor) getLevelColor() string { + LevelColor.value = "\033[32m" + return LevelColor.value +} + +// Set Notice Color +func (LevelColor NoticeColor) getLevelColor() string { + LevelColor.value = "\033[33m" + return LevelColor.value +} + +// Set Warn Color +func (LevelColor WarnColor) getLevelColor() string { + LevelColor.value = "\033[35m" + return LevelColor.value +} + +// Set Error Color +func (LevelColor ErrorColor) getLevelColor() string { + LevelColor.value = "\033[31m" + return LevelColor.value +} + +// Set Panic Color +func (LevelColor PanicColor) getLevelColor() string { + LevelColor.value = "\033[91m" + return LevelColor.value +} + +// Set Alert Color +func (LevelColor AlertColor) getLevelColor() string { + LevelColor.value = "\033[93m" + return LevelColor.value +} + +// Set Fatal Color +func (LevelColor FatalColor) getLevelColor() string { + LevelColor.value = "\033[95m" + return LevelColor.value +} + +// Set Default Color +func (LevelColor DefaultColor) getLevelColor() string { + LevelColor.value = "\033[0m" + return LevelColor.value +} + +// Color interface defines the getLevelColor method to get the ANSI escape code. +type Color interface { + getLevelColor() string +} + +// PrintColor sets the appropriate color for the log level and prints it. +func PrintColor(C Color) { + LevelColor := C.getLevelColor() + fmt.Print(LevelColor) +} diff --git a/color_test.go b/color_test.go new file mode 100644 index 0000000..1e7922b --- /dev/null +++ b/color_test.go @@ -0,0 +1,89 @@ +package log + +import ( + "bytes" + "fmt" + "os" + "testing" +) + +// CustomWriter is a custom writer that wraps bytes.Buffer and implements io.Writer +type CustomWriter struct { + buffer *bytes.Buffer +} + +// Write writes the data to the underlying buffer +func (w *CustomWriter) Write(p []byte) (n int, err error) { + return w.buffer.Write(p) +} + +// File returns the underlying *os.File +func (w *CustomWriter) File() *os.File { + return os.Stdout +} + +func TestColorizeLevel(t *testing.T) { + buff := new(bytes.Buffer) + + // Create a custom writer that wraps the buffer + customWriter := &CustomWriter{buffer: buff} + + // Redirect stdout to the buffer + old := os.Stdout + os.Stdout = customWriter.File() + + // Test cases + testCases := []struct { + level Level + color string + expected string + expectedErr error + }{ + {DebugLevel, "\033[36m", "", nil}, // Expected ANSI escape code for DEBUG: Cyan + {InfoLevel, "\033[32m", "", nil}, // Expected ANSI escape code for INFO: Green + {NoticeLevel, "\033[33m", "", nil}, // Expected ANSI escape code for NOTICE: Yellow + {WarnLevel, "\033[35m", "", nil}, // Expected ANSI escape code for WARN: Magenta + {ErrorLevel, "\033[31m", "", nil}, // Expected ANSI escape code for ERROR: Red + {PanicLevel, "\033[91m", "", nil}, // Expected ANSI escape code for PANIC: Light Red + {AlertLevel, "\033[93m", "", nil}, // Expected ANSI escape code for ALERT: Light Yellow + {FatalLevel, "\033[95m", "", nil}, // Expected ANSI escape code for FATAL: Light Magenta + {InfoLevel, "\033[32m", "", nil}, // Invalid color (for testing error handling) + //{UNKNOWN, "\033[0m", "\033[0m", nil}, // Expected ANSI escape code for unknown level: Reset to default + } + + // Iterate over test cases + for _, tc := range testCases { + t.Run(tc.level.String(), func(t *testing.T) { + // Reset the buffer + buff.Reset() + + // Call the ColorizeLevel function + err := func() (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + }() + ColorizeLevel(tc.level) + return nil + }() + + // Check if an error occurred (for error handling testing) + if tc.expectedErr != nil { + if err == nil || err.Error() != tc.expectedErr.Error() { + t.Errorf("Expected error: %v, but got: %v", tc.expectedErr, err) + } + return + } + + // Check if the printed output matches the expected ANSI escape code + actual := buff.String() + if actual != tc.expected { + t.Errorf("Expected: %s, but got: %s", tc.expected, actual) + } + }) + } + + // Restore stdout + os.Stdout = old +} diff --git a/log.go b/log.go index 53275df..5b353fd 100644 --- a/log.go +++ b/log.go @@ -150,6 +150,9 @@ func HandleEntry(e Entry) { } e.Timestamp = time.Now() + //Adding colors for each output level + ColorizeLevel(e.Level) + rw.RLock() for _, h := range logHandlers[e.Level] { h.Log(e)