Skip to content

Commit

Permalink
support for multiple paths, more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
PAKalucki committed Sep 11, 2024
1 parent 6549eb5 commit f5abfe4
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 65 deletions.
1 change: 0 additions & 1 deletion .version

This file was deleted.

20 changes: 19 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"gopkg.in/yaml.v3"
)

type Paths []string

type LogEntry struct {
Path string `yaml:"path"`
Path Paths `yaml:"path"`
Type string `yaml:"type"`
Condition *Condition `yaml:"condition,omitempty"`
}
Expand Down Expand Up @@ -55,3 +57,19 @@ func loadConfig(filePath string) (Config, error) {

return config, nil
}

func (p *Paths) UnmarshalYAML(value *yaml.Node) error {
var singlePath string
if err := value.Decode(&singlePath); err == nil {
*p = Paths{singlePath}
return nil
}

var multiplePaths []string
if err := value.Decode(&multiplePaths); err == nil {
*p = Paths(multiplePaths)
return nil
}

return fmt.Errorf("failed to unmarshal path, expected a string or a list of strings")
}
49 changes: 49 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"os"
"path/filepath"
"testing"
)

func TestLoadConfig(t *testing.T) {
yamlContent := `
logs:
- path: "/path/to/log/*.log"
type: delete
- path:
- "/path/to/log1/*.log"
- "/path/to/log2/*.log"
type: rotate
condition:
size: "100MB"
schedule: "*/5 * * * *"
`

tempFile := filepath.Join(t.TempDir(), "config.yaml")
if err := os.WriteFile(tempFile, []byte(yamlContent), 0644); err != nil {
t.Fatalf("Failed to create temp config file: %v", err)
}

config, err := loadConfig(tempFile)
if err != nil {
t.Fatalf("Failed to load config: %v", err)
}

// Verify loaded config values
if len(config.Logs) != 2 {
t.Errorf("Expected 2 log entries, got %d", len(config.Logs))
}

if config.Logs[0].Type != "delete" {
t.Errorf("Expected type 'delete', got %s", config.Logs[0].Type)
}

if len(config.Logs[1].Path) != 2 {
t.Errorf("Expected 2 paths, got %d", len(config.Logs[1].Path))
}

if config.Schedule != "*/5 * * * *" {
t.Errorf("Expected schedule '*/5 * * * *', got %s", config.Schedule)
}
}
34 changes: 0 additions & 34 deletions configs/wingologrotate.yaml

This file was deleted.

19 changes: 19 additions & 0 deletions configs/wingologrotate.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
schedule: "0 0 * * *"

logs:
- path: "/tmp/test/logs/delete/*.log"
type: delete
condition:
age: "30m"

- path: "/tmp/test/logs/compress/*.log"
type: rotate
condition:
max_keep: 5
size: "30MB"
compress: true

