Add metrics about client bytes/messages sent/received.

This commit is contained in:
Joachim Bauch 2025-11-24 17:03:14 +01:00
commit 9ee64b8c66
No known key found for this signature in database
GPG key ID: 77C1D22D53E15F02
4 changed files with 89 additions and 2 deletions

View file

@ -26,6 +26,7 @@ import (
"context"
"encoding/json"
"errors"
"io"
"net"
"strconv"
"strings"
@ -344,6 +345,7 @@ func (c *Client) ReadPump() {
if msg == "" {
return nil
}
statsClientBytesTotal.WithLabelValues("incoming").Add(float64(len(msg)))
if ts, err := strconv.ParseInt(msg, 10, 64); err == nil {
rtt := now.Sub(time.Unix(0, ts))
if c.logRTT {
@ -406,6 +408,8 @@ func (c *Client) ReadPump() {
break
}
statsClientBytesTotal.WithLabelValues("incoming").Add(float64(decodeBuffer.Len()))
statsClientMessagesTotal.WithLabelValues("incoming").Inc()
c.messageChan <- decodeBuffer
}
}
@ -425,16 +429,33 @@ func (c *Client) processMessages() {
c.doClose()
}
type counterWriter struct {
w io.Writer
counter *int
}
func (w *counterWriter) Write(p []byte) (int, error) {
written, err := w.w.Write(p)
if written > 0 {
*w.counter += written
}
return written, err
}
func (c *Client) writeInternal(message json.Marshaler) bool {
var closeData []byte
c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint
writer, err := c.conn.NextWriter(websocket.TextMessage)
var written int
if err == nil {
if m, ok := (any(message)).(easyjson.Marshaler); ok {
_, err = easyjson.MarshalToWriter(m, writer)
written, err = easyjson.MarshalToWriter(m, writer)
} else {
err = json.NewEncoder(writer).Encode(message)
err = json.NewEncoder(&counterWriter{
w: writer,
counter: &written,
}).Encode(message)
}
}
if err == nil {
@ -454,6 +475,9 @@ func (c *Client) writeInternal(message json.Marshaler) bool {
closeData = websocket.FormatCloseMessage(websocket.CloseInternalServerErr, "")
goto close
}
statsClientBytesTotal.WithLabelValues("outgoing").Add(float64(written))
statsClientMessagesTotal.WithLabelValues("outgoing").Inc()
return true
close:
@ -542,6 +566,7 @@ func (c *Client) sendPing() bool {
return false
}
statsClientBytesTotal.WithLabelValues("outgoing").Add(float64(len(msg)))
return true
}

View file

@ -39,10 +39,24 @@ var (
Help: "The roundtrip time of WebSocket ping messages in milliseconds",
Buckets: prometheus.ExponentialBucketsRange(1, 30000, 50),
})
statsClientBytesTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: "signaling",
Subsystem: "client",
Name: "bytes_total",
Help: "The total number of bytes sent to or received by clients",
}, []string{"direction"})
statsClientMessagesTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: "signaling",
Subsystem: "client",
Name: "messages_total",
Help: "The total number of messages sent to or received by clients",
}, []string{"direction"})
clientStats = []prometheus.Collector{
statsClientCountries,
statsClientRTT,
statsClientBytesTotal,
statsClientMessagesTotal,
}
)

46
client_test.go Normal file
View file

@ -0,0 +1,46 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2025 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCounterWriter(t *testing.T) {
assert := assert.New(t)
var b bytes.Buffer
var written int
w := &counterWriter{
w: &b,
counter: &written,
}
if count, err := w.Write(nil); assert.NoError(err) && assert.EqualValues(0, count) {
assert.EqualValues(0, written)
}
if count, err := w.Write([]byte("foo")); assert.NoError(err) && assert.EqualValues(3, count) {
assert.EqualValues(3, written)
}
}

View file

@ -72,3 +72,5 @@ The following metrics are available:
| `signaling_mcu_media_retransmissions_total` | Counter | 2.0.5 | The total number of received retransmissions | `media` |
| `signaling_mcu_media_bytes_total` | Counter | 2.0.5 | The total number of media bytes sent / received | `media`, `direction` |
| `signaling_mcu_media_lost_total` | Counter | 2.0.5 | The total number of lost media packets | `media`, `origin` |
| `signaling_client_bytes_total` | Counter | 2.0.5 | The total number of bytes sent to or received by clients | `direction` |
| `signaling_client_messages_total` | Counter | 2.0.5 | The total number of messages sent to or received by clients | `direction` |