Skip to content

Commit

Permalink
Add online and offline sessions metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
OleksandrMishchuk committed Jan 10, 2023
1 parent 15aa5e4 commit b4fb253
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 65 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,25 @@ This counter counts the number of response errors (responses where the http stat
keycloak_response_errors{code="500",method="GET",} 1
```
##### keycloak_online_sessions
This gauge indicates the number of online sessions.
```c
# HELP keycloak_online_sessions Total online sessions
# TYPE keycloak_online_sessions gauge
keycloak_online_sessions{realm="test",client_id="application1",} 1.0
```

##### keycloak_offline_sessions
This gauge indicates the number of offline sessions.

```c
# HELP keycloak_offline_sessions Total offline sessions
# TYPE keycloak_offline_sessions gauge
keycloak_offline_sessions{realm="test",client_id="application1",} 1.0
```
#### Metrics URI
The URI can be added to the metrics by setting the environment variable ```URI_METRICS_ENABLED``` to `true`.
This will output a consolidated realm URI value to the metrics. The realm value is replaced with a generic `{realm}` value
Expand Down
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,35 @@
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.KeycloakSession;

public class MetricsEventListener implements EventListenerProvider {

public final static String ID = "metrics-listener";

private final static Logger logger = Logger.getLogger(MetricsEventListener.class);

private final KeycloakSession keycloakSession;

public MetricsEventListener(KeycloakSession keycloakSession) {
this.keycloakSession = keycloakSession;
}

@Override
public void onEvent(Event event) {
logEventDetails(event);

switch (event.getType()) {
case LOGIN:
PrometheusExporter.instance().recordLogin(event);
PrometheusExporter.instance().recordSessions(event, keycloakSession);
break;
case CLIENT_LOGIN:
PrometheusExporter.instance().recordClientLogin(event);
PrometheusExporter.instance().recordSessions(event, keycloakSession);
break;
case LOGOUT:
PrometheusExporter.instance().recordSessions(event, keycloakSession);
break;
case REGISTER:
PrometheusExporter.instance().recordRegistration(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class MetricsEventListenerFactory implements EventListenerProviderFactory

@Override
public EventListenerProvider create(KeycloakSession session) {
return new MetricsEventListener();
return new MetricsEventListener(session);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;
import io.prometheus.client.exporter.PushGateway;
import io.prometheus.client.exporter.common.TextFormat;
import io.prometheus.client.hotspot.DefaultExports;
import org.apache.commons.collections4.MapUtils;
import org.jboss.logging.Logger;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.events.admin.OperationType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;

import java.io.BufferedWriter;
import java.io.IOException;
Expand Down Expand Up @@ -42,6 +47,8 @@ public final class PrometheusExporter {

private static PrometheusExporter INSTANCE;

private static final Gson gson = new Gson();

private final static Logger logger = Logger.getLogger(PrometheusExporter.class);

// these fields are package private on purpose
Expand All @@ -60,6 +67,8 @@ public final class PrometheusExporter {
final Counter responseTotal;
final Counter responseErrors;
final Histogram requestDuration;
final Gauge totalOnlineSessions;
final Gauge totalOfflineSessions;
final PushGateway PUSH_GATEWAY;

private PrometheusExporter() {
Expand Down Expand Up @@ -149,6 +158,18 @@ private PrometheusExporter() {
.labelNames("realm", "provider", "error", "client_id")
.register();

totalOnlineSessions = Gauge.build()
.name("keycloak_online_sessions")
.help("Total online sessions")
.labelNames("realm", "provider", "client_id")
.register();

totalOfflineSessions = Gauge.build()
.name("keycloak_offline_sessions")
.help("Total offline sessions")
.labelNames("realm", "provider", "client_id")
.register();

final boolean URI_METRICS_ENABLED = Boolean.parseBoolean(System.getenv("URI_METRICS_ENABLED"));
if (URI_METRICS_ENABLED){
responseTotal = Counter.build()
Expand Down Expand Up @@ -274,6 +295,25 @@ public void recordLogin(final Event event) {
pushAsync();
}

public void recordSessions(final Event event, final KeycloakSession keycloakSession) {
final String provider = getIdentityProvider(event);
final RealmModel realmModel = keycloakSession.realms().getRealm(event.getRealmId());
final ClientModel clientModel = keycloakSession.clients().getClientByClientId(realmModel, event.getClientId());

if(clientModel != null) {
final Map<String, Long> onlineClientSessionStats = keycloakSession.sessions().getActiveClientSessionStats(realmModel, false);
final Optional<Long> onlineSessionCount = Optional.ofNullable(MapUtils.emptyIfNull(onlineClientSessionStats).get(clientModel.getId()));

final Map<String, Long> offlineClientSessionStats = keycloakSession.sessions().getActiveClientSessionStats(realmModel, true);
final Optional<Long> offlineSessionCount = Optional.ofNullable(MapUtils.emptyIfNull(offlineClientSessionStats).get(clientModel.getId()));

totalOnlineSessions.labels(nullToEmpty(event.getRealmId()), provider, nullToEmpty(event.getClientId())).set(onlineSessionCount.orElse(0L));
totalOfflineSessions.labels(nullToEmpty(event.getRealmId()), provider, nullToEmpty(event.getClientId())).set(offlineSessionCount.orElse(0L));

pushAsync();
}
}

/**
* Increase the number registered users
*
Expand Down
Loading

0 comments on commit b4fb253

Please sign in to comment.