LFG
Some checks failed
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
CI/CD Pipeline / Build Docker Image (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Create Release (push) Has been cancelled
Some checks failed
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
CI/CD Pipeline / Build Docker Image (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Create Release (push) Has been cancelled
This commit is contained in:
124
internal/logger/logger.go
Normal file
124
internal/logger/logger.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Logger represents the application logger
|
||||
type Logger struct {
|
||||
debugLogger *log.Logger
|
||||
infoLogger *log.Logger
|
||||
warnLogger *log.Logger
|
||||
errorLogger *log.Logger
|
||||
}
|
||||
|
||||
// Field represents a log field
|
||||
type Field struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Option represents a logger option
|
||||
type Option func(*Logger)
|
||||
|
||||
// NewLogger creates a new logger with default settings
|
||||
func NewLogger(opts ...Option) *Logger {
|
||||
logger := &Logger{
|
||||
debugLogger: log.New(os.Stdout, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile),
|
||||
infoLogger: log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile),
|
||||
warnLogger: log.New(os.Stdout, "WARN: ", log.Ldate|log.Ltime|log.Lshortfile),
|
||||
errorLogger: log.New(os.Stdout, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile),
|
||||
}
|
||||
|
||||
// Apply options
|
||||
for _, opt := range opts {
|
||||
opt(logger)
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
// Debug logs a debug message
|
||||
func (l *Logger) Debug(msg string, fields ...Field) {
|
||||
l.debugLogger.Printf(formatMessage(msg, fields...))
|
||||
}
|
||||
|
||||
// Info logs an info message
|
||||
func (l *Logger) Info(msg string, fields ...Field) {
|
||||
l.infoLogger.Printf(formatMessage(msg, fields...))
|
||||
}
|
||||
|
||||
// Warn logs a warning message
|
||||
func (l *Logger) Warn(msg string, fields ...Field) {
|
||||
l.warnLogger.Printf(formatMessage(msg, fields...))
|
||||
}
|
||||
|
||||
// Error logs an error message
|
||||
func (l *Logger) Error(msg string, fields ...Field) {
|
||||
l.errorLogger.Printf(formatMessage(msg, fields...))
|
||||
}
|
||||
|
||||
// Fatal logs a fatal message and exits
|
||||
func (l *Logger) Fatal(msg string, fields ...Field) {
|
||||
l.errorLogger.Printf(formatMessage(msg, fields...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// String creates a string field
|
||||
func String(key, value string) Field {
|
||||
return Field{Key: key, Value: value}
|
||||
}
|
||||
|
||||
// Int creates an int field
|
||||
func Int(key string, value int) Field {
|
||||
return Field{Key: key, Value: value}
|
||||
}
|
||||
|
||||
// Bool creates a bool field
|
||||
func Bool(key string, value bool) Field {
|
||||
return Field{Key: key, Value: value}
|
||||
}
|
||||
|
||||
// Error creates an error field
|
||||
func Error(err error) Field {
|
||||
return Field{Key: "error", Value: err}
|
||||
}
|
||||
|
||||
// formatMessage formats a log message with fields
|
||||
func formatMessage(msg string, fields ...Field) string {
|
||||
if len(fields) == 0 {
|
||||
return msg
|
||||
}
|
||||
|
||||
result := msg + " ["
|
||||
for i, field := range fields {
|
||||
if i > 0 {
|
||||
result += ", "
|
||||
}
|
||||
result += field.Key + "="
|
||||
result += toString(field.Value)
|
||||
}
|
||||
result += "]"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// toString converts a value to string
|
||||
func toString(value interface{}) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return v
|
||||
case int:
|
||||
return string(v)
|
||||
case bool:
|
||||
if v {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
case error:
|
||||
return v.Error()
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
185
internal/logger/logger_test.go
Normal file
185
internal/logger/logger_test.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewLogger(t *testing.T) {
|
||||
logger := NewLogger()
|
||||
if logger == nil {
|
||||
t.Error("Expected logger to be created, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerDebug(t *testing.T) {
|
||||
// Create a buffer to capture log output
|
||||
var buf bytes.Buffer
|
||||
log.SetOutput(&buf)
|
||||
defer func() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
|
||||
logger := NewLogger()
|
||||
logger.Debug("Debug message", String("key", "value"))
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "DEBUG: Debug message") {
|
||||
t.Errorf("Expected log output to contain 'DEBUG: Debug message', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "key=value") {
|
||||
t.Errorf("Expected log output to contain 'key=value', got %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerInfo(t *testing.T) {
|
||||
// Create a buffer to capture log output
|
||||
var buf bytes.Buffer
|
||||
log.SetOutput(&buf)
|
||||
defer func() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
|
||||
logger := NewLogger()
|
||||
logger.Info("Info message", Int("number", 42))
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "INFO: Info message") {
|
||||
t.Errorf("Expected log output to contain 'INFO: Info message', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "number=42") {
|
||||
t.Errorf("Expected log output to contain 'number=42', got %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerWarn(t *testing.T) {
|
||||
// Create a buffer to capture log output
|
||||
var buf bytes.Buffer
|
||||
log.SetOutput(&buf)
|
||||
defer func() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
|
||||
logger := NewLogger()
|
||||
logger.Warn("Warning message", Bool("flag", true))
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "WARN: Warning message") {
|
||||
t.Errorf("Expected log output to contain 'WARN: Warning message', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "flag=true") {
|
||||
t.Errorf("Expected log output to contain 'flag=true', got %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerError(t *testing.T) {
|
||||
// Create a buffer to capture log output
|
||||
var buf bytes.Buffer
|
||||
log.SetOutput(&buf)
|
||||
defer func() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
|
||||
logger := NewLogger()
|
||||
err := os.ErrNotExist
|
||||
logger.Error("Error message", Error(err))
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "ERROR: Error message") {
|
||||
t.Errorf("Expected log output to contain 'ERROR: Error message', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "error=file does not exist") {
|
||||
t.Errorf("Expected log output to contain 'error=file does not exist', got %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerFatal(t *testing.T) {
|
||||
// Create a buffer to capture log output
|
||||
var buf bytes.Buffer
|
||||
log.SetOutput(&buf)
|
||||
defer func() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
|
||||
// Mock os.Exit to prevent the test from exiting
|
||||
exitCalled := false
|
||||
exitFunc := func(code int) {
|
||||
exitCalled = true
|
||||
}
|
||||
osExit = exitFunc
|
||||
defer func() {
|
||||
osExit = realOsExit
|
||||
}()
|
||||
|
||||
logger := NewLogger()
|
||||
logger.Fatal("Fatal message", String("reason", "testing"))
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "ERROR: Fatal message") {
|
||||
t.Errorf("Expected log output to contain 'ERROR: Fatal message', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "reason=testing") {
|
||||
t.Errorf("Expected log output to contain 'reason=testing', got %s", output)
|
||||
}
|
||||
if !exitCalled {
|
||||
t.Error("Expected os.Exit to be called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerMultipleFields(t *testing.T) {
|
||||
// Create a buffer to capture log output
|
||||
var buf bytes.Buffer
|
||||
log.SetOutput(&buf)
|
||||
defer func() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
|
||||
logger := NewLogger()
|
||||
logger.Info("Multiple fields",
|
||||
String("string", "value"),
|
||||
Int("int", 123),
|
||||
Bool("bool", false))
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "INFO: Multiple fields") {
|
||||
t.Errorf("Expected log output to contain 'INFO: Multiple fields', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "string=value") {
|
||||
t.Errorf("Expected log output to contain 'string=value', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "int=123") {
|
||||
t.Errorf("Expected log output to contain 'int=123', got %s", output)
|
||||
}
|
||||
if !strings.Contains(output, "bool=false") {
|
||||
t.Errorf("Expected log output to contain 'bool=false', got %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerNoFields(t *testing.T) {
|
||||
// Create a buffer to capture log output
|
||||
var buf bytes.Buffer
|
||||
log.SetOutput(&buf)
|
||||
defer func() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
|
||||
logger := NewLogger()
|
||||
logger.Info("No fields")
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "INFO: No fields") {
|
||||
t.Errorf("Expected log output to contain 'INFO: No fields', got %s", output)
|
||||
}
|
||||
if strings.Contains(output, "[") {
|
||||
t.Error("Expected log output to not contain field brackets when no fields are provided")
|
||||
}
|
||||
}
|
||||
|
||||
// Mock os.Exit for testing
|
||||
var (
|
||||
osExit = func(code int) { os.Exit(code) }
|
||||
realOsExit = osExit
|
||||
)
|
Reference in New Issue
Block a user