Skip to main content
Home
Gerald Villorente

Main navigation

  • Home
  • Blog

Breadcrumb

  1. Home

How to Manage Large Log Files in Go: Truncate a Log File to a Specific Size

By gerald, 18 November, 2024
log management

Log management

When working with applications, log files can quickly grow to unmanageable sizes, consuming valuable disk space and slowing down systems. To address this, you might want to monitor your log file size and truncate it when it exceeds a specific limit. This blog walks you through implementing such functionality in Go.

logs management
Photo by Pavel Danilyuk: https://www.pexels.com/photo/close-up-shot-of-document-folders-on-wooden-surface-7654130/

Objective

The goal is to create a Go program that:

  • Monitors the size of a log file.
  • Truncates the file to a specified size (e.g., 500MB) if it exceeds a threshold (e.g., 1GB).

Step 1: Define the Thresholds

We'll define two constants for our file size limits:

  • 1GB as the maximum allowed size.
  • 500MB as the target size after truncation.

Here’s how to define these values in bytes in Go:

const (
    maxSize = 1 * 1024 * 1024 * 1024 // 1GB in bytes
    truncateSize = 500 * 1024 * 1024 // 500MB in bytes
)

Step 2: Check File Size

Use Go's os.Stat() function to retrieve the size of the file:

fileInfo, err := os.Stat(logFilePath)
if err != nil {
    fmt.Printf("Error accessing file: %v\n", err)
    os.Exit(1)
}
fileSize := fileInfo.Size()

This retrieves the file's metadata, including its size in bytes.

Step 3: Truncate the File

If the file exceeds the size threshold, we need to truncate it to 500MB. Since we want to retain the most recent logs, we’ll:

  1. Count the total number of lines in the file.
  2. Calculate how many lines correspond to 500MB.
  3. Keep only the last targetLines lines.

Here’s the code to achieve this:

// Open the log file for reading
file, err := os.Open(logFilePath)
if err != nil {
    fmt.Printf("Error opening file: %v\n", err)
    os.Exit(1)
}
defer file.Close()
// Count total lines in the file
scanner := bufio.NewScanner(file)
totalLines := 0
for scanner.Scan() {
    totalLines++
}
if err := scanner.Err(); err != nil {
    fmt.Printf("Error reading file: %v\n", err)
    os.Exit(1)
}
// Calculate the number of lines to keep for 500MB
targetLines := int64(totalLines) * truncateSize / fileSize
// Read the last `targetLines` lines
file.Seek(0, 0)
linesToKeep := make([]string, 0, targetLines)
scanner = bufio.NewScanner(file)
lineCount := 0
for scanner.Scan() {
    lineCount++
    if lineCount > int(totalLines)-int(targetLines) {
        linesToKeep = append(linesToKeep, scanner.Text())
    }
}

Step 4: Write to a Temporary File

Once you’ve identified the lines to keep, write them to a temporary file:

tempFilePath := logFilePath + ".tmp"
tempFile, err := os.Create(tempFilePath)
if err != nil {
    fmt.Printf("Error creating temp file: %v\n", err)
    os.Exit(1)
}
defer tempFile.Close()
for _, line := range linesToKeep {
    tempFile.WriteString(line + "\n")
}

Step 5: Replace the Original File

Finally, replace the original file with the truncated file:

if err := os.Rename(tempFilePath, logFilePath); err != nil {
    fmt.Printf("Error replacing file: %v\n", err)
    os.Exit(1)
}

Full Code Example

Here’s the complete program:

package main
import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)
const (
    maxSize      = 1 * 1024 * 1024 * 1024 // 1GB in bytes
    truncateSize = 500 * 1024 * 1024      // 500MB in bytes
)
func main() {
    if len(os.Args) < 2 {
        fmt.Println("Usage: truncate_log /path/to/logfile.log")
        os.Exit(1)
    }
    logFilePath := os.Args[1]
    fileInfo, err := os.Stat(logFilePath)
    if err != nil {
        fmt.Printf("Error accessing file: %v\n", err)
        os.Exit(1)
    }
    fileSize := fileInfo.Size()
    if fileSize >= maxSize {
        fmt.Printf("Log file is %d bytes, truncating to %d bytes.\n", fileSize, truncateSize)
        file, err := os.Open(logFilePath)
        if err != nil {
            fmt.Printf("Error opening file: %v\n", err)
            os.Exit(1)
        }
        defer file.Close()
        scanner := bufio.NewScanner(file)
        totalLines := 0
        for scanner.Scan() {
            totalLines++
        }
        targetLines := int64(totalLines) * truncateSize / fileSize
        file.Seek(0, 0)
        linesToKeep := make([]string, 0, targetLines)
        scanner = bufio.NewScanner(file)
        lineCount := 0
        for scanner.Scan() {
            lineCount++
            if lineCount > int(totalLines)-int(targetLines) {
                linesToKeep = append(linesToKeep, scanner.Text())
            }
        }
        tempFilePath := logFilePath + ".tmp"
        tempFile, err := os.Create(tempFilePath)
        if err != nil {
            fmt.Printf("Error creating temp file: %v\n", err)
            os.Exit(1)
        }
        defer tempFile.Close()
        for _, line := range linesToKeep {
            tempFile.WriteString(line + "\n")
        }
        if err := os.Rename(tempFilePath, logFilePath); err != nil {
            fmt.Printf("Error replacing file: %v\n", err)
            os.Exit(1)
        }
        fmt.Printf("Log file truncated to %d bytes.\n", truncateSize)
    } else {
        fmt.Printf("Log file size is within limits: %d bytes.\n", fileSize)
    }
}

How to Run

  1. Save the file as truncate_log.go.
  2. Build the program:

    go build -o truncate_log truncate_log.go

  3. Run the program with a log file as an argument:

    ./truncate_log /path/to/logfile.log

Final Thoughts

This Go program efficiently manages large log files by monitoring their size and truncating them to a manageable size. You can integrate it into your automation pipeline or use it as a standalone tool to keep log files under control.

Have questions or suggestions? Feel free to share them in the comments below!

Tags

  • log management
  • scripting
  • Log in or register to post comments

Comments

Recent content

  • Fixing the "Malware Detected" Error in Docker for macOS
  • How to Manage Large Log Files in Go: Truncate a Log File to a Specific Size
  • Taming the Slowpokes: A Guide to Conquering Sluggish MySQL Queries
  • Taming the Slow Beast: Using Percona pt-query-digest to Diagnose MySQL Bottlenecks
  • Speed Up Your Web App: The Ins and Outs of Redis
  • Cherishing the Present: A Timeless Gift for Your Loved Ones
  • Diving Deep: Redis Slowlog vs. Redis MONITOR
  • MSET vs. HSET: Storing Data Efficiently in Redis
  • Installing TP-Link AC600 Wireless Adapter on Manjaro with Realtek RTL8811AU
  • Understanding Variadic Parameters in Go (Golang)
RSS feed

This website is powered by Drupal and Pantheon WebOps Platform.

pantheon