package main import ( "io" "os" "path/filepath" "time" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) // Logger is the global logger instance var Logger *logrus.Logger // InitLogger initializes the structured logger with file output func InitLogger(config *Config) error { Logger = logrus.New() // Set log level based on configuration level, err := logrus.ParseLevel(config.LogLevel) if err != nil { level = logrus.InfoLevel } Logger.SetLevel(level) // Set JSON formatter for structured logging Logger.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: time.RFC3339, FieldMap: logrus.FieldMap{ logrus.FieldKeyTime: "timestamp", logrus.FieldKeyLevel: "level", logrus.FieldKeyMsg: "message", }, }) // Create log directory if it doesn't exist if err := os.MkdirAll(config.LogDir, 0755); err != nil { return err } // Create log file with timestamp logFile := filepath.Join(config.LogDir, "app.log") file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return err } // Set output to both file and stdout multiWriter := io.MultiWriter(os.Stdout, file) Logger.SetOutput(multiWriter) // Log initialization with default fields Logger.WithFields(logrus.Fields{ "service": "counter-app", "environment": string(config.Environment), "version": "1.0.0", }).Info("Logger initialized successfully") return nil } // GetLogger returns the global logger instance func GetLogger() *logrus.Logger { if Logger == nil { // Fallback to standard logger if not initialized logrus.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: time.RFC3339, }) return logrus.StandardLogger() } return Logger } // GetLoggerWithDefaults returns a logger entry with default fields func GetLoggerWithDefaults(env Environment) *logrus.Entry { return Logger.WithFields(logrus.Fields{ "service": "counter-app", "environment": string(env), "version": "1.0.0", }) } // LoggingMiddleware creates a Gin middleware for HTTP request logging func LoggingMiddleware(config *Config) gin.HandlerFunc { return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { // Create structured log entry with default fields entry := Logger.WithFields(logrus.Fields{ "service": "counter-app", "environment": string(config.Environment), "version": "1.0.0", "method": param.Method, "path": param.Path, "status": param.StatusCode, "latency": param.Latency.String(), "client_ip": param.ClientIP, "user_agent": param.Request.UserAgent(), "timestamp": param.TimeStamp.Format(time.RFC3339), }) // Set log level based on status code switch { case param.StatusCode >= 500: entry.Error("HTTP Request") case param.StatusCode >= 400: entry.Warn("HTTP Request") default: entry.Info("HTTP Request") } // Return empty string since we're handling logging ourselves return "" }) }