- path:
- "/path1/*.log"
- "/path2/*.log"
type: delete
117 changes: 88 additions & 29 deletions logrotate.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,43 +46,102 @@ func runLogRotation() {

func createTask(logEntry LogEntry) func() {
return func() {
log.Printf("Running task for path: %s", logEntry.Path)
matchingFiles, err := filepath.Glob(filepath.Clean(logEntry.Path))
if err != nil {
log.Printf("Failed to expand wildcard for path %s: %v", logEntry.Path, err)
return
}
for _, path := range logEntry.Path {
log.Printf("Running task for path: %s", path)
matchingFiles, err := filepath.Glob(filepath.Clean(path))
if err != nil {
log.Printf("Failed to expand wildcard for path %s: %v", path, err)
return
}

if logEntry.Type == "delete" {
for _, file := range matchingFiles {
if logEntry.Condition != nil && logEntry.Condition.Age != nil {
ageDuration, err := parseDuration(*logEntry.Condition.Age)
if err != nil {
log.Printf("Invalid age format for file %s: %v", file, err)
continue
switch logEntry.Type {
case "delete":
for _, file := range matchingFiles {
if logEntry.Condition != nil && logEntry.Condition.Age != nil {
ageDuration, err := parseDuration(*logEntry.Condition.Age)
if err != nil {
log.Printf("Invalid age format for file %s: %v", file, err)
continue
}

fileInfo, err := os.Stat(file)
if err != nil {
log.Printf("Failed to get file info for %s: %v", file, err)
continue
}
fileAge := time.Since(fileInfo.ModTime())

if fileAge < ageDuration {
// log.Printf("Skipping file %s, does not meet age condition (%s)", file, logEntry.Condition.Age)
continue
}
}

fileInfo, err := os.Stat(file)
log.Printf("Deleting file: %s", file)
err := os.Remove(file)
if err != nil {
log.Printf("Failed to get file info for %s: %v", file, err)
continue
}
fileAge := time.Since(fileInfo.ModTime())

if fileAge < ageDuration {
// log.Printf("Skipping file %s, does not meet age condition (%s)", file, logEntry.Condition.Age)
continue
log.Printf("Failed to delete file %s: %v", file, err)
} else {
log.Printf("Successfully deleted file: %s", file)
}
}

log.Printf("Deleting file: %s", file)
err := os.Remove(file)
if err != nil {
log.Printf("Failed to delete file %s: %v", file, err)
} else {
log.Printf("Successfully deleted file: %s", file)
}
case "rotate":
rotateLogFiles(logEntry)

default:
log.Printf("Unsupported task type: %s", logEntry.Type)
}
}
}
}

func rotateLogFiles(logEntry LogEntry) {
log.Printf("Rotating logs for path: %s", logEntry.Path)
// matchingFiles, err := filepath.Glob(filepath.Clean(logEntry.Path))
// if err != nil {
// log.Printf("Failed to expand wildcard for path %s: %v", logEntry.Path, err)
// return
// }

// for _, file := range matchingFiles {
// // Check if file size exceeds the specified condition
// fileInfo, err := os.Stat(file)
// if err != nil {
// log.Printf("Failed to get file info for %s: %v", file, err)
// continue
// }

// // Parse size condition
// maxSize, err := parseSize(logEntry.Condition.Size)
// if err != nil {
// log.Printf("Invalid size format for rotation: %v", err)
// continue
// }

// if fileInfo.Size() >= maxSize {
// rotatedFilePath := fmt.Sprintf("%s.%s", file, time.Now().Format("20060102-150405"))
// if err := os.Rename(file, rotatedFilePath); err != nil {
// log.Printf("Failed to rotate log file %s: %v", file, err)
// continue
// }
// log.Printf("Rotated log file: %s to %s", file, rotatedFilePath)

// // Compress the rotated log file if necessary
// if logEntry.Condition.Compress == nil || *logEntry.Condition.Compress {
// if err := compressLogFile(rotatedFilePath); err != nil {
// log.Printf("Failed to compress rotated log file %s: %v", rotatedFilePath, err)
// } else {
// log.Printf("Compressed log file: %s", rotatedFilePath)
// }
// }

// // Remove old log files if MaxKeep is set
// if logEntry.Condition.MaxKeep != nil {
// if err := removeOldLogFiles(filepath.Dir(file), filepath.Base(file), *logEntry.Condition.MaxKeep); err != nil {
// log.Printf("Failed to remove old log files: %v", err)
// }
// }
// }
// }
}
78 changes: 78 additions & 0 deletions logrotate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"bytes"
"log"
"os"
"path/filepath"
"strings"
"testing"
)

func TestRotateLogFiles(t *testing.T) {
tempDir := t.TempDir()

// Create test log files
file1 := filepath.Join(tempDir, "log1.log")
file2 := filepath.Join(tempDir, "log2.log")
_ = os.WriteFile(file1, make([]byte, 1024*1024*5), 0644) // 5MB log file
_ = os.WriteFile(file2, make([]byte, 1024*1024*10), 0644) // 10MB log file

logEntry := LogEntry{
Path: Paths{filepath.Join(tempDir, "*.log")},
Type: "rotate",
Condition: &Condition{
Size: stringPtr("1MB"), // Should trigger rotation
Compress: boolPtr(true),
MaxKeep: intPtr(1),
},
}

rotateLogFiles(logEntry)

compressedFiles, _ := filepath.Glob(filepath.Join(tempDir, "*.gz"))
if len(compressedFiles) == 0 {
t.Errorf("Expected compressed log files, but found none.")
}

remainingFiles, _ := filepath.Glob(filepath.Join(tempDir, "*.log"))
if len(remainingFiles) > 1 {
t.Errorf("Expected at most 1 log file, but found %d", len(remainingFiles))
}
}

// Helper functions for creating pointers to primitives
func stringPtr(s string) *string {
return &s
}

func boolPtr(b bool) *bool {
return &b
}

func intPtr(i int) *int {
return &i
}

func TestCreateTask(t *testing.T) {
logEntry := LogEntry{
Path: Paths{"/tmp/test/logs/delete/*.log"},
Type: "delete",
Condition: &Condition{
Age: stringPtr("1h"),
},
}

task := createTask(logEntry)

// Mock logging
logBuf := new(bytes.Buffer)
log.SetOutput(logBuf)

// Run the task
task()

if !strings.Contains(logBuf.String(), "Running task for path: /tmp/test/logs/delete/*.log") {
t.Errorf("Expected log output for running task, got %s", logBuf.String())
}
}

0 comments on commit f5abfe4

Please sign in to comment.