added a few gauges updated on a timer

This commit is contained in:
Garth 2023-04-06 14:55:50 +02:00
parent ca966a4c6b
commit d88e22d899
3 changed files with 83 additions and 4 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*~
target/

16
docker-compose.yml Normal file
View file

@ -0,0 +1,16 @@
version: '3'
services:
keycloak:
image: quay.io/keycloak/keycloak:21.0.2
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_HTTP_RELATIVE_PATH: /auth
KC_HEALTH_ENABLED: true
KC_METRICS_ENABLED: true
ports:
- 8080:8080
volumes:
- ./target/keycloak-event-metrics-0.2.1-SNAPSHOT.jar:/opt/keycloak/providers/keycloak-event-metrics.jar
command: [ "start-dev" ]

View file

@ -1,15 +1,18 @@
package io.kokuwa.keycloak.metrics; package io.kokuwa.keycloak.metrics;
import com.google.common.collect.ImmutableList;
import io.micrometer.core.instrument.ImmutableTag;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import javax.enterprise.inject.spi.CDI; import javax.enterprise.inject.spi.CDI;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.Config.Scope; import org.keycloak.Config.Scope;
import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerProviderFactory; import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import io.micrometer.core.instrument.MeterRegistry; import org.keycloak.timer.TimerProvider;
/** /**
* Factory for {@link MicrometerEventListener}, uses {@link MeterRegistry} from CDI. * Factory for {@link MicrometerEventListener}, uses {@link MeterRegistry} from CDI.
@ -18,13 +21,15 @@ import io.micrometer.core.instrument.MeterRegistry;
*/ */
public class MicrometerEventListenerFactory implements EventListenerProviderFactory { public class MicrometerEventListenerFactory implements EventListenerProviderFactory {
private static final String PROVIDER_ID = "metrics-listener";
private static final int INTERVAL = 60 * 1000; //1 MINUTE
private static final Logger log = Logger.getLogger(MicrometerEventListener.class); private static final Logger log = Logger.getLogger(MicrometerEventListener.class);
private MeterRegistry registry; private MeterRegistry registry;
private boolean replace; private boolean replace;
@Override @Override
public String getId() { public String getId() {
return "metrics-listener"; return PROVIDER_ID;
} }
@Override @Override
@ -36,6 +41,17 @@ public class MicrometerEventListenerFactory implements EventListenerProviderFact
@Override @Override
public void postInit(KeycloakSessionFactory factory) { public void postInit(KeycloakSessionFactory factory) {
registry = CDI.current().select(MeterRegistry.class).get(); registry = CDI.current().select(MeterRegistry.class).get();
KeycloakModelUtils.runJobInTransaction(factory, s1 -> {
TimerProvider timer = s1.getProvider(TimerProvider.class);
log.info("Registering gauge update job TimerProvider");
timer.schedule(() -> {
KeycloakModelUtils.runJobInTransaction(s1.getKeycloakSessionFactory(), s2 -> {
log.info("Updating gauges");
updateGauges(s2);
});
}, INTERVAL, PROVIDER_ID);
});
} }
@Override @Override
@ -45,4 +61,49 @@ public class MicrometerEventListenerFactory implements EventListenerProviderFact
@Override @Override
public void close() {} public void close() {}
private Iterable<Tag> tags(String... tags) {
if (tags.length % 2 != 0) throw new IllegalStateException("Tag name value pairs must be even");
ImmutableList.Builder<Tag> builder = new ImmutableList.Builder<Tag>();
for (int i = 0;i < tags.length;i+=2) {
builder.add(new ImmutableTag(tags[i], tags[i+1]));
}
return builder.build();
}
private void updateGauges(KeycloakSession session) {
session.realms().getRealmsStream().forEach(realm -> {
// keycloak_users = total number of users
registry.gauge("keycloak_users",
tags("realm", replace ? realm.getName() : realm.getId()),
session.users().getUsersCount(realm));
// keycloak_clients = total number of clients
registry.gauge("keycloak_clients",
tags("realm", replace ? realm.getName() : realm.getId()),
realm.getClientsCount());
// sessions - by client
realm.getClientsStream().forEach(client -> {
// keycloak_active_user_sessions
registry.gauge("keycloak_active_user_sessions",
tags("realm", replace ? realm.getName() : realm.getId(),
"client", replace ? client.getClientId() : client.getId()),
session.sessions().getActiveUserSessions(realm, client));
// keycloak_active_client_sessions
registry.gauge("keycloak_active_client_sessions",
tags("realm", replace ? realm.getName() : realm.getId(),
"client", replace ? client.getClientId() : client.getId()),
session.sessions().getActiveClientSessionStats(realm,false).get(client.getId()));
// keycloak_offline_sessions
registry.gauge("keycloak_offline_sessions",
tags("realm", replace ? realm.getName() : realm.getId(),
"client", replace ? client.getClientId() : client.getId()),
session.sessions().getOfflineSessionsCount(realm, client));
});
});
}
} }