More metrics

pull/662/head
binwiederhier 2023-03-16 22:19:20 -04:00
parent 358b344916
commit ca9fed7b67
5 changed files with 60 additions and 19 deletions

View File

@ -1099,7 +1099,7 @@ If a non-200 HTTP status code is returned or if the returned `health` field is `
See [Installation for Docker](install.md#docker) for an example of how this could be used in a `docker-compose` environment.
## Metrics
## Monitoring
If configured, ntfy can expose a `/metrics` endpoint for [Prometheus](https://prometheus.io/), which can then be used to
create dashboards and alerts (e.g. via [Grafana](https://grafana.com/)).
@ -1108,7 +1108,7 @@ listen address. Metrics may be considered sensitive information, so before you e
doing, and/or secure access to the endpoint in your reverse proxy.
- `enable-metrics` enables the /metrics endpoint for the default ntfy server (i.e. HTTP, HTTPS and/or Unix socket)
- `metrics-listen-http` exposes the metrics endpoint via a dedicated [IP]:port. If set, this option implicitly
- `metrics-listen-http` exposes the metrics endpoint via a dedicated `[IP]:port`. If set, this option implicitly
enables metrics as well, e.g. "10.0.1.1:9090" or ":9090"
=== Using default port

View File

@ -618,6 +618,7 @@ func (s *Server) handleMatrixDiscovery(w http.ResponseWriter) error {
}
func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*message, error) {
start := time.Now()
t, err := fromContext[*topic](r, contextTopic)
if err != nil {
return nil, err
@ -707,6 +708,7 @@ func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*message, e
if unifiedpush {
minc(metricUnifiedPushPublishedSuccess)
}
mset(metricMessagePublishDurationMillis, time.Since(start).Milliseconds())
return m, nil
}

View File

@ -63,6 +63,15 @@ func (s *Server) execManager() {
sentMailTotal, sentMailSuccess, sentMailFailure = s.smtpSender.Counts()
}
// Users
var usersCount int64
if s.userManager != nil {
usersCount, err = s.userManager.UsersCount()
if err != nil {
log.Tag(tagManager).Err(err).Warn("Error counting users")
}
}
// Print stats
s.mu.Lock()
messagesCount, topicsCount, visitorsCount := s.messages, len(s.topics), len(s.visitors)
@ -75,6 +84,7 @@ func (s *Server) execManager() {
"topics_active": topicsCount,
"subscribers": subscribers,
"visitors": visitorsCount,
"users": usersCount,
"emails_received": receivedMailTotal,
"emails_received_success": receivedMailSuccess,
"emails_received_failure": receivedMailFailure,
@ -85,6 +95,7 @@ func (s *Server) execManager() {
Info("Server stats")
mset(metricMessagesCached, messagesCached)
mset(metricVisitors, visitorsCount)
mset(metricUsers, usersCount)
mset(metricSubscribers, subscribers)
mset(metricTopics, topicsCount)
}

View File

@ -8,6 +8,7 @@ var (
metricMessagesPublishedSuccess prometheus.Counter
metricMessagesPublishedFailure prometheus.Counter
metricMessagesCached prometheus.Gauge
metricMessagePublishDurationMillis prometheus.Gauge
metricFirebasePublishedSuccess prometheus.Counter
metricFirebasePublishedFailure prometheus.Counter
metricEmailsPublishedSuccess prometheus.Counter
@ -21,6 +22,7 @@ var (
metricVisitors prometheus.Gauge
metricSubscribers prometheus.Gauge
metricTopics prometheus.Gauge
metricUsers prometheus.Gauge
metricHTTPRequests *prometheus.CounterVec
)
@ -34,6 +36,9 @@ func initMetrics() {
metricMessagesCached = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ntfy_messages_cached_total",
})
metricMessagePublishDurationMillis = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ntfy_message_publish_duration_ms",
})
metricFirebasePublishedSuccess = prometheus.NewCounter(prometheus.CounterOpts{
Name: "ntfy_firebase_published_success",
})
@ -67,6 +72,9 @@ func initMetrics() {
metricVisitors = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ntfy_visitors_total",
})
metricUsers = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ntfy_users_total",
})
metricSubscribers = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ntfy_subscribers_total",
})
@ -80,6 +88,7 @@ func initMetrics() {
metricMessagesPublishedSuccess,
metricMessagesPublishedFailure,
metricMessagesCached,
metricMessagePublishDurationMillis,
metricFirebasePublishedSuccess,
metricFirebasePublishedFailure,
metricEmailsPublishedSuccess,
@ -91,6 +100,7 @@ func initMetrics() {
metricMatrixPublishedFailure,
metricAttachmentsTotalSize,
metricVisitors,
metricUsers,
metricSubscribers,
metricTopics,
metricHTTPRequests,

View File

@ -169,6 +169,7 @@ const (
ELSE 2
END, user
`
selectUserCountQuery = `SELECT COUNT(*) FROM user`
updateUserPassQuery = `UPDATE user SET pass = ? WHERE user = ?`
updateUserRoleQuery = `UPDATE user SET role = ? WHERE user = ?`
updateUserPrefsQuery = `UPDATE user SET prefs = ? WHERE id = ?`
@ -853,6 +854,23 @@ func (a *Manager) Users() ([]*User, error) {
return users, nil
}
// UsersCount returns the number of users in the databsae
func (a *Manager) UsersCount() (int64, error) {
rows, err := a.db.Query(selectUserCountQuery)
if err != nil {
return 0, err
}
defer rows.Close()
if !rows.Next() {
return 0, errNoRows
}
var count int64
if err := rows.Scan(&count); err != nil {
return 0, err
}
return count, nil
}
// User returns the user with the given username if it exists, or ErrUserNotFound otherwise.
// You may also pass Everyone to retrieve the anonymous user and its Grant list.
func (a *Manager) User(username string) (*User, error) {