Logging

Go menyediakan package log untuk logging. Selain itu, ada banyak third-party logging package yang populer seperti logrus dan zap.

Contoh Masalah

Bagaimana cara:

  1. Menggunakan standard logger
  2. Membuat custom logger
  3. Mengatur log level
  4. Menulis log ke file

Penyelesaian

package main

import (
    "fmt"
    "io"
    "log"
    "os"
    "path/filepath"
    "runtime"
    "sync"
    "time"
)

// 1. Custom logger
type Logger struct {
    *log.Logger
    level LogLevel
}

type LogLevel int

const (
    DEBUG LogLevel = iota
    INFO
    WARNING
    ERROR
    FATAL
)

var levelNames = map[LogLevel]string{
    DEBUG:   "DEBUG",
    INFO:    "INFO",
    WARNING: "WARN",
    ERROR:   "ERROR",
    FATAL:   "FATAL",
}

// 2. Custom log formatter
func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
    if level < l.level {
        return
    }

    // Get caller info
    _, file, line, ok := runtime.Caller(2)
    if !ok {
        file = "???"
        line = 0
    }
    file = filepath.Base(file)

    // Format message
    msg := fmt.Sprintf(format, v...)
    l.Printf("[%s] %s:%d: %s",
        levelNames[level], file, line, msg)
}

// 3. Logger methods
func (l *Logger) Debug(format string, v ...interface{}) {
    l.log(DEBUG, format, v...)
}

func (l *Logger) Info(format string, v ...interface{}) {
    l.log(INFO, format, v...)
}

func (l *Logger) Warn(format string, v ...interface{}) {
    l.log(WARNING, format, v...)
}

func (l *Logger) Error(format string, v ...interface{}) {
    l.log(ERROR, format, v...)
}

func (l *Logger) Fatal(format string, v ...interface{}) {
    l.log(FATAL, format, v...)
    os.Exit(1)
}

// 4. Logger factory
func NewLogger(out io.Writer, prefix string, level LogLevel) *Logger {
    return &Logger{
        Logger: log.New(out, prefix,
            log.Ldate|log.Ltime|log.Lmicroseconds),
        level: level,
    }
}

// 5. Rotating file writer
type RotatingFileWriter struct {
    sync.Mutex
    filename     string
    maxSize     int64
    currentSize int64
    file        *os.File
}

