feat(outputs): use only one output file, appending on every run
This commit is contained in:
@@ -25,17 +25,28 @@ func NewPacketCapture(iface, outputPath string) (*PacketCapture, error) {
|
||||
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 {
|
||||
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)
|
||||
if err := writer.WriteFileHeader(65535, handle.LinkType()); err != nil {
|
||||
handle.Close()
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("pcap header: %w", err)
|
||||
|
||||
// Only write header if file is new
|
||||
if !fileExists {
|
||||
if err := writer.WriteFileHeader(65535, handle.LinkType()); err != nil {
|
||||
handle.Close()
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("pcap header: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &PacketCapture{
|
||||
@@ -81,20 +92,20 @@ func (pc *PacketCapture) GetError() error {
|
||||
|
||||
func (pc *PacketCapture) Close() error {
|
||||
var errs []error
|
||||
|
||||
|
||||
if pc.handle != nil {
|
||||
pc.handle.Close()
|
||||
}
|
||||
|
||||
|
||||
if pc.file != nil {
|
||||
if err := pc.file.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errs[0]
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -31,27 +31,35 @@ type MetricsWriter struct {
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, fmt.Errorf("create csv output: %w", err)
|
||||
return nil, fmt.Errorf("create/open csv output: %w", err)
|
||||
}
|
||||
|
||||
writer := csv.NewWriter(file)
|
||||
|
||||
// Write CSV header
|
||||
header := []string{
|
||||
"domain", "query_type", "protocol", "dnssec", "auth_dnssec", "keep_alive",
|
||||
"dns_server", "timestamp", "duration_ns", "duration_ms",
|
||||
"request_size_bytes", "response_size_bytes", "response_code", "error",
|
||||
}
|
||||
// Only write header if file is new
|
||||
if !fileExists {
|
||||
header := []string{
|
||||
"domain", "query_type", "protocol", "dnssec", "auth_dnssec", "keep_alive",
|
||||
"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 {
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("write csv header: %w", err)
|
||||
if err := writer.Write(header); err != nil {
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("write csv header: %w", err)
|
||||
}
|
||||
writer.Flush()
|
||||
}
|
||||
|
||||
writer.Flush()
|
||||
|
||||
return &MetricsWriter{
|
||||
writer: writer,
|
||||
file: file,
|
||||
|
||||
@@ -5,25 +5,17 @@ import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateOutputPaths(outputDir, upstream string, dnssec, authDNSSEC, keepAlive bool) (csvPath, pcapPath string) {
|
||||
proto := DetectProtocol(upstream)
|
||||
cleanServer := cleanServerName(upstream)
|
||||
|
||||
// Create date-based subdirectory
|
||||
date := time.Now().Format("2006-01-02")
|
||||
timestamp := time.Now().Format("150405")
|
||||
subDir := filepath.Join(outputDir, cleanServer)
|
||||
|
||||
// Organize hierarchically: resolver/date/filename
|
||||
subDir := filepath.Join(outputDir, cleanServer, date)
|
||||
|
||||
// Create simple filename
|
||||
base := proto
|
||||
|
||||
// Add flags if enabled
|
||||
var flags []string
|
||||
|
||||
if dnssec {
|
||||
if authDNSSEC {
|
||||
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, "-"))
|
||||
}
|
||||
|
||||
// Add timestamp
|
||||
filename := fmt.Sprintf("%s-%s", base, timestamp)
|
||||
|
||||
return filepath.Join(subDir, filename+".csv"),
|
||||
filepath.Join(subDir, filename+".pcap")
|
||||
return filepath.Join(subDir, base+".csv"),
|
||||
filepath.Join(subDir, base+".pcap")
|
||||
}
|
||||
|
||||
func cleanServerName(server string) string {
|
||||
|
||||
Reference in New Issue
Block a user