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)
|
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{
|
||||||
@@ -81,20 +92,20 @@ func (pc *PacketCapture) GetError() error {
|
|||||||
|
|
||||||
func (pc *PacketCapture) Close() error {
|
func (pc *PacketCapture) Close() error {
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
if pc.handle != nil {
|
if pc.handle != nil {
|
||||||
pc.handle.Close()
|
pc.handle.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if pc.file != nil {
|
if pc.file != nil {
|
||||||
if err := pc.file.Close(); err != nil {
|
if err := pc.file.Close(); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return errs[0]
|
return errs[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user