Skip to content

Commit

Permalink
Add keycloak online/offline sessions records
Browse files Browse the repository at this point in the history
  • Loading branch information
Jerome Marchand committed Oct 16, 2020
1 parent f4cf84c commit acacffa
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 1 deletion.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,24 @@ 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 records 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 records 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
```

## Grafana Dashboard

You can use this dashboard or create yours https://grafana.com/dashboards/10441
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
package org.jboss.aerogear.keycloak.metrics;

import java.util.Map;
import java.util.HashMap;
import org.jboss.logging.Logger;
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;

public class MetricsEventListener implements EventListenerProvider {

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

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

private KeycloakSession session;

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

@Override
public void onEvent(Event event) {
logEventDetails(event);
Expand All @@ -31,13 +41,32 @@ public void onEvent(Event event) {
default:
PrometheusExporter.instance().recordGenericEvent(event);
}

setSessions(session.realms().getRealm(event.getRealmId()));
}

@Override
public void onEvent(AdminEvent event, boolean includeRepresentation) {
logAdminEventDetails(event);

PrometheusExporter.instance().recordGenericAdminEvent(event);

setSessions(session.realms().getRealm(event.getRealmId()));
}

private void setSessions(RealmModel realm) {

Map<String,Long> onlineSessions = new HashMap<String,Long>();
session.sessions().getActiveClientSessionStats(realm,false).forEach((id, count) ->
onlineSessions.put(realm.getClientById(id).getClientId(), count)
);

Map<String,Long> offlineSessions = new HashMap<String,Long>();
session.sessions().getActiveClientSessionStats(realm,true).forEach((id, count) ->
offlineSessions.put(realm.getClientById(id).getClientId(), count)
);

PrometheusExporter.instance().recordSessions(realm.getId(), onlineSessions, offlineSessions);
}

private void logEventDetails(Event 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,6 +2,7 @@

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;
Expand Down Expand Up @@ -39,6 +40,8 @@ public final class PrometheusExporter {
final Counter totalRegistrationsErrors;
final Counter responseErrors;
final Histogram requestDuration;
final Gauge totalOnlineSessions;
final Gauge totalOfflineSessions;
final PushGateway PUSH_GATEWAY;

private PrometheusExporter() {
Expand Down Expand Up @@ -91,6 +94,17 @@ private PrometheusExporter() {
.labelNames("method")
.register();

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

// Counters for all user events
for (EventType type : EventType.values()) {
if (type.equals(EventType.LOGIN) || type.equals(EventType.LOGIN_ERROR) || type.equals(EventType.REGISTER)) {
Expand Down Expand Up @@ -210,6 +224,23 @@ public void recordLoginError(final Event event) {
pushAsync();
}

/**
* Set sessions number
*
* @param event LoginError event
*/
public void recordSessions(final String realmId, Map<String,Long> onlineSessions, Map<String,Long> offlineSessions) {

onlineSessions.forEach((clientId, count) -> {
totalOnlineSessions.labels(nullToEmpty(realmId), nullToEmpty(clientId)).set(count);
});

offlineSessions.forEach((clientId, count) -> {
totalOfflineSessions.labels(nullToEmpty(realmId), nullToEmpty(clientId)).set(count);
});
pushAsync();
}

/**
* Record the duration between one request and response
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
Expand Down Expand Up @@ -177,6 +178,26 @@ public void shouldCorrectlyRecordResponseErrors() throws IOException {
assertGenericMetric("keycloak_response_errors", 1, tuple("code", "500"), tuple("method", "POST"));
}

@Test
public void shouldCorrectlyRecordSessions() throws IOException {

Map<String,Long> map1 = new HashMap<String, Long>() {{
put("account", new Long(10));
put("admin-cli", new Long(0));
}};

Map<String,Long> map2 = new HashMap<String, Long>() {{
put("account", new Long(0));
put("admin-cli", new Long(10));
}};

PrometheusExporter.instance().recordSessions(DEFAULT_REALM, map1, map2);
assertGenericMetric("keycloak_online_sessions", 10, tuple("realm", DEFAULT_REALM), tuple("client_id", "account"));
assertGenericMetric("keycloak_online_sessions", 0, tuple("realm", DEFAULT_REALM), tuple("client_id", "admin-cli"));
assertGenericMetric("keycloak_offline_sessions", 0, tuple("realm", DEFAULT_REALM), tuple("client_id", "account"));
assertGenericMetric("keycloak_offline_sessions", 10, tuple("realm", DEFAULT_REALM), tuple("client_id", "admin-cli"));
}

@Test
public void shouldTolerateNullLabels() throws IOException {
final Event nullEvent = new Event();
Expand Down

0 comments on commit acacffa

Please sign in to comment.