func NewRotatingFileWriter(filename string, maxSize int64) (*RotatingFileWriter, error) {
    file, err := os.OpenFile(filename,
        os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
    if err != nil {
        return nil, err
    }

    info, err := file.Stat()
    if err != nil {
        return nil, err
    }

    return &RotatingFileWriter{
        filename:    filename,
        maxSize:    maxSize,
        currentSize: info.Size(),
        file:       file,
    }, nil
}

func (w *RotatingFileWriter) Write(p []byte) (n int, err error) {
    w.Lock()
    defer w.Unlock()

    if w.currentSize+int64(len(p)) > w.maxSize {
        w.file.Close()

        // Rotate file
        backup := fmt.Sprintf("%s.%s",
            w.filename,
            time.Now().Format("2006-01-02-15-04-05"))
        os.Rename(w.filename, backup)

        // Create new file
        w.file, err = os.OpenFile(w.filename,
            os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
        if err != nil {
            return 0, err
        }
        w.currentSize = 0
    }

    n, err = w.file.Write(p)
    w.currentSize += int64(n)
    return
}

func (w *RotatingFileWriter) Close() error {
    w.Lock()
    defer w.Unlock()
    return w.file.Close()
}

// 6. Example application
type App struct {
    logger *Logger
}

func (a *App) doSomething() {
    a.logger.Debug("Debug message: %d", 42)
    a.logger.Info("Info message: %s", "hello")
    a.logger.Warn("Warning message: %v", true)
    
    err := fmt.Errorf("something went wrong")
    if err != nil {
        a.logger.Error("Error occurred: %v", err)
    }
}

func main() {
    // Contoh 1: Standard logger
    fmt.Println("Standard Logger Example:")
    log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
    log.Println("This is a standard log message")

    // Contoh 2: Custom logger with console output
    fmt.Println("\nCustom Console Logger Example:")
    consoleLogger := NewLogger(os.Stdout, "", DEBUG)
    app := &App{logger: consoleLogger}
    app.doSomething()

    // Contoh 3: File logger with rotation
    fmt.Println("\nRotating File Logger Example:")
    rotatingWriter, err := NewRotatingFileWriter("app.log", 1024) // 1KB
    if err != nil {
        log.Fatal(err)
    }
    defer rotatingWriter.Close()

    fileLogger := NewLogger(rotatingWriter, "", INFO)
    app = &App{logger: fileLogger}
    
    // Generate some log entries
    for i := 0; i < 5; i++ {
        app.doSomething()
        time.Sleep(time.Millisecond * 100)
    }

    // Contoh 4: Multi-writer logger
    fmt.Println("\nMulti-writer Logger Example:")
    multiWriter := io.MultiWriter(os.Stdout, rotatingWriter)
    multiLogger := NewLogger(multiWriter, "", WARNING)
    app = &App{logger: multiLogger}
    app.doSomething()

    // Contoh 5: Different log levels
    fmt.Println("\nLog Levels Example:")
    levels := []LogLevel{DEBUG, INFO, WARNING, ERROR}
    for _, level := range levels {
        logger := NewLogger(os.Stdout, "", level)
        fmt.Printf("\nLogger with level %s:\n", levelNames[level])
        app = &App{logger: logger}
        app.doSomething()
    }

    // Cleanup
    os.Remove("app.log")
}

Penjelasan Kode

  1. Basic Logging

    • Standard logger
    • Log levels
    • Formatting
  2. Advanced Features

    • Custom logger
    • File rotation
    • Multi-writer
  3. Best Practices

    • Structured logging
    • Error handling
    • Performance

Output

Standard Logger Example:
2024/01/21 11:57:22 main.go:42: This is a standard log message

Custom Console Logger Example:
2024/01/21 11:57:22.123456 [DEBUG] main.go:42: Debug message: 42
2024/01/21 11:57:22.123456 [INFO] main.go:43: Info message: hello
2024/01/21 11:57:22.123456 [WARN] main.go:44: Warning message: true
2024/01/21 11:57:22.123456 [ERROR] main.go:47: Error occurred: something went wrong

Rotating File Logger Example:
[Writing to app.log...]

Multi-writer Logger Example:
2024/01/21 11:57:22.123456 [WARN] main.go:44: Warning message: true
2024/01/21 11:57:22.123456 [ERROR] main.go:47: Error occurred: something went wrong

Log Levels Example:
Logger with level DEBUG:
2024/01/21 11:57:22.123456 [DEBUG] main.go:42: Debug message: 42
2024/01/21 11:57:22.123456 [INFO] main.go:43: Info message: hello
2024/01/21 11:57:22.123456 [WARN] main.go:44: Warning message: true
2024/01/21 11:57:22.123456 [ERROR] main.go:47: Error occurred: something went wrong

Logger with level INFO:
2024/01/21 11:57:22.123456 [INFO] main.go:43: Info message: hello
2024/01/21 11:57:22.123456 [WARN] main.go:44: Warning message: true
2024/01/21 11:57:22.123456 [ERROR] main.go:47: Error occurred: something went wrong

Logger with level WARNING:
2024/01/21 11:57:22.123456 [WARN] main.go:44: Warning message: true
2024/01/21 11:57:22.123456 [ERROR] main.go:47: Error occurred: something went wrong

Logger with level ERROR:
2024/01/21 11:57:22.123456 [ERROR] main.go:47: Error occurred: something went wrong

Tips

  • Gunakan log level yang sesuai
  • Implementasi file rotation
  • Log error dengan detail
  • Pertimbangkan performance
  • Backup log files