mirror of
https://github.com/strukturag/nextcloud-spreed-signaling
synced 2024-06-04 23:12:29 +02:00
Add more metrics on rooms / sessions / backends.
This commit is contained in:
parent
5d431e5612
commit
ce1b3fc6e2
|
@ -90,6 +90,7 @@ func (b *Backend) AddSession(session Session) error {
|
|||
if b.sessions == nil {
|
||||
b.sessions = make(map[string]bool)
|
||||
} else if uint64(len(b.sessions)) >= b.sessionLimit {
|
||||
statsBackendLimitExceededTotal.WithLabelValues(b.id).Inc()
|
||||
return SessionLimitExceeded
|
||||
}
|
||||
|
||||
|
@ -123,6 +124,7 @@ func NewBackendConfiguration(config *goconf.ConfigFile) (*BackendConfiguration,
|
|||
}
|
||||
backends := make(map[string][]*Backend)
|
||||
var compatBackend *Backend
|
||||
numBackends := 0
|
||||
if allowAll {
|
||||
log.Println("WARNING: All backend hostnames are allowed, only use for development!")
|
||||
compatBackend = &Backend{
|
||||
|
@ -137,12 +139,14 @@ func NewBackendConfiguration(config *goconf.ConfigFile) (*BackendConfiguration,
|
|||
if sessionLimit > 0 {
|
||||
log.Printf("Allow a maximum of %d sessions", sessionLimit)
|
||||
}
|
||||
numBackends += 1
|
||||
} else if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" {
|
||||
for host, configuredBackends := range getConfiguredHosts(backendIds, config) {
|
||||
backends[host] = append(backends[host], configuredBackends...)
|
||||
for _, be := range configuredBackends {
|
||||
log.Printf("Backend %s added for %s", be.id, be.url)
|
||||
}
|
||||
numBackends += len(configuredBackends)
|
||||
}
|
||||
} else if allowedUrls, _ := config.GetString("backend", "allowed"); allowedUrls != "" {
|
||||
// Old-style configuration, only hosts are configured and are using a common secret.
|
||||
|
@ -182,9 +186,14 @@ func NewBackendConfiguration(config *goconf.ConfigFile) (*BackendConfiguration,
|
|||
if sessionLimit > 0 {
|
||||
log.Printf("Allow a maximum of %d sessions", sessionLimit)
|
||||
}
|
||||
numBackends += 1
|
||||
}
|
||||
}
|
||||
|
||||
RegisterBackendConfigurationStats()
|
||||
log.Printf("Initial: %d", numBackends)
|
||||
statsBackendsCurrent.Add(float64(numBackends))
|
||||
|
||||
return &BackendConfiguration{
|
||||
backends: backends,
|
||||
|
||||
|
@ -199,6 +208,7 @@ func (b *BackendConfiguration) RemoveBackendsForHost(host string) {
|
|||
for _, backend := range oldBackends {
|
||||
log.Printf("Backend %s removed for %s", backend.id, backend.url)
|
||||
}
|
||||
statsBackendsCurrent.Sub(float64(len(oldBackends)))
|
||||
}
|
||||
delete(b.backends, host)
|
||||
}
|
||||
|
@ -225,6 +235,7 @@ func (b *BackendConfiguration) UpsertHost(host string, backends []*Backend) {
|
|||
removed := b.backends[host][existingIndex]
|
||||
log.Printf("Backend %s removed for %s", removed.id, removed.url)
|
||||
b.backends[host] = append(b.backends[host][:existingIndex], b.backends[host][existingIndex+1:]...)
|
||||
statsBackendsCurrent.Dec()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,6 +243,7 @@ func (b *BackendConfiguration) UpsertHost(host string, backends []*Backend) {
|
|||
for _, added := range backends {
|
||||
log.Printf("Backend %s added for %s", added.id, added.url)
|
||||
}
|
||||
statsBackendsCurrent.Add(float64(len(backends)))
|
||||
}
|
||||
|
||||
func getConfiguredBackendIDs(backendIds string) (ids []string) {
|
||||
|
|
50
backend_configuration_stats_prometheus.go
Normal file
50
backend_configuration_stats_prometheus.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* Standalone signaling server for the Nextcloud Spreed app.
|
||||
* Copyright (C) 2021 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 (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
statsBackendLimitExceededTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "backend",
|
||||
Name: "session_limit_exceeded_total",
|
||||
Help: "The number of times the session limit exceeded",
|
||||
}, []string{"backend"})
|
||||
statsBackendsCurrent = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "backend",
|
||||
Name: "current",
|
||||
Help: "The current number of configured backends",
|
||||
})
|
||||
|
||||
backendConfigurationStats = []prometheus.Collector{
|
||||
statsBackendLimitExceededTotal,
|
||||
statsBackendsCurrent,
|
||||
}
|
||||
)
|
||||
|
||||
func RegisterBackendConfigurationStats() {
|
||||
registerAll(backendConfigurationStats...)
|
||||
}
|
|
@ -28,6 +28,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/dlintw/goconf"
|
||||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||
)
|
||||
|
||||
func testUrls(t *testing.T, config *BackendConfiguration, valid_urls []string, invalid_urls []string) {
|
||||
|
@ -238,6 +239,7 @@ func TestParseBackendIds(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBackendReloadNoChange(t *testing.T) {
|
||||
current := testutil.ToFloat64(statsBackendsCurrent)
|
||||
original_config := goconf.NewConfigFile()
|
||||
original_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
original_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -249,6 +251,7 @@ func TestBackendReloadNoChange(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkStatsValue(t, statsBackendsCurrent, current+2)
|
||||
|
||||
new_config := goconf.NewConfigFile()
|
||||
new_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
|
@ -262,13 +265,16 @@ func TestBackendReloadNoChange(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+4)
|
||||
o_cfg.Reload(original_config)
|
||||
checkStatsValue(t, statsBackendsCurrent, current+4)
|
||||
if !reflect.DeepEqual(n_cfg, o_cfg) {
|
||||
t.Error("BackendConfiguration should be equal after Reload")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendReloadChangeExistingURL(t *testing.T) {
|
||||
current := testutil.ToFloat64(statsBackendsCurrent)
|
||||
original_config := goconf.NewConfigFile()
|
||||
original_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
original_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -281,6 +287,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+2)
|
||||
new_config := goconf.NewConfigFile()
|
||||
new_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
new_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -294,17 +301,20 @@ func TestBackendReloadChangeExistingURL(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+4)
|
||||
original_config.RemoveOption("backend1", "url")
|
||||
original_config.AddOption("backend1", "url", "http://domain3.invalid")
|
||||
original_config.AddOption("backend1", "sessionlimit", "10")
|
||||
|
||||
o_cfg.Reload(original_config)
|
||||
checkStatsValue(t, statsBackendsCurrent, current+4)
|
||||
if !reflect.DeepEqual(n_cfg, o_cfg) {
|
||||
t.Error("BackendConfiguration should be equal after Reload")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendReloadChangeSecret(t *testing.T) {
|
||||
current := testutil.ToFloat64(statsBackendsCurrent)
|
||||
original_config := goconf.NewConfigFile()
|
||||
original_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
original_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -317,6 +327,7 @@ func TestBackendReloadChangeSecret(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+2)
|
||||
new_config := goconf.NewConfigFile()
|
||||
new_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
new_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -329,16 +340,19 @@ func TestBackendReloadChangeSecret(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+4)
|
||||
original_config.RemoveOption("backend1", "secret")
|
||||
original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend3")
|
||||
|
||||
o_cfg.Reload(original_config)
|
||||
checkStatsValue(t, statsBackendsCurrent, current+4)
|
||||
if !reflect.DeepEqual(n_cfg, o_cfg) {
|
||||
t.Error("BackendConfiguration should be equal after Reload")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendReloadAddBackend(t *testing.T) {
|
||||
current := testutil.ToFloat64(statsBackendsCurrent)
|
||||
original_config := goconf.NewConfigFile()
|
||||
original_config.AddOption("backend", "backends", "backend1")
|
||||
original_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -349,6 +363,7 @@ func TestBackendReloadAddBackend(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+1)
|
||||
new_config := goconf.NewConfigFile()
|
||||
new_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
new_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -362,6 +377,7 @@ func TestBackendReloadAddBackend(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+3)
|
||||
original_config.RemoveOption("backend", "backends")
|
||||
original_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
original_config.AddOption("backend2", "url", "http://domain2.invalid")
|
||||
|
@ -369,12 +385,14 @@ func TestBackendReloadAddBackend(t *testing.T) {
|
|||
original_config.AddOption("backend2", "sessionlimit", "10")
|
||||
|
||||
o_cfg.Reload(original_config)
|
||||
checkStatsValue(t, statsBackendsCurrent, current+4)
|
||||
if !reflect.DeepEqual(n_cfg, o_cfg) {
|
||||
t.Error("BackendConfiguration should be equal after Reload")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendReloadRemoveHost(t *testing.T) {
|
||||
current := testutil.ToFloat64(statsBackendsCurrent)
|
||||
original_config := goconf.NewConfigFile()
|
||||
original_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
original_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -387,6 +405,7 @@ func TestBackendReloadRemoveHost(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+2)
|
||||
new_config := goconf.NewConfigFile()
|
||||
new_config.AddOption("backend", "backends", "backend1")
|
||||
new_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -397,17 +416,20 @@ func TestBackendReloadRemoveHost(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+3)
|
||||
original_config.RemoveOption("backend", "backends")
|
||||
original_config.AddOption("backend", "backends", "backend1")
|
||||
original_config.RemoveSection("backend2")
|
||||
|
||||
o_cfg.Reload(original_config)
|
||||
checkStatsValue(t, statsBackendsCurrent, current+2)
|
||||
if !reflect.DeepEqual(n_cfg, o_cfg) {
|
||||
t.Error("BackendConfiguration should be equal after Reload")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
|
||||
current := testutil.ToFloat64(statsBackendsCurrent)
|
||||
original_config := goconf.NewConfigFile()
|
||||
original_config.AddOption("backend", "backends", "backend1, backend2")
|
||||
original_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -420,6 +442,7 @@ func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+2)
|
||||
new_config := goconf.NewConfigFile()
|
||||
new_config.AddOption("backend", "backends", "backend1")
|
||||
new_config.AddOption("backend", "allowall", "false")
|
||||
|
@ -430,11 +453,13 @@ func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkStatsValue(t, statsBackendsCurrent, current+3)
|
||||
original_config.RemoveOption("backend", "backends")
|
||||
original_config.AddOption("backend", "backends", "backend1")
|
||||
original_config.RemoveSection("backend2")
|
||||
|
||||
o_cfg.Reload(original_config)
|
||||
checkStatsValue(t, statsBackendsCurrent, current+2)
|
||||
if !reflect.DeepEqual(n_cfg, o_cfg) {
|
||||
t.Error("BackendConfiguration should be equal after Reload")
|
||||
}
|
||||
|
|
|
@ -58,6 +58,10 @@ var (
|
|||
unknownCountry string = "unknown-country"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterClientStats()
|
||||
}
|
||||
|
||||
func IsValidCountry(country string) bool {
|
||||
switch country {
|
||||
case "":
|
||||
|
|
43
client_stats_prometheus.go
Normal file
43
client_stats_prometheus.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Standalone signaling server for the Nextcloud Spreed app.
|
||||
* Copyright (C) 2021 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 (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
statsClientCountries = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "client",
|
||||
Name: "countries_total",
|
||||
Help: "The total number of connections by country",
|
||||
}, []string{"country"})
|
||||
|
||||
clientStats = []prometheus.Collector{
|
||||
statsClientCountries,
|
||||
}
|
||||
)
|
||||
|
||||
func RegisterClientStats() {
|
||||
registerAll(clientStats...)
|
||||
}
|
24
hub.go
24
hub.go
|
@ -95,6 +95,10 @@ const (
|
|||
publicSessionName = "public-session"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterHubStats()
|
||||
}
|
||||
|
||||
type Hub struct {
|
||||
// 64-bit members that are accessed atomically must be 64-bit aligned.
|
||||
sid uint64
|
||||
|
@ -607,6 +611,7 @@ func (h *Hub) removeSession(session Session) (removed bool) {
|
|||
delete(h.clients, data.Sid)
|
||||
if _, found := h.sessions[data.Sid]; found {
|
||||
delete(h.sessions, data.Sid)
|
||||
statsHubSessionsCurrent.WithLabelValues(session.Backend().Id(), session.ClientType()).Dec()
|
||||
removed = true
|
||||
}
|
||||
}
|
||||
|
@ -742,6 +747,12 @@ func (h *Hub) processRegister(client *Client, message *ClientMessage, backend *B
|
|||
}
|
||||
h.mu.Unlock()
|
||||
|
||||
if country := client.Country(); IsValidCountry(country) {
|
||||
statsClientCountries.WithLabelValues(country).Inc()
|
||||
}
|
||||
statsHubSessionsCurrent.WithLabelValues(backend.Id(), session.ClientType()).Inc()
|
||||
statsHubSessionsTotal.WithLabelValues(backend.Id(), session.ClientType()).Inc()
|
||||
|
||||
h.setDecodedSessionId(privateSessionId, privateSessionName, sessionIdData)
|
||||
h.setDecodedSessionId(publicSessionId, publicSessionName, sessionIdData)
|
||||
h.sendHelloResponse(session, message)
|
||||
|
@ -840,6 +851,7 @@ func (h *Hub) processHello(client *Client, message *ClientMessage) {
|
|||
if resumeId != "" {
|
||||
data := h.decodeSessionId(resumeId, privateSessionName)
|
||||
if data == nil {
|
||||
statsHubSessionResumeFailed.Inc()
|
||||
client.SendMessage(message.NewErrorServerMessage(NoSuchSession))
|
||||
return
|
||||
}
|
||||
|
@ -848,6 +860,7 @@ func (h *Hub) processHello(client *Client, message *ClientMessage) {
|
|||
session, found := h.sessions[data.Sid]
|
||||
if !found || resumeId != session.PrivateId() {
|
||||
h.mu.Unlock()
|
||||
statsHubSessionResumeFailed.Inc()
|
||||
client.SendMessage(message.NewErrorServerMessage(NoSuchSession))
|
||||
return
|
||||
}
|
||||
|
@ -857,6 +870,7 @@ func (h *Hub) processHello(client *Client, message *ClientMessage) {
|
|||
// Should never happen as clients only can resume their own sessions.
|
||||
h.mu.Unlock()
|
||||
log.Printf("Client resumed non-client session %s (private=%s)", session.PublicId(), session.PrivateId())
|
||||
statsHubSessionResumeFailed.Inc()
|
||||
client.SendMessage(message.NewErrorServerMessage(NoSuchSession))
|
||||
return
|
||||
}
|
||||
|
@ -879,6 +893,7 @@ func (h *Hub) processHello(client *Client, message *ClientMessage) {
|
|||
|
||||
log.Printf("Resume session from %s in %s (%s) %s (private=%s)", client.RemoteAddr(), client.Country(), client.UserAgent(), session.PublicId(), session.PrivateId())
|
||||
|
||||
statsHubSessionsResumedTotal.WithLabelValues(clientSession.Backend().Id(), clientSession.ClientType()).Inc()
|
||||
h.sendHelloResponse(clientSession, message)
|
||||
clientSession.NotifySessionResumed(client)
|
||||
return
|
||||
|
@ -1087,7 +1102,10 @@ func (h *Hub) getRoomForBackend(id string, backend *Backend) *Room {
|
|||
func (h *Hub) removeRoom(room *Room) {
|
||||
internalRoomId := getRoomIdForBackend(room.Id(), room.Backend())
|
||||
h.ru.Lock()
|
||||
delete(h.rooms, internalRoomId)
|
||||
if _, found := h.rooms[internalRoomId]; found {
|
||||
delete(h.rooms, internalRoomId)
|
||||
statsHubRoomsCurrent.WithLabelValues(room.Backend().Id()).Dec()
|
||||
}
|
||||
h.ru.Unlock()
|
||||
}
|
||||
|
||||
|
@ -1100,6 +1118,7 @@ func (h *Hub) createRoom(id string, properties *json.RawMessage, backend *Backen
|
|||
|
||||
internalRoomId := getRoomIdForBackend(id, backend)
|
||||
h.rooms[internalRoomId] = room
|
||||
statsHubRoomsCurrent.WithLabelValues(backend.Id()).Inc()
|
||||
return room, nil
|
||||
}
|
||||
|
||||
|
@ -1550,6 +1569,8 @@ func (h *Hub) processInternalMsg(client *Client, message *ClientMessage) {
|
|||
h.sessions[sessionIdData.Sid] = sess
|
||||
h.virtualSessions[virtualSessionId] = sessionIdData.Sid
|
||||
h.mu.Unlock()
|
||||
statsHubSessionsCurrent.WithLabelValues(session.Backend().Id(), sess.ClientType()).Inc()
|
||||
statsHubSessionsTotal.WithLabelValues(session.Backend().Id(), sess.ClientType()).Inc()
|
||||
log.Printf("Session %s added virtual session %s with initial flags %d", session.PublicId(), sess.PublicId(), sess.Flags())
|
||||
session.AddVirtualSession(sess)
|
||||
sess.SetRoom(room)
|
||||
|
@ -1608,6 +1629,7 @@ func (h *Hub) processInternalMsg(client *Client, message *ClientMessage) {
|
|||
h.mu.Unlock()
|
||||
if sess != nil {
|
||||
log.Printf("Session %s removed virtual session %s", session.PublicId(), sess.PublicId())
|
||||
statsHubSessionsCurrent.WithLabelValues(session.Backend().Id(), sess.ClientType()).Dec()
|
||||
if vsess, ok := sess.(*VirtualSession); ok {
|
||||
// We should always have a VirtualSession here.
|
||||
vsess.CloseWithFeedback(session, message)
|
||||
|
|
70
hub_stats_prometheus.go
Normal file
70
hub_stats_prometheus.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Standalone signaling server for the Nextcloud Spreed app.
|
||||
* Copyright (C) 2021 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 (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
statsHubRoomsCurrent = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "hub",
|
||||
Name: "rooms",
|
||||
Help: "The current number of rooms per backend",
|
||||
}, []string{"backend"})
|
||||
statsHubSessionsCurrent = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "hub",
|
||||
Name: "sessions",
|
||||
Help: "The current number of sessions per backend",
|
||||
}, []string{"backend", "clienttype"})
|
||||
statsHubSessionsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "hub",
|
||||
Name: "sessions_total",
|
||||
Help: "The total number of sessions per backend",
|
||||
}, []string{"backend", "clienttype"})
|
||||
statsHubSessionsResumedTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "hub",
|
||||
Name: "sessions_resume_total",
|
||||
Help: "The total number of resumed sessions per backend",
|
||||
}, []string{"backend", "clienttype"})
|
||||
statsHubSessionResumeFailed = prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "hub",
|
||||
Name: "sessions_resume_failed_total",
|
||||
Help: "The total number of failed session resume requests",
|
||||
})
|
||||
|
||||
hubStats = []prometheus.Collector{
|
||||
statsHubRoomsCurrent,
|
||||
statsHubSessionsCurrent,
|
||||
statsHubSessionsTotal,
|
||||
statsHubSessionResumeFailed,
|
||||
}
|
||||
)
|
||||
|
||||
func RegisterHubStats() {
|
||||
registerAll(hubStats...)
|
||||
}
|
22
room.go
22
room.go
|
@ -32,6 +32,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -47,6 +48,10 @@ var (
|
|||
updateActiveSessionsInterval = 10 * time.Second
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterRoomStats()
|
||||
}
|
||||
|
||||
type Room struct {
|
||||
id string
|
||||
hub *Hub
|
||||
|
@ -64,6 +69,8 @@ type Room struct {
|
|||
inCallSessions map[Session]bool
|
||||
roomSessionData map[string]*RoomSessionData
|
||||
|
||||
statsRoomSessionsCurrent *prometheus.GaugeVec
|
||||
|
||||
natsReceiver chan *nats.Msg
|
||||
backendSubscription NatsSubscription
|
||||
|
||||
|
@ -123,6 +130,11 @@ func NewRoom(roomId string, properties *json.RawMessage, hub *Hub, n NatsClient,
|
|||
inCallSessions: make(map[Session]bool),
|
||||
roomSessionData: make(map[string]*RoomSessionData),
|
||||
|
||||
statsRoomSessionsCurrent: statsRoomSessionsCurrent.MustCurryWith(prometheus.Labels{
|
||||
"backend": backend.Id(),
|
||||
"room": roomId,
|
||||
}),
|
||||
|
||||
natsReceiver: natsReceiver,
|
||||
backendSubscription: backendSubscription,
|
||||
|
||||
|
@ -194,6 +206,9 @@ func (r *Room) Close() []Session {
|
|||
result = append(result, s)
|
||||
}
|
||||
r.sessions = nil
|
||||
r.statsRoomSessionsCurrent.Delete(prometheus.Labels{"clienttype": HelloClientTypeClient})
|
||||
r.statsRoomSessionsCurrent.Delete(prometheus.Labels{"clienttype": HelloClientTypeInternal})
|
||||
r.statsRoomSessionsCurrent.Delete(prometheus.Labels{"clienttype": HelloClientTypeVirtual})
|
||||
r.mu.Unlock()
|
||||
return result
|
||||
}
|
||||
|
@ -264,6 +279,9 @@ func (r *Room) AddSession(session Session, sessionData *json.RawMessage) []Sessi
|
|||
}
|
||||
}
|
||||
r.sessions[sid] = session
|
||||
if !found {
|
||||
r.statsRoomSessionsCurrent.With(prometheus.Labels{"clienttype": session.ClientType()}).Inc()
|
||||
}
|
||||
var publishUsersChanged bool
|
||||
switch session.ClientType() {
|
||||
case HelloClientTypeInternal:
|
||||
|
@ -311,6 +329,7 @@ func (r *Room) RemoveSession(session Session) bool {
|
|||
}
|
||||
|
||||
sid := session.PublicId()
|
||||
r.statsRoomSessionsCurrent.With(prometheus.Labels{"clienttype": session.ClientType()}).Dec()
|
||||
delete(r.sessions, sid)
|
||||
delete(r.internalSessions, session)
|
||||
if virtualSession, ok := session.(*VirtualSession); ok {
|
||||
|
@ -325,6 +344,9 @@ func (r *Room) RemoveSession(session Session) bool {
|
|||
}
|
||||
|
||||
r.hub.removeRoom(r)
|
||||
r.statsRoomSessionsCurrent.Delete(prometheus.Labels{"clienttype": HelloClientTypeClient})
|
||||
r.statsRoomSessionsCurrent.Delete(prometheus.Labels{"clienttype": HelloClientTypeInternal})
|
||||
r.statsRoomSessionsCurrent.Delete(prometheus.Labels{"clienttype": HelloClientTypeVirtual})
|
||||
r.unsubscribeBackend()
|
||||
r.doClose()
|
||||
r.mu.Unlock()
|
||||
|
|
43
room_stats_prometheus.go
Normal file
43
room_stats_prometheus.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Standalone signaling server for the Nextcloud Spreed app.
|
||||
* Copyright (C) 2021 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 (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
statsRoomSessionsCurrent = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "signaling",
|
||||
Subsystem: "room",
|
||||
Name: "sessions",
|
||||
Help: "The current number of sessions in a room",
|
||||
}, []string{"backend", "room", "clienttype"})
|
||||
|
||||
roomStats = []prometheus.Collector{
|
||||
statsRoomSessionsCurrent,
|
||||
}
|
||||
)
|
||||
|
||||
func RegisterRoomStats() {
|
||||
registerAll(roomStats...)
|
||||
}
|
|
@ -22,6 +22,9 @@
|
|||
package signaling
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
@ -34,7 +37,27 @@ func checkStatsValue(t *testing.T, collector prometheus.Collector, value float64
|
|||
desc := <-ch
|
||||
v := testutil.ToFloat64(collector)
|
||||
if v != value {
|
||||
t.Errorf("Expected value %f for %s, got %f", value, desc, v)
|
||||
pc := make([]uintptr, 10)
|
||||
n := runtime.Callers(2, pc)
|
||||
if n == 0 {
|
||||
t.Errorf("Expected value %f for %s, got %f", value, desc, v)
|
||||
return
|
||||
}
|
||||
|
||||
pc = pc[:n]
|
||||
frames := runtime.CallersFrames(pc)
|
||||
stack := ""
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
if !strings.Contains(frame.File, "nextcloud-spreed-signaling") {
|
||||
break
|
||||
}
|
||||
stack += fmt.Sprintf("%s:%d\n", frame.File, frame.Line)
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
t.Errorf("Expected value %f for %s, got %f at\n%s", value, desc, v, stack)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue