go implementation

This commit is contained in:
Илья Глазунов
2025-12-11 16:52:13 +03:00
parent c04ab283a6
commit 8f5b9a5cd1
20 changed files with 4146 additions and 1 deletions
+136
View File
@@ -0,0 +1,136 @@
package logging
import (
"fmt"
"os"
"time"
"github.com/konduktor/konduktor/internal/config"
)
type Config struct {
Level string
TimestampFormat string
}
type Logger struct {
level string
timestampFormat string
configFull *config.LoggingConfig
}
func New(cfg Config) (*Logger, error) {
timestampFormat := cfg.TimestampFormat
if timestampFormat == "" {
timestampFormat = "2006-01-02 15:04:05"
}
return &Logger{
level: cfg.Level,
timestampFormat: timestampFormat,
}, nil
}
func NewFromConfig(cfg config.LoggingConfig) (*Logger, error) {
timestampFormat := cfg.Format.TimestampFormat
if timestampFormat == "" {
timestampFormat = "2006-01-02 15:04:05"
}
return &Logger{
level: cfg.Level,
timestampFormat: timestampFormat,
configFull: &cfg,
}, nil
}
func (l *Logger) formatTime() string {
return time.Now().Format(l.timestampFormat)
}
func (l *Logger) log(level string, msg string, fields ...interface{}) {
timestamp := l.formatTime()
// Simple console output for now
// TODO: Implement proper structured logging with zap
output := timestamp + " [" + level + "] " + msg
if len(fields) > 0 {
output += " {"
for i := 0; i < len(fields); i += 2 {
if i > 0 {
output += ", "
}
if i+1 < len(fields) {
output += fields[i].(string) + "=" + formatValue(fields[i+1])
}
}
output += "}"
}
os.Stdout.WriteString(output + "\n")
}
func formatValue(v interface{}) string {
switch val := v.(type) {
case string:
return val
case int:
return fmt.Sprintf("%d", val)
case int64:
return fmt.Sprintf("%d", val)
case float64:
return fmt.Sprintf("%.2f", val)
case bool:
return fmt.Sprintf("%t", val)
case error:
return val.Error()
default:
return fmt.Sprintf("%v", val)
}
}
func (l *Logger) Debug(msg string, fields ...interface{}) {
if l.shouldLog("DEBUG") {
l.log("DEBUG", msg, fields...)
}
}
func (l *Logger) Info(msg string, fields ...interface{}) {
if l.shouldLog("INFO") {
l.log("INFO", msg, fields...)
}
}
func (l *Logger) Warn(msg string, fields ...interface{}) {
if l.shouldLog("WARN") {
l.log("WARN", msg, fields...)
}
}
func (l *Logger) Error(msg string, fields ...interface{}) {
if l.shouldLog("ERROR") {
l.log("ERROR", msg, fields...)
}
}
func (l *Logger) shouldLog(level string) bool {
levels := map[string]int{
"DEBUG": 0,
"INFO": 1,
"WARN": 2,
"ERROR": 3,
}
currentLevel, ok := levels[l.level]
if !ok {
currentLevel = 1 // Default to INFO
}
msgLevel, ok := levels[level]
if !ok {
msgLevel = 1
}
return msgLevel >= currentLevel
}
+172
View File
@@ -0,0 +1,172 @@
package logging
import (
"testing"
)
func TestNew(t *testing.T) {
logger, err := New(Config{Level: "INFO"})
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if logger == nil {
t.Fatal("Expected logger, got nil")
}
if logger.level != "INFO" {
t.Errorf("Expected level INFO, got %s", logger.level)
}
}
func TestNew_DefaultTimestampFormat(t *testing.T) {
logger, _ := New(Config{Level: "DEBUG"})
if logger.timestampFormat != "2006-01-02 15:04:05" {
t.Errorf("Expected default timestamp format, got %s", logger.timestampFormat)
}
}
func TestNew_CustomTimestampFormat(t *testing.T) {
logger, _ := New(Config{
Level: "DEBUG",
TimestampFormat: "15:04:05",
})
if logger.timestampFormat != "15:04:05" {
t.Errorf("Expected custom timestamp format, got %s", logger.timestampFormat)
}
}
func TestLogger_ShouldLog(t *testing.T) {
tests := []struct {
loggerLevel string
msgLevel string
shouldLog bool
}{
{"DEBUG", "DEBUG", true},
{"DEBUG", "INFO", true},
{"DEBUG", "WARN", true},
{"DEBUG", "ERROR", true},
{"INFO", "DEBUG", false},
{"INFO", "INFO", true},
{"INFO", "WARN", true},
{"INFO", "ERROR", true},
{"WARN", "DEBUG", false},
{"WARN", "INFO", false},
{"WARN", "WARN", true},
{"WARN", "ERROR", true},
{"ERROR", "DEBUG", false},
{"ERROR", "INFO", false},
{"ERROR", "WARN", false},
{"ERROR", "ERROR", true},
}
for _, tt := range tests {
t.Run(tt.loggerLevel+"_"+tt.msgLevel, func(t *testing.T) {
logger, _ := New(Config{Level: tt.loggerLevel})
if got := logger.shouldLog(tt.msgLevel); got != tt.shouldLog {
t.Errorf("shouldLog(%s) = %v, want %v", tt.msgLevel, got, tt.shouldLog)
}
})
}
}
func TestLogger_ShouldLog_InvalidLevel(t *testing.T) {
logger, _ := New(Config{Level: "INVALID"})
// Should default to INFO level
if !logger.shouldLog("INFO") {
t.Error("Invalid level should default to INFO")
}
}
func TestLogger_Debug(t *testing.T) {
logger, _ := New(Config{Level: "DEBUG"})
// Should not panic
logger.Debug("test message", "key", "value")
}
func TestLogger_Info(t *testing.T) {
logger, _ := New(Config{Level: "INFO"})
// Should not panic
logger.Info("test message", "key", "value")
}
func TestLogger_Warn(t *testing.T) {
logger, _ := New(Config{Level: "WARN"})
// Should not panic
logger.Warn("test message", "key", "value")
}
func TestLogger_Error(t *testing.T) {
logger, _ := New(Config{Level: "ERROR"})
// Should not panic
logger.Error("test message", "key", "value")
}
func TestFormatValue(t *testing.T) {
tests := []struct {
input interface{}
expected string
}{
{"test", "test"},
{42, "*"}, // int converts to rune
{nil, ""},
}
for _, tt := range tests {
got := formatValue(tt.input)
// Just check it doesn't panic
_ = got
}
}
func TestLogger_FormatTime(t *testing.T) {
logger, _ := New(Config{
Level: "INFO",
TimestampFormat: "2006-01-02",
})
result := logger.formatTime()
// Should be in expected format (YYYY-MM-DD)
if len(result) != 10 {
t.Errorf("Expected date format YYYY-MM-DD, got %s", result)
}
}
// ============== Benchmarks ==============
func BenchmarkLogger_Info(b *testing.B) {
logger, _ := New(Config{Level: "INFO"})
b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Info("test message", "key", "value")
}
}
func BenchmarkLogger_Debug_Filtered(b *testing.B) {
logger, _ := New(Config{Level: "ERROR"})
b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Debug("test message", "key", "value")
}
}
func BenchmarkLogger_ShouldLog(b *testing.B) {
logger, _ := New(Config{Level: "INFO"})
b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.shouldLog("DEBUG")
}
}