Skip to content

Commit ca2d6ea

Browse files
committed
Add metrics for backend client requests.
1 parent 1212c60 commit ca2d6ea

File tree

2 files changed

+86
-4
lines changed

2 files changed

+86
-4
lines changed

backend_client.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"net/http"
3131
"net/url"
3232
"strings"
33+
"time"
3334

3435
"github.com/dlintw/goconf"
3536
)
@@ -42,6 +43,10 @@ var (
4243
ErrThrottledResponse = errors.New("throttled OCS response")
4344
)
4445

46+
func init() {
47+
RegisterBackendClientStats()
48+
}
49+
4550
type BackendClient struct {
4651
hub *Hub
4752
version string
@@ -117,9 +122,9 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ
117122
return fmt.Errorf("no url passed to perform JSON request %+v", request)
118123
}
119124

120-
secret := b.backends.GetSecret(u)
121-
if secret == nil {
122-
return fmt.Errorf("no backend secret configured for for %s", u)
125+
backend := b.backends.GetBackend(u)
126+
if backend == nil {
127+
return fmt.Errorf("no backend configured for %s", u)
123128
}
124129

125130
var requestUrl *url.URL
@@ -160,10 +165,22 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ
160165
}
161166

162167
// Add checksum so the backend can validate the request.
163-
AddBackendChecksum(req, data.Bytes(), secret)
168+
AddBackendChecksum(req, data.Bytes(), backend.Secret())
164169

170+
start := time.Now()
165171
resp, err := c.Do(req)
172+
end := time.Now()
173+
duration := end.Sub(start)
174+
statsBackendClientRequests.WithLabelValues(backend.Url()).Inc()
175+
statsBackendClientDuration.WithLabelValues(backend.Url()).Observe(duration.Seconds())
166176
if err != nil {
177+
if errors.Is(err, context.DeadlineExceeded) {
178+
statsBackendClientError.WithLabelValues(backend.Url(), "timeout").Inc()
179+
} else if errors.Is(err, context.Canceled) {
180+
statsBackendClientError.WithLabelValues(backend.Url(), "canceled").Inc()
181+
} else {
182+
statsBackendClientError.WithLabelValues(backend.Url(), "unknown").Inc()
183+
}
167184
log.Printf("Could not send request %s to %s: %s", data.String(), req.URL, err)
168185
return err
169186
}
@@ -172,12 +189,14 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ
172189
ct := resp.Header.Get("Content-Type")
173190
if !strings.HasPrefix(ct, "application/json") {
174191
log.Printf("Received unsupported content-type from %s: %s (%s)", req.URL, ct, resp.Status)
192+
statsBackendClientError.WithLabelValues(backend.Url(), "invalid_content_type").Inc()
175193
return ErrUnsupportedContentType
176194
}
177195

178196
body, err := b.buffers.ReadAll(resp.Body)
179197
if err != nil {
180198
log.Printf("Could not read response body from %s: %s", req.URL, err)
199+
statsBackendClientError.WithLabelValues(backend.Url(), "error_reading_body").Inc()
181200
return err
182201
}
183202

@@ -195,24 +214,29 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ
195214
var ocs OcsResponse
196215
if err := json.Unmarshal(body.Bytes(), &ocs); err != nil {
197216
log.Printf("Could not decode OCS response %s from %s: %s", body.String(), req.URL, err)
217+
statsBackendClientError.WithLabelValues(backend.Url(), "error_decoding_ocs").Inc()
198218
return err
199219
} else if ocs.Ocs == nil || len(ocs.Ocs.Data) == 0 {
200220
log.Printf("Incomplete OCS response %s from %s", body.String(), req.URL)
221+
statsBackendClientError.WithLabelValues(backend.Url(), "error_incomplete_ocs").Inc()
201222
return ErrIncompleteResponse
202223
}
203224

204225
switch ocs.Ocs.Meta.StatusCode {
205226
case http.StatusTooManyRequests:
206227
log.Printf("Throttled OCS response %s from %s", body.String(), req.URL)
228+
statsBackendClientError.WithLabelValues(backend.Url(), "throttled").Inc()
207229
return ErrThrottledResponse
208230
}
209231

210232
if err := json.Unmarshal(ocs.Ocs.Data, response); err != nil {
211233
log.Printf("Could not decode OCS response body %s from %s: %s", string(ocs.Ocs.Data), req.URL, err)
234+
statsBackendClientError.WithLabelValues(backend.Url(), "error_decoding_ocs_data").Inc()
212235
return err
213236
}
214237
} else if err := json.Unmarshal(body.Bytes(), response); err != nil {
215238
log.Printf("Could not decode response body %s from %s: %s", body.String(), req.URL, err)
239+
statsBackendClientError.WithLabelValues(backend.Url(), "error_decoding_body").Inc()
216240
return err
217241
}
218242
return nil

backend_client_stats_prometheus.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Standalone signaling server for the Nextcloud Spreed app.
3+
* Copyright (C) 2025 struktur AG
4+
*
5+
* @author Joachim Bauch <[email protected]>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*/
22+
package signaling
23+
24+
import (
25+
"github.com/prometheus/client_golang/prometheus"
26+
)
27+
28+
var (
29+
statsBackendClientRequests = prometheus.NewCounterVec(prometheus.CounterOpts{
30+
Namespace: "signaling",
31+
Subsystem: "backend_client",
32+
Name: "requests_total",
33+
Help: "The total number of backend client requests",
34+
}, []string{"backend"})
35+
statsBackendClientDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
36+
Namespace: "signaling",
37+
Subsystem: "backend_client",
38+
Name: "requests_duration",
39+
Help: "The duration of backend client requests in seconds",
40+
Buckets: prometheus.ExponentialBucketsRange(0.01, 30, 30),
41+
}, []string{"backend"})
42+
statsBackendClientError = prometheus.NewCounterVec(prometheus.CounterOpts{
43+
Namespace: "signaling",
44+
Subsystem: "backend_client",
45+
Name: "requests_errors_total",
46+
Help: "The total number of backend client requests that had an error",
47+
}, []string{"backend", "error"})
48+
49+
backendClientStats = []prometheus.Collector{
50+
statsBackendClientRequests,
51+
statsBackendClientDuration,
52+
statsBackendClientError,
53+
}
54+
)
55+
56+
func RegisterBackendClientStats() {
57+
registerAll(backendClientStats...)
58+
}

0 commit comments

Comments
 (0)