From a00aeb2eb5a709d0ec820df1cd8769b66d03dd1d Mon Sep 17 00:00:00 2001 From: Cody Lee Date: Thu, 11 Dec 2025 10:55:33 -0600 Subject: [PATCH] Add byte counters for InfluxDB and Prometheus outputs (issue #350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track the number of bytes written per request for both InfluxDB and Prometheus outputs. InfluxDB: - Added bytesT counter constant - Implemented calculateMetricBytes() to estimate line protocol size - Updated batchV1() and batchV2() to count bytes per point - Updated log output to display bytes written Prometheus: - Added Bytes field to Report struct - Updated export() to calculate approximate metric byte size - Updated log output to display bytes written 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- pkg/influxunifi/report.go | 27 +++++++++++++++++++++++++-- pkg/promunifi/collector.go | 1 + pkg/promunifi/report.go | 14 ++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/pkg/influxunifi/report.go b/pkg/influxunifi/report.go index c351b65e..893c5309 100644 --- a/pkg/influxunifi/report.go +++ b/pkg/influxunifi/report.go @@ -94,17 +94,40 @@ func (r *Report) error(err error) { const ( pointT = item("Point") fieldT = item("Fields") + bytesT = item("Bytes") ) +// calculateMetricBytes estimates the size of a metric in InfluxDB line protocol format. +// Format: measurement,tag1=value1,tag2=value2 field1=value1,field2=value2 timestamp +func calculateMetricBytes(m *metric) int { + bytes := len(m.Table) // measurement name + + // Add tags + for k, v := range m.Tags { + bytes += len(k) + len(v) + 2 // tag key + tag value + '=' and ',' + } + + // Add fields + for k, v := range m.Fields { + bytes += len(k) + len(fmt.Sprint(v)) + 2 // field key + field value + '=' and ',' + } + + bytes += 20 // approximate size for timestamp and separators + + return bytes +} + func (r *Report) batchV1(m *metric, p *influxV1.Point) { r.addCount(pointT) r.addCount(fieldT, len(m.Fields)) + r.addCount(bytesT, calculateMetricBytes(m)) r.bp.AddPoint(p) } func (r *Report) batchV2(m *metric, p *influxV2Write.Point) { r.addCount(pointT) r.addCount(fieldT, len(m.Fields)) + r.addCount(bytesT, calculateMetricBytes(m)) r.writer.WritePoint(p) } @@ -116,10 +139,10 @@ func (r *Report) String() string { return fmt.Sprintf("Site: %d, Client: %d, "+ "Gateways: %d, %s: %d, %s: %d, %s/%s/%s/%s: %d/%d/%d/%d, "+ - "DPI Site/Client: %d/%d, %s: %d, %s: %d, Err: %d, Dur: %v", + "DPI Site/Client: %d/%d, %s: %d, %s: %d, %s: %d, Err: %d, Dur: %v", len(m.Sites), len(m.Clients), c[udmT]+c[usgT]+c[uxgT]+c[uciT]+c[ubbT], uapT, c[uapT], uswT, c[uswT], idsT, eventT, alarmT, anomalyT, c[idsT], c[eventT], c[alarmT], c[anomalyT], len(m.SitesDPI), len(m.ClientsDPI), pointT, c[pointT], fieldT, c[fieldT], - len(r.Errors), r.Elapsed.Round(time.Millisecond)) + bytesT, c[bytesT], len(r.Errors), r.Elapsed.Round(time.Millisecond)) } diff --git a/pkg/promunifi/collector.go b/pkg/promunifi/collector.go index 7db0ff67..5a6c4c32 100644 --- a/pkg/promunifi/collector.go +++ b/pkg/promunifi/collector.go @@ -87,6 +87,7 @@ type Report struct { Total int // Total count of metrics recorded. Errors int // Total count of errors recording metrics. Zeros int // Total count of metrics equal to zero. + Bytes int // Total count of bytes written. USG int // Total count of USG devices. USW int // Total count of USW devices. PDU int // Total count of PDU devices. diff --git a/pkg/promunifi/report.go b/pkg/promunifi/report.go index cf788e11..8663ce12 100644 --- a/pkg/promunifi/report.go +++ b/pkg/promunifi/report.go @@ -51,9 +51,9 @@ func (r *Report) report(c poller.Logger, descs map[*prometheus.Desc]bool) { c.Logf("UniFi Measurements Exported. Site: %d, Client: %d, "+ "UAP: %d, USG/UDM: %d, USW: %d, DPI Site/Client: %d/%d, Desc: %d, "+ - "Metric: %d, Err: %d, 0s: %d, Req/Total: %v / %v", + "Metric: %d, Bytes: %d, Err: %d, 0s: %d, Req/Total: %v / %v", len(m.Sites), len(m.Clients), r.UAP, r.UDM+r.USG+r.UXG, r.USW, len(m.SitesDPI), - len(m.ClientsDPI), len(descs), r.Total, r.Errors, r.Zeros, + len(m.ClientsDPI), len(descs), r.Total, r.Bytes, r.Errors, r.Zeros, r.Fetch.Round(time.Millisecond/oneDecimalPoint), r.Elapsed.Round(time.Millisecond/oneDecimalPoint)) } @@ -65,6 +65,16 @@ func (r *Report) export(m *metric, v float64) prometheus.Metric { r.Zeros++ } + // Calculate approximate byte size of the metric in Prometheus text format. + // Format: metric_name{label1="value1",label2="value2"} value timestamp + bytes := len(m.Desc.String()) // Metric name with label names + for _, label := range m.Labels { + bytes += len(label) + 3 // label value + quotes and comma/equals overhead + } + bytes += 20 // approximate size for value and timestamp + + r.Bytes += bytes + return prometheus.MustNewConstMetric(m.Desc, m.ValueType, v, m.Labels...) }