Prometheus metrics for logins and registrations (#9511)

Add prom metrics for number of users successfully registering and logging in, by SSO provider.
pull/9550/head
Richard van der Hoff 2021-03-04 16:39:27 +00:00 committed by GitHub
parent 7eb6e39a8f
commit df425c2c63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 4 deletions

1
changelog.d/9511.feature Normal file
View File

@ -0,0 +1 @@
Add prometheus metrics for number of users successfully registering and logging in.

View File

@ -18,6 +18,8 @@
import logging import logging
from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple
from prometheus_client import Counter
from synapse import types from synapse import types
from synapse.api.constants import MAX_USERID_LENGTH, EventTypes, JoinRules, LoginType from synapse.api.constants import MAX_USERID_LENGTH, EventTypes, JoinRules, LoginType
from synapse.api.errors import AuthError, Codes, ConsentNotGivenError, SynapseError from synapse.api.errors import AuthError, Codes, ConsentNotGivenError, SynapseError
@ -41,6 +43,19 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
registration_counter = Counter(
"synapse_user_registrations_total",
"Number of new users registered (since restart)",
["guest", "shadow_banned", "auth_provider"],
)
login_counter = Counter(
"synapse_user_logins_total",
"Number of user logins (since restart)",
["guest", "auth_provider"],
)
class RegistrationHandler(BaseHandler): class RegistrationHandler(BaseHandler):
def __init__(self, hs: "HomeServer"): def __init__(self, hs: "HomeServer"):
super().__init__(hs) super().__init__(hs)
@ -156,6 +171,7 @@ class RegistrationHandler(BaseHandler):
bind_emails: Iterable[str] = [], bind_emails: Iterable[str] = [],
by_admin: bool = False, by_admin: bool = False,
user_agent_ips: Optional[List[Tuple[str, str]]] = None, user_agent_ips: Optional[List[Tuple[str, str]]] = None,
auth_provider_id: Optional[str] = None,
) -> str: ) -> str:
"""Registers a new client on the server. """Registers a new client on the server.
@ -181,8 +197,10 @@ class RegistrationHandler(BaseHandler):
admin api, otherwise False. admin api, otherwise False.
user_agent_ips: Tuples of IP addresses and user-agents used user_agent_ips: Tuples of IP addresses and user-agents used
during the registration process. during the registration process.
auth_provider_id: The SSO IdP the user used, if any (just used for the
prometheus metrics).
Returns: Returns:
The registere user_id. The registered user_id.
Raises: Raises:
SynapseError if there was a problem registering. SynapseError if there was a problem registering.
""" """
@ -280,6 +298,12 @@ class RegistrationHandler(BaseHandler):
# if user id is taken, just generate another # if user id is taken, just generate another
fail_count += 1 fail_count += 1
registration_counter.labels(
guest=make_guest,
shadow_banned=shadow_banned,
auth_provider=(auth_provider_id or ""),
).inc()
if not self.hs.config.user_consent_at_registration: if not self.hs.config.user_consent_at_registration:
if not self.hs.config.auto_join_rooms_for_guests and make_guest: if not self.hs.config.auto_join_rooms_for_guests and make_guest:
logger.info( logger.info(
@ -638,6 +662,7 @@ class RegistrationHandler(BaseHandler):
initial_display_name: Optional[str], initial_display_name: Optional[str],
is_guest: bool = False, is_guest: bool = False,
is_appservice_ghost: bool = False, is_appservice_ghost: bool = False,
auth_provider_id: Optional[str] = None,
) -> Tuple[str, str]: ) -> Tuple[str, str]:
"""Register a device for a user and generate an access token. """Register a device for a user and generate an access token.
@ -648,7 +673,8 @@ class RegistrationHandler(BaseHandler):
device_id: The device ID to check, or None to generate a new one. device_id: The device ID to check, or None to generate a new one.
initial_display_name: An optional display name for the device. initial_display_name: An optional display name for the device.
is_guest: Whether this is a guest account is_guest: Whether this is a guest account
auth_provider_id: The SSO IdP the user used, if any (just used for the
prometheus metrics).
Returns: Returns:
Tuple of device ID and access token Tuple of device ID and access token
""" """
@ -687,6 +713,11 @@ class RegistrationHandler(BaseHandler):
is_appservice_ghost=is_appservice_ghost, is_appservice_ghost=is_appservice_ghost,
) )
login_counter.labels(
guest=is_guest,
auth_provider=(auth_provider_id or ""),
).inc()
return (registered_device_id, access_token) return (registered_device_id, access_token)
async def post_registration_actions( async def post_registration_actions(

View File

@ -606,6 +606,7 @@ class SsoHandler:
default_display_name=attributes.display_name, default_display_name=attributes.display_name,
bind_emails=attributes.emails, bind_emails=attributes.emails,
user_agent_ips=[(user_agent, ip_address)], user_agent_ips=[(user_agent, ip_address)],
auth_provider_id=auth_provider_id,
) )
await self._store.record_user_external_id( await self._store.record_user_external_id(

View File

@ -219,6 +219,7 @@ class LoginRestServlet(RestServlet):
callback: Optional[Callable[[Dict[str, str]], Awaitable[None]]] = None, callback: Optional[Callable[[Dict[str, str]], Awaitable[None]]] = None,
create_non_existent_users: bool = False, create_non_existent_users: bool = False,
ratelimit: bool = True, ratelimit: bool = True,
auth_provider_id: Optional[str] = None,
) -> Dict[str, str]: ) -> Dict[str, str]:
"""Called when we've successfully authed the user and now need to """Called when we've successfully authed the user and now need to
actually login them in (e.g. create devices). This gets called on actually login them in (e.g. create devices). This gets called on
@ -234,6 +235,8 @@ class LoginRestServlet(RestServlet):
create_non_existent_users: Whether to create the user if they don't create_non_existent_users: Whether to create the user if they don't
exist. Defaults to False. exist. Defaults to False.
ratelimit: Whether to ratelimit the login request. ratelimit: Whether to ratelimit the login request.
auth_provider_id: The SSO IdP the user used, if any (just used for the
prometheus metrics).
Returns: Returns:
result: Dictionary of account information after successful login. result: Dictionary of account information after successful login.
@ -256,7 +259,7 @@ class LoginRestServlet(RestServlet):
device_id = login_submission.get("device_id") device_id = login_submission.get("device_id")
initial_display_name = login_submission.get("initial_device_display_name") initial_display_name = login_submission.get("initial_device_display_name")
device_id, access_token = await self.registration_handler.register_device( device_id, access_token = await self.registration_handler.register_device(
user_id, device_id, initial_display_name user_id, device_id, initial_display_name, auth_provider_id=auth_provider_id
) )
result = { result = {
@ -286,7 +289,10 @@ class LoginRestServlet(RestServlet):
res = await auth_handler.validate_short_term_login_token(token) res = await auth_handler.validate_short_term_login_token(token)
return await self._complete_login( return await self._complete_login(
res.user_id, login_submission, self.auth_handler._sso_login_callback res.user_id,
login_submission,
self.auth_handler._sso_login_callback,
auth_provider_id=res.auth_provider_id,
) )
async def _do_jwt_login(self, login_submission: JsonDict) -> Dict[str, str]: async def _do_jwt_login(self, login_submission: JsonDict) -> Dict[str, str]: