feat(csv): change to csv from jsonl

This commit is contained in:
2025-09-27 21:59:53 +01:00
parent 2240d18f0b
commit 65bca470eb
3 changed files with 55 additions and 14 deletions

View File

@@ -75,7 +75,7 @@ func (r *MeasurementRunner) runMeasurement(upstream string, domains []string, qT
defer dnsClient.Close() defer dnsClient.Close()
// Setup output files // Setup output files
jsonPath, pcapPath := GenerateOutputPaths(r.config.OutputDir, upstream, r.config.DNSSEC, r.config.KeepAlive) csvPath, pcapPath := GenerateOutputPaths(r.config.OutputDir, upstream, r.config.DNSSEC, r.config.KeepAlive)
keepAliveStr := "" keepAliveStr := ""
if r.config.KeepAlive { if r.config.KeepAlive {
@@ -83,7 +83,7 @@ func (r *MeasurementRunner) runMeasurement(upstream string, domains []string, qT
} }
fmt.Printf(">>> Measuring %s (dnssec=%v%s) → %s\n", upstream, r.config.DNSSEC, keepAliveStr, fmt.Printf(">>> Measuring %s (dnssec=%v%s) → %s\n", upstream, r.config.DNSSEC, keepAliveStr,
strings.TrimSuffix(strings.TrimSuffix(jsonPath, ".jsonl"), r.config.OutputDir+"/")) strings.TrimSuffix(strings.TrimSuffix(csvPath, ".csv"), r.config.OutputDir+"/"))
// Setup packet capture // Setup packet capture
packetCapture, err := capture.NewPacketCapture(r.config.Interface, pcapPath) packetCapture, err := capture.NewPacketCapture(r.config.Interface, pcapPath)
@@ -93,7 +93,7 @@ func (r *MeasurementRunner) runMeasurement(upstream string, domains []string, qT
defer packetCapture.Close() defer packetCapture.Close()
// Setup results writer // Setup results writer
writer, err := results.NewMetricsWriter(jsonPath) writer, err := results.NewMetricsWriter(csvPath)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,9 +1,10 @@
package results package results
import ( import (
"encoding/json" "encoding/csv"
"fmt" "fmt"
"os" "os"
"strconv"
"time" "time"
) )
@@ -23,29 +24,69 @@ type DNSMetric struct {
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
} }
// Rest stays exactly the same
type MetricsWriter struct { type MetricsWriter struct {
encoder *json.Encoder writer *csv.Writer
file *os.File file *os.File
} }
func NewMetricsWriter(path string) (*MetricsWriter, error) { func NewMetricsWriter(path string) (*MetricsWriter, error) {
file, err := os.Create(path) file, err := os.Create(path)
if err != nil { if err != nil {
return nil, fmt.Errorf("create json output: %w", err) return nil, fmt.Errorf("create csv output: %w", err)
} }
writer := csv.NewWriter(file)
// Write CSV header
header := []string{
"domain", "query_type", "protocol", "dnssec", "keep_alive",
"dns_server", "timestamp", "duration_ns", "duration_ms",
"request_size_bytes", "response_size_bytes", "response_code", "error",
}
if err := writer.Write(header); err != nil {
file.Close()
return nil, fmt.Errorf("write csv header: %w", err)
}
writer.Flush()
return &MetricsWriter{ return &MetricsWriter{
encoder: json.NewEncoder(file), writer: writer,
file: file, file: file,
}, nil }, nil
} }
func (mw *MetricsWriter) WriteMetric(metric DNSMetric) error { func (mw *MetricsWriter) WriteMetric(metric DNSMetric) error {
return mw.encoder.Encode(metric) record := []string{
metric.Domain,
metric.QueryType,
metric.Protocol,
strconv.FormatBool(metric.DNSSEC),
strconv.FormatBool(metric.KeepAlive),
metric.DNSServer,
metric.Timestamp.Format(time.RFC3339),
strconv.FormatInt(metric.Duration, 10),
strconv.FormatFloat(metric.DurationMs, 'f', 3, 64),
strconv.Itoa(metric.RequestSize),
strconv.Itoa(metric.ResponseSize),
metric.ResponseCode,
metric.Error,
}
err := mw.writer.Write(record)
if err != nil {
return err
}
mw.writer.Flush()
return mw.writer.Error()
} }
func (mw *MetricsWriter) Close() error { func (mw *MetricsWriter) Close() error {
if mw.writer != nil {
mw.writer.Flush()
}
if mw.file != nil { if mw.file != nil {
return mw.file.Close() return mw.file.Close()
} }

View File

@@ -8,7 +8,7 @@ import (
"time" "time"
) )
func GenerateOutputPaths(outputDir, upstream string, dnssec, keepAlive bool) (jsonPath, pcapPath string) { func GenerateOutputPaths(outputDir, upstream string, dnssec, keepAlive bool) (csvPath, pcapPath string) {
proto := DetectProtocol(upstream) proto := DetectProtocol(upstream)
serverName := ExtractServerName(upstream) serverName := ExtractServerName(upstream)
ts := time.Now().Format("20060102_1504") ts := time.Now().Format("20060102_1504")
@@ -18,8 +18,8 @@ func GenerateOutputPaths(outputDir, upstream string, dnssec, keepAlive bool) (js
base := fmt.Sprintf("%s_%s_dnssec_%s_keepalive_%s_%s", base := fmt.Sprintf("%s_%s_dnssec_%s_keepalive_%s_%s",
proto, sanitize(serverName), dnssecStr, keepAliveStr, ts) proto, sanitize(serverName), dnssecStr, keepAliveStr, ts)
return filepath.Join(outputDir, base+".jsonl"), return filepath.Join(outputDir, base+".csv"),
filepath.Join(outputDir, base+".pcap") filepath.Join(outputDir, base+".pcap")
} }
func sanitize(s string) string { func sanitize(s string) string {