Skip to content

Commit

Permalink
Removing new SPI
Browse files Browse the repository at this point in the history
Fixed keycloak#33043

Signed-off-by: Alexander Schwartz <[email protected]>
  • Loading branch information
ahus1 committed Oct 10, 2024
1 parent 2ca7501 commit a5f19f4
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 215 deletions.
36 changes: 21 additions & 15 deletions docs/guides/server/event-metrics.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ title="Enabling {project_name} Event Metrics"
summary="Learn how to enable and use {project_name} Event Metrics"
includedOptions="metrics-enabled user-event-metrics-*">
Event metrics can provide admins an overview of the different activities in a {project_name} instance.
For example, you can monitor the number of logins per second, login failures per second, or token refreshes performed per second.
The metrics are exposed using the standard metrics endpoint, and you can use it in your own metrics collection system to create dashboards and alerts.
The metrics are reported per {project_name} instance. If you have multiple instances running in a cluster, you will need to collect the metrics from all instances and sum them up to get the total number of events in your cluster.
== Enable event metrics
To start collecting metrics, enable the feature `user-event-metrics`, enable metrics, and enable the metrics for user events.
Expand All @@ -23,25 +30,24 @@ The following limits the tags to the realm tag:
<@kc.start parameters="... --user-event-metrics-tags=realm ..."/>
The snippet below is an example of a response:
You can limit the metrics for which {project_name} will expose metrics.
The following example limits the events collected to `LOGIN` and `LOGOUT` events:
// FIXME: Example might be outdated
<@kc.start parameters="... --user-event-metrics-events=login,logout ..."/>
All error events will be collected with the primary event type and will have the `error` tag filled with the error code.
The snippet below is an example of a response provided by the metric endpoint:
[source]
----
# TYPE keycloak_simple_events counter
# HELP keycloak_simple_events The total number of Keycloak events
keycloak_simple_events_total{event="update_password",realm="master"} 1.0
keycloak_simple_events_total{event="update_credential",realm="master"} 1.0
# TYPE keycloak_events counter
# HELP keycloak_events The total number of Keycloak events
keycloak_events_total{client_id="security-admin-console",error="",event="code_to_token",idp="",realm="master"} 6.0
keycloak_events_total{client_id="security-admin-console",error="",event="logout",idp="",realm="master"} 2.0
keycloak_events_total{client_id="security-admin-console",error="",event="login",idp="",realm="master"} 6.0
keycloak_events_total{client_id="security-admin-console",error="",event="refresh_token",idp="",realm="master"} 2.0
keycloak_events_total{client_id="",error="client_not_found",event="login_error",idp="",realm="master"} 1.0
...
# HELP keycloak_user_events_total Keycloak user events
# TYPE keycloak_user_events_total counter
keycloak_user_events_total{client_id="security-admin-console",error="",event="code_to_token",idp="",realm="master",} 1.0
keycloak_user_events_total{client_id="security-admin-console",error="",event="login",idp="",realm="master",} 1.0
keycloak_user_events_total{client_id="security-admin-console",error="",event="logout",idp="",realm="master",} 1.0
keycloak_user_events_total{client_id="security-admin-console",error="invalid_user_credentials",event="login",idp="",realm="master",} 1.0
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ private EventPropertyMappers(){}
public static PropertyMapper<?>[] getMetricsPropertyMappers() {
return new PropertyMapper[] {
fromOption(USER_EVENT_METRICS_ENABLED)
.to("kc.spi-global-events-listener-micrometer-metrics-enabled")
.to("kc.spi-events-listener-micrometer-metrics-enabled")
.isEnabled(EventPropertyMappers::userEventsMetricsEnabled, METRICS_ENABLED_MSG + " and feature " + Profile.Feature.USER_EVENT_METRICS.getKey() + " is enabled")
.build(),
fromOption(USER_EVENT_METRICS_TAGS)
.to("kc.spi-global-events-listener-micrometer-metrics-tags")
.to("kc.spi-events-listener-micrometer-metrics-tags")
.paramLabel("tags")
.isEnabled(EventPropertyMappers::userEventsMetricsTags, "user event metrics are enabled")
.build(),
fromOption(USER_EVENT_METRICS_EVENTS)
.to("kc.spi-global-events-listener-micrometer-metrics-events")
.to("kc.spi-events-listener-micrometer-metrics-events")
.paramLabel("events")
.isEnabled(EventPropertyMappers::userEventsMetricsTags, "user event metrics are enabled")
.build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerTransaction;
import org.keycloak.events.EventType;
import org.keycloak.events.GlobalEventListenerProvider;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.KeycloakSession;

import java.util.HashSet;
import java.util.Locale;

public class MicrometerMetricsEventListener implements GlobalEventListenerProvider {
public class MicrometerMetricsEventListenerProvider implements EventListenerProvider {

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

private static final String REALM_TAG = "realm";
private static final String IDP_TAG = "idp";
Expand All @@ -57,7 +57,7 @@ public class MicrometerMetricsEventListener implements GlobalEventListenerProvid
private final EventListenerTransaction tx =
new EventListenerTransaction(null, this::countEvent);

public MicrometerMetricsEventListener(KeycloakSession session, boolean withIdp, boolean withRealm, boolean withClientId, HashSet<String> events) {
public MicrometerMetricsEventListenerProvider(KeycloakSession session, boolean withIdp, boolean withRealm, boolean withClientId, HashSet<String> events) {
this.withIdp = withIdp;
this.withRealm = withRealm;
this.withClientId = withClientId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@

import org.bouncycastle.util.Strings;
import org.keycloak.Config;
import org.keycloak.events.GlobalEventListenerProvider;
import org.keycloak.events.GlobalEventListenerProviderFactory;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.EnvironmentDependentProviderFactory;

import java.util.HashSet;

public class MicrometerMetricsEventListenerFactory implements GlobalEventListenerProviderFactory, EnvironmentDependentProviderFactory {
public class MicrometerMetricsEventListenerProviderFactory implements EventListenerProviderFactory, EnvironmentDependentProviderFactory {

private static final String ID = "micrometer-metrics";

Expand All @@ -36,8 +36,8 @@ public class MicrometerMetricsEventListenerFactory implements GlobalEventListene
private HashSet<String> events;

@Override
public GlobalEventListenerProvider create(KeycloakSession session) {
return new MicrometerMetricsEventListener(session, withIdp, withRealm, withClientId, events);
public EventListenerProvider create(KeycloakSession session) {
return new MicrometerMetricsEventListenerProvider(session, withIdp, withRealm, withClientId, events);
}

@Override
Expand Down Expand Up @@ -76,6 +76,11 @@ public String getId() {
return ID;
}

@Override
public boolean isEnabled(KeycloakSession session) {
return true;
}

@Override
public boolean isSupported(Config.Scope config) {
Boolean enabled = config.getBoolean("enabled");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# limitations under the License.
#

org.keycloak.quarkus.runtime.services.metrics.events.MicrometerMetricsEventListenerFactory
org.keycloak.quarkus.runtime.services.metrics.events.MicrometerMetricsEventListenerProviderFactory
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,8 @@ private Event createEvent(EventType type) {
return createEvent(type, DEFAULT_REALM_NAME, "THE_CLIENT_ID", (String) null);
}

private static MicrometerMetricsEventListener getMicrometerMetricsEventListener(KeycloakSession session) {
return session.getProvider(MicrometerMetricsEventListener.class);
private static MicrometerMetricsEventListenerProvider getMicrometerMetricsEventListener(KeycloakSession session) {
return session.getProvider(MicrometerMetricsEventListenerProvider.class);
}

private KeycloakSessionFactory createKeycloakSessionFactory() {
Expand Down Expand Up @@ -539,7 +539,7 @@ public boolean isActive() {

@Override
public <T extends Provider> T getProvider(Class<T> clazz) {
return (T) new MicrometerMetricsEventListener(this, true, true, true, null);
return (T) new MicrometerMetricsEventListenerProvider(this, true, true, true, null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -79,17 +80,18 @@ private static EventStoreProvider getEventStoreProvider(KeycloakSession session)
}

private static List<EventListenerProvider> getEventListeners(KeycloakSession session, RealmModel realm) {
return realm.getEventsListenersStream().map(id -> {
EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
if (listener != null) {
return listener;
} else {
log.error("Event listener '" + id + "' registered, but provider not found");
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
HashSet<String> realmListeners = new HashSet<>(realm.getEventsListenersStream().toList());
List<EventListenerProvider> result = session.getKeycloakSessionFactory().getProviderFactoriesStream(EventListenerProvider.class)
.filter(providerFactory -> ((EventListenerProviderFactory) providerFactory).isEnabled(session))
.map(providerFactory -> {
realmListeners.remove(providerFactory.getId());
return (EventListenerProvider) providerFactory.create(session);
})
.toList();
if (!realmListeners.isEmpty()) {
log.error("Event listeners " + realmListeners + " registered, but provider not found");
}
return result;
}

private EventBuilder(KeycloakSession session, EventStoreProvider store, List<EventListenerProvider> listeners, RealmModel realm, Event event) {
Expand Down Expand Up @@ -263,15 +265,6 @@ private void sendNow(EventStoreProvider targetStore, Set<String> eventTypes, Lis
log.error("Failed to send type to " + l, t);
}
}

session.getAllProviders(GlobalEventListenerProvider.class).forEach(p -> {
try {
p.onEvent(event);
} catch (Throwable t) {
log.error("Failed to send type to " + p, t);
}
});

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,24 @@

package org.keycloak.events;

import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ProviderFactory;

/**
* @author <a href="mailto:[email protected]">Stian Thorgersen</a>
*/
public interface EventListenerProviderFactory extends ProviderFactory<EventListenerProvider> {

default boolean isEnabled(KeycloakSession session) {
if (isGlobal()) {
return true;
} else {
return session.getContext().getRealm().getEventsListenersStream().anyMatch(s -> s.equals(getId()));
}
}

default boolean isGlobal() {
return false;
}

}

This file was deleted.

This file was deleted.

Loading

0 comments on commit a5f19f4

Please sign in to comment.