Files
mev-beta/internal/logger/logger.go

148 lines
3.6 KiB
Go

package logger
import (
"fmt"
"log"
"os"
"strings"
"time"
)
// LogLevel represents different log levels
type LogLevel int
const (
DEBUG LogLevel = iota
INFO
WARN
ERROR
OPPORTUNITY // Special level for opportunities
)
var logLevelNames = map[LogLevel]string{
DEBUG: "DEBUG",
INFO: "INFO",
WARN: "WARN",
ERROR: "ERROR",
OPPORTUNITY: "OPPORTUNITY",
}
// Logger represents a simple logger wrapper
type Logger struct {
logger *log.Logger
level LogLevel
levelName string
}
// parseLogLevel converts string log level to LogLevel enum
func parseLogLevel(level string) LogLevel {
switch strings.ToLower(level) {
case "debug":
return DEBUG
case "info":
return INFO
case "warn", "warning":
return WARN
case "error":
return ERROR
default:
return INFO // Default to INFO level
}
}
// New creates a new logger
func New(level string, format string, file string) *Logger {
// Determine output destination
var output *os.File
if file != "" {
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Printf("Failed to create log file %s: %v, falling back to stdout", file, err)
output = os.Stdout
} else {
output = f
}
} else {
output = os.Stdout
}
// Create the logger with custom format
logger := log.New(output, "", 0) // No flags, we'll format ourselves
logLevel := parseLogLevel(level)
return &Logger{
logger: logger,
level: logLevel,
levelName: level,
}
}
// shouldLog determines if a message should be logged based on level
func (l *Logger) shouldLog(level LogLevel) bool {
return level >= l.level
}
// formatMessage formats a log message with timestamp and level
func (l *Logger) formatMessage(level LogLevel, v ...interface{}) string {
timestamp := time.Now().Format("2006/01/02 15:04:05")
levelName := logLevelNames[level]
message := fmt.Sprint(v...)
return fmt.Sprintf("%s [%s] %s", timestamp, levelName, message)
}
// Debug logs a debug message
func (l *Logger) Debug(v ...interface{}) {
if l.shouldLog(DEBUG) {
l.logger.Println(l.formatMessage(DEBUG, v...))
}
}
// Info logs an info message
func (l *Logger) Info(v ...interface{}) {
if l.shouldLog(INFO) {
l.logger.Println(l.formatMessage(INFO, v...))
}
}
// Warn logs a warning message
func (l *Logger) Warn(v ...interface{}) {
if l.shouldLog(WARN) {
l.logger.Println(l.formatMessage(WARN, v...))
}
}
// Error logs an error message
func (l *Logger) Error(v ...interface{}) {
if l.shouldLog(ERROR) {
l.logger.Println(l.formatMessage(ERROR, v...))
}
}
// Opportunity logs a found opportunity with detailed information
// This always logs regardless of level since opportunities are critical
func (l *Logger) Opportunity(txHash, from, to, method, protocol string, amountIn, amountOut, minOut, profitUSD float64, additionalData map[string]interface{}) {
timestamp := time.Now().Format("2006/01/02 15:04:05")
message := fmt.Sprintf(`%s [OPPORTUNITY] 🎯 ARBITRAGE OPPORTUNITY DETECTED
├── Transaction: %s
├── From: %s → To: %s
├── Method: %s (%s)
├── Amount In: %.6f tokens
├── Amount Out: %.6f tokens
├── Min Out: %.6f tokens
├── Estimated Profit: $%.2f USD
└── Additional Data: %v`,
timestamp, txHash, from, to, method, protocol,
amountIn, amountOut, minOut, profitUSD, additionalData)
l.logger.Println(message)
}
// OpportunitySimple logs a simple opportunity message (for backwards compatibility)
func (l *Logger) OpportunitySimple(v ...interface{}) {
timestamp := time.Now().Format("2006/01/02 15:04:05")
message := fmt.Sprint(v...)
l.logger.Printf("%s [OPPORTUNITY] %s", timestamp, message)
}