feat(outputs): use only one output file, appending on every run

This commit is contained in:
2025-10-12 10:39:00 +01:00
parent 98aa70e08f
commit 68d0b5fe4d
3 changed files with 46 additions and 38 deletions

View File

@@ -25,17 +25,28 @@ func NewPacketCapture(iface, outputPath string) (*PacketCapture, error) {
return nil, fmt.Errorf("pcap open (try running as root): %w", err) return nil, fmt.Errorf("pcap open (try running as root): %w", err)
} }
file, err := os.Create(outputPath) // Check if file exists
fileExists := false
if _, err := os.Stat(outputPath); err == nil {
fileExists = true
}
// Open in append mode
file, err := os.OpenFile(outputPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
handle.Close() handle.Close()
return nil, fmt.Errorf("create pcap file: %w", err) return nil, fmt.Errorf("create/open pcap file: %w", err)
} }
writer := pcapgo.NewWriter(file) writer := pcapgo.NewWriter(file)
if err := writer.WriteFileHeader(65535, handle.LinkType()); err != nil {
handle.Close() // Only write header if file is new
file.Close() if !fileExists {
return nil, fmt.Errorf("pcap header: %w", err) if err := writer.WriteFileHeader(65535, handle.LinkType()); err != nil {
handle.Close()
file.Close()
return nil, fmt.Errorf("pcap header: %w", err)
}
} }
return &PacketCapture{ return &PacketCapture{

View File

@@ -31,27 +31,35 @@ type MetricsWriter struct {
} }
func NewMetricsWriter(path string) (*MetricsWriter, error) { func NewMetricsWriter(path string) (*MetricsWriter, error) {
file, err := os.Create(path) // Check if file exists
fileExists := false
if _, err := os.Stat(path); err == nil {
fileExists = true
}
// Open in append mode
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
return nil, fmt.Errorf("create csv output: %w", err) return nil, fmt.Errorf("create/open csv output: %w", err)
} }
writer := csv.NewWriter(file) writer := csv.NewWriter(file)
// Write CSV header // Only write header if file is new
header := []string{ if !fileExists {
"domain", "query_type", "protocol", "dnssec", "auth_dnssec", "keep_alive", header := []string{
"dns_server", "timestamp", "duration_ns", "duration_ms", "domain", "query_type", "protocol", "dnssec", "auth_dnssec", "keep_alive",
"request_size_bytes", "response_size_bytes", "response_code", "error", "dns_server", "timestamp", "duration_ns", "duration_ms",
} "request_size_bytes", "response_size_bytes", "response_code", "error", "run_id",
}
if err := writer.Write(header); err != nil { if err := writer.Write(header); err != nil {
file.Close() file.Close()
return nil, fmt.Errorf("write csv header: %w", err) return nil, fmt.Errorf("write csv header: %w", err)
}
writer.Flush()
} }
writer.Flush()
return &MetricsWriter{ return &MetricsWriter{
writer: writer, writer: writer,
file: file, file: file,

View File

@@ -5,25 +5,17 @@ import (
"net/url" "net/url"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
) )
func GenerateOutputPaths(outputDir, upstream string, dnssec, authDNSSEC, keepAlive bool) (csvPath, pcapPath string) { func GenerateOutputPaths(outputDir, upstream string, dnssec, authDNSSEC, keepAlive bool) (csvPath, pcapPath string) {
proto := DetectProtocol(upstream) proto := DetectProtocol(upstream)
cleanServer := cleanServerName(upstream) cleanServer := cleanServerName(upstream)
// Create date-based subdirectory subDir := filepath.Join(outputDir, cleanServer)
date := time.Now().Format("2006-01-02")
timestamp := time.Now().Format("150405")
// Organize hierarchically: resolver/date/filename
subDir := filepath.Join(outputDir, cleanServer, date)
// Create simple filename
base := proto base := proto
// Add flags if enabled
var flags []string var flags []string
if dnssec { if dnssec {
if authDNSSEC { if authDNSSEC {
flags = append(flags, "auth") flags = append(flags, "auth")
@@ -39,11 +31,8 @@ func GenerateOutputPaths(outputDir, upstream string, dnssec, authDNSSEC, keepAli
base = fmt.Sprintf("%s-%s", base, strings.Join(flags, "-")) base = fmt.Sprintf("%s-%s", base, strings.Join(flags, "-"))
} }
// Add timestamp return filepath.Join(subDir, base+".csv"),
filename := fmt.Sprintf("%s-%s", base, timestamp) filepath.Join(subDir, base+".pcap")
return filepath.Join(subDir, filename+".csv"),
filepath.Join(subDir, filename+".pcap")
} }
func cleanServerName(server string) string { func cleanServerName(server string) string {