Simplify metrics and add documentation
This commit is contained in:
parent
c99bad79d2
commit
31620d8136
7 changed files with 129 additions and 137 deletions
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
|
@ -3,9 +3,12 @@ updates:
|
||||||
- package-ecosystem: maven
|
- package-ecosystem: maven
|
||||||
directory: /
|
directory: /
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: monthly
|
||||||
allow:
|
day: monday
|
||||||
- dependency-name: org.keycloak:keycloak-parent
|
# github parses time without quotes to int
|
||||||
|
# yamllint disable-line rule:quoted-strings
|
||||||
|
time: "09:00"
|
||||||
|
timezone: Europe/Berlin
|
||||||
- package-ecosystem: github-actions
|
- package-ecosystem: github-actions
|
||||||
directory: /
|
directory: /
|
||||||
schedule:
|
schedule:
|
||||||
|
|
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
|
@ -61,9 +61,9 @@ jobs:
|
||||||
server-username: SERVER_USERNAME
|
server-username: SERVER_USERNAME
|
||||||
server-password: SERVER_PASSWORD
|
server-password: SERVER_PASSWORD
|
||||||
- run: mvn -B -ntp dependency:go-offline
|
- run: mvn -B -ntp dependency:go-offline
|
||||||
- run: mvn -B -ntp verify -Dcheckstyle.skip
|
- run: mvn -B -ntp verify -Dcheckstyle.skip -Dmaven.test.redirectTestOutputToFile=false
|
||||||
if: ${{ github.ref != 'refs/heads/main' }}
|
if: ${{ github.ref != 'refs/heads/main' }}
|
||||||
- run: mvn -B -ntp deploy -Dcheckstyle.skip
|
- run: mvn -B -ntp deploy -Dcheckstyle.skip -Dmaven.test.redirectTestOutputToFile=false
|
||||||
if: ${{ github.ref == 'refs/heads/main' }}
|
if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
env:
|
env:
|
||||||
SERVER_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
|
SERVER_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
|
||||||
|
|
71
README.md
71
README.md
|
@ -1,7 +1,74 @@
|
||||||
# Keycloak Event Metrics
|
# Keycloak Event Metrics
|
||||||
|
|
||||||
Provides metrics for Keycloak events.
|
Provides metrics for Keycloak user/admin events.
|
||||||
|
|
||||||
[](http://www.apache.org/licenses/)
|
[](http://www.apache.org/licenses/)
|
||||||
[](https://search.maven.org/search?q=g:%22io.kokuwa.keycloak%22%20AND%20a:%22keycloak-event-metrics%22)
|
[](https://central.sonatype.com/search?namespace=io.kokuwa.keycloak&q=keycloak-event-metrics)
|
||||||
[](https://github.com/kokuwaio/keycloak-event-metrics/actions/workflows/ci.yaml?query=branch%3Amain)
|
[](https://github.com/kokuwaio/keycloak-event-metrics/actions/workflows/ci.yaml?query=branch%3Amain)
|
||||||
|
|
||||||
|
## What?
|
||||||
|
|
||||||
|
Resuses micrometer from Quarkus distribution to add metrics for Keycloak for events.
|
||||||
|
|
||||||
|
### User Events
|
||||||
|
|
||||||
|
User events are added with key `keycloak_event_user_total` and tags:
|
||||||
|
|
||||||
|
* `type`: [EventType](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/EventType.java#L27) from [Event#type](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/Event.java#L44)
|
||||||
|
* `realm`: realm id from [Event#realmId](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/Event.java#L46)
|
||||||
|
* `client`: client id from [Event#clientId](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/Event.java#L48)
|
||||||
|
* `error`: error from [Event#error](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/Event.java#L56), only present for error types
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
keycloak_event_user_total{client="test",realm="9039a0b5-e8c9-437a-a02e-9d91b04548a4",type="LOGIN",error="",} 2.0
|
||||||
|
keycloak_event_user_total{client="test",realm="1fdb3465-1675-49e8-88ad-292e2f42ee72",type="LOGIN",error="",} 1.0
|
||||||
|
keycloak_event_user_total{client="test",realm="1fdb3465-1675-49e8-88ad-292e2f42ee72",type="LOGIN_ERROR",error="invalid_user_credentials",} 1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Admin Events
|
||||||
|
|
||||||
|
Admin events are added with key `keycloak_event_admin_total` and tags:
|
||||||
|
|
||||||
|
* `realm`: realm id from [AdminEvent#realmId](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java#L44)
|
||||||
|
* `operation`: [OperationType](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/admin/OperationType.java#L27) from [AdminEvent#operationType](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java#L53)
|
||||||
|
* `resource`: [ResourceType](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/admin/ResourceType.java#L24) from [AdminEvent#resourceType](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java#L51)
|
||||||
|
* `error`: error from [AdminEvent#error](https://github.com/keycloak/keycloak/blob/main/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java#L59), only present for error types
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
keycloak_event_admin_total{error="",operation="CREATE",realm="1fdb3465-1675-49e8-88ad-292e2f42ee72",resource="USER",} 1.0
|
||||||
|
keycloak_event_admin_total{error="",operation="CREATE",realm="9039a0b5-e8c9-437a-a02e-9d91b04548a4",resource="USER",} 1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Testcontainers
|
||||||
|
|
||||||
|
For usage in [Testcontainers](https://www.testcontainers.org/) see [KeycloakExtension.java](src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java#L57-L68)
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
Dockerfile:
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM quay.io/keycloak/keycloak:21.0.1
|
||||||
|
|
||||||
|
ENV KEYCLOAK_ADMIN=admin
|
||||||
|
ENV KEYCLOAK_ADMIN_PASSWORD=password
|
||||||
|
ENV KC_HEALTH_ENABLED=true
|
||||||
|
ENV KC_METRICS_ENABLED=true
|
||||||
|
ENV KC_LOG_CONSOLE_COLOR=true
|
||||||
|
|
||||||
|
ADD target/keycloak-event-metrics-0.0.1-SNAPSHOT.jar /opt/keycloak/providers
|
||||||
|
RUN /opt/keycloak/bin/kc.sh build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker build . --tag keycloak:metrics
|
||||||
|
docker run --rm -p8080 keycloak:metrics start-dev
|
||||||
|
```
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
<groupId>io.kokuwa.keycloak</groupId>
|
<groupId>io.kokuwa.keycloak</groupId>
|
||||||
<artifactId>keycloak-event-metrics</artifactId>
|
<artifactId>keycloak-event-metrics</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>Keycloak Metrics</name>
|
<name>Keycloak Metrics</name>
|
||||||
<description>Provides metrics for Keycloak events</description>
|
<description>Provides metrics for Keycloak user/admin events</description>
|
||||||
<url>https://github.com/kokuwaio/keycloak-event-metrics</url>
|
<url>https://github.com/kokuwaio/keycloak-event-metrics</url>
|
||||||
<inceptionYear>2023</inceptionYear>
|
<inceptionYear>2023</inceptionYear>
|
||||||
<organization>
|
<organization>
|
||||||
|
@ -210,6 +210,7 @@
|
||||||
<version>${version.org.apache.maven.plugins.surefire}</version>
|
<version>${version.org.apache.maven.plugins.surefire}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<failIfNoTests>true</failIfNoTests>
|
<failIfNoTests>true</failIfNoTests>
|
||||||
|
<redirectTestOutputToFile>${maven.test.redirectTestOutputToFile}</redirectTestOutputToFile>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
|
@ -3,7 +3,6 @@ package io.kokuwa.keycloak.metrics;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.keycloak.events.Event;
|
import org.keycloak.events.Event;
|
||||||
import org.keycloak.events.admin.AdminEvent;
|
import org.keycloak.events.admin.AdminEvent;
|
||||||
|
@ -18,22 +17,6 @@ import io.micrometer.core.instrument.MeterRegistry;
|
||||||
*/
|
*/
|
||||||
public class MicrometerEventRecorder {
|
public class MicrometerEventRecorder {
|
||||||
|
|
||||||
private static final String PREFIX = "keycloak_";
|
|
||||||
private static final String USER_EVENT_PREFIX = PREFIX + "user_event_";
|
|
||||||
private static final String ADMIN_EVENT_PREFIX = PREFIX + "admin_event_";
|
|
||||||
|
|
||||||
private static final String LOGIN_ATTEMPTS = PREFIX + "login_attempts";
|
|
||||||
private static final String LOGIN_SUCCESS = PREFIX + "logins";
|
|
||||||
private static final String LOGIN_FAILURE = PREFIX + "failed_login_attempts";
|
|
||||||
private static final String CLIENT_LOGIN_SUCCESS = PREFIX + "client_logins";
|
|
||||||
private static final String CLIENT_LOGIN_FAILURE = PREFIX + "failed_client_login_attempts";
|
|
||||||
private static final String REGISTER_SUCCESS = PREFIX + "registrations";
|
|
||||||
private static final String REGISTER_FAILURE = PREFIX + "registrations_errors";
|
|
||||||
private static final String REFRESH_TOKEN_SUCCESS = PREFIX + "refresh_tokens";
|
|
||||||
private static final String REFRESH_TOKEN_FAILURE = PREFIX + "refresh_tokens_errors";
|
|
||||||
private static final String CODE_TO_TOKEN_SUCCESS = PREFIX + "code_to_tokens";
|
|
||||||
private static final String CODE_TO_TOKEN_FAILURE = PREFIX + "code_to_tokens_errors";
|
|
||||||
|
|
||||||
private final Map<String, Counter> counters = new HashMap<>();
|
private final Map<String, Counter> counters = new HashMap<>();
|
||||||
private final MeterRegistry registry;
|
private final MeterRegistry registry;
|
||||||
|
|
||||||
|
@ -42,61 +25,27 @@ public class MicrometerEventRecorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
void adminEvent(AdminEvent event) {
|
void adminEvent(AdminEvent event) {
|
||||||
counter(ADMIN_EVENT_PREFIX + event.getOperationType().name(),
|
counter("keycloak_event_admin",
|
||||||
"realm", event.getRealmId(),
|
"realm", toBlankIfNull(event.getRealmId()),
|
||||||
"resource", event.getResourceType() == null ? "" : event.getResourceType().name());
|
"resource", toBlankIfNull(event.getResourceType()),
|
||||||
|
"operation", toBlankIfNull(event.getOperationType()),
|
||||||
|
"error", toBlankIfNull(event.getError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void userEvent(Event event) {
|
void userEvent(Event event) {
|
||||||
|
counter("keycloak_event_user",
|
||||||
var tags = new String[] {
|
"realm", toBlankIfNull(event.getRealmId()),
|
||||||
"provider", Optional
|
"type", toBlankIfNull(event.getType()),
|
||||||
.ofNullable(event.getDetails()).orElseGet(Map::of)
|
"client", toBlankIfNull(event.getClientId()),
|
||||||
.getOrDefault("identity_provider", "keycloak"),
|
"error", toBlankIfNull(event.getError()));
|
||||||
"realm", event.getRealmId() == null ? "" : event.getRealmId(),
|
|
||||||
"client_id", event.getClientId() == null ? "" : event.getClientId(),
|
|
||||||
"error", event.getError() == null ? "" : event.getError() };
|
|
||||||
|
|
||||||
switch (event.getType()) {
|
|
||||||
case LOGIN:
|
|
||||||
counter(LOGIN_ATTEMPTS, tags);
|
|
||||||
counter(LOGIN_SUCCESS, tags);
|
|
||||||
break;
|
|
||||||
case LOGIN_ERROR:
|
|
||||||
counter(LOGIN_ATTEMPTS, tags);
|
|
||||||
counter(LOGIN_FAILURE, tags);
|
|
||||||
break;
|
|
||||||
case CLIENT_LOGIN:
|
|
||||||
counter(CLIENT_LOGIN_SUCCESS, tags);
|
|
||||||
break;
|
|
||||||
case CLIENT_LOGIN_ERROR:
|
|
||||||
counter(CLIENT_LOGIN_FAILURE, tags);
|
|
||||||
break;
|
|
||||||
case REGISTER:
|
|
||||||
counter(REGISTER_SUCCESS, tags);
|
|
||||||
break;
|
|
||||||
case REGISTER_ERROR:
|
|
||||||
counter(REGISTER_FAILURE, tags);
|
|
||||||
break;
|
|
||||||
case REFRESH_TOKEN:
|
|
||||||
counter(REFRESH_TOKEN_SUCCESS, tags);
|
|
||||||
break;
|
|
||||||
case REFRESH_TOKEN_ERROR:
|
|
||||||
counter(REFRESH_TOKEN_FAILURE, tags);
|
|
||||||
break;
|
|
||||||
case CODE_TO_TOKEN:
|
|
||||||
counter(CODE_TO_TOKEN_SUCCESS, tags);
|
|
||||||
break;
|
|
||||||
case CODE_TO_TOKEN_ERROR:
|
|
||||||
counter(CODE_TO_TOKEN_FAILURE, tags);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
counter(USER_EVENT_PREFIX + event.getType().name(), tags);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void counter(String counter, String... tags) {
|
private void counter(String counter, String... tags) {
|
||||||
counters.computeIfAbsent(counter + Arrays.toString(tags), string -> registry.counter(counter, tags))
|
counters.computeIfAbsent(counter + Arrays.toString(tags), string -> registry.counter(counter, tags))
|
||||||
.increment();
|
.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String toBlankIfNull(Object value) {
|
||||||
|
return value == null ? "" : value.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.UUID;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
|
||||||
import io.kokuwa.keycloak.metrics.junit.KeycloakClient;
|
import io.kokuwa.keycloak.metrics.junit.KeycloakClient;
|
||||||
import io.kokuwa.keycloak.metrics.junit.KeycloakExtension;
|
import io.kokuwa.keycloak.metrics.junit.KeycloakExtension;
|
||||||
|
@ -34,15 +35,12 @@ public class KeycloakIT {
|
||||||
keycloak.createUser(realmName2, username2, password2);
|
keycloak.createUser(realmName2, username2, password2);
|
||||||
|
|
||||||
prometheus.scrap();
|
prometheus.scrap();
|
||||||
var loginAttemptsBefore = prometheus.loginAttempts();
|
var loginBefore = prometheus.userEvent(EventType.LOGIN);
|
||||||
var loginAttemptsBefore1 = prometheus.loginAttempts(realmId1);
|
var loginBefore1 = prometheus.userEvent(EventType.LOGIN, realmId1);
|
||||||
var loginAttemptsBefore2 = prometheus.loginAttempts(realmId2);
|
var loginBefore2 = prometheus.userEvent(EventType.LOGIN, realmId2);
|
||||||
var loginSuccessBefore = prometheus.loginSuccess();
|
var loginErrorBefore = prometheus.userEvent(EventType.LOGIN_ERROR);
|
||||||
var loginSuccessBefore1 = prometheus.loginSuccess(realmId1);
|
var loginErrorBefore1 = prometheus.userEvent(EventType.LOGIN_ERROR, realmId1);
|
||||||
var loginSuccessBefore2 = prometheus.loginSuccess(realmId2);
|
var loginErrorBefore2 = prometheus.userEvent(EventType.LOGIN_ERROR, realmId2);
|
||||||
var loginFailureBefore = prometheus.loginFailure();
|
|
||||||
var loginFailureBefore1 = prometheus.loginFailure(realmId1);
|
|
||||||
var loginFailureBefore2 = prometheus.loginFailure(realmId2);
|
|
||||||
|
|
||||||
assertTrue(keycloak.login(realmName1, username1, password1));
|
assertTrue(keycloak.login(realmName1, username1, password1));
|
||||||
assertTrue(keycloak.login(realmName1, username1, password1));
|
assertTrue(keycloak.login(realmName1, username1, password1));
|
||||||
|
@ -50,25 +48,19 @@ public class KeycloakIT {
|
||||||
assertFalse(keycloak.login(realmName2, username2, "nope"));
|
assertFalse(keycloak.login(realmName2, username2, "nope"));
|
||||||
|
|
||||||
prometheus.scrap();
|
prometheus.scrap();
|
||||||
var loginAttemptsAfter = prometheus.loginAttempts();
|
var loginAfter = prometheus.userEvent(EventType.LOGIN);
|
||||||
var loginAttemptsAfter1 = prometheus.loginAttempts(realmId1);
|
var loginAfter1 = prometheus.userEvent(EventType.LOGIN, realmId1);
|
||||||
var loginAttemptsAfter2 = prometheus.loginAttempts(realmId2);
|
var loginAfter2 = prometheus.userEvent(EventType.LOGIN, realmId2);
|
||||||
var loginSuccessAfter = prometheus.loginSuccess();
|
var loginErrorAfter = prometheus.userEvent(EventType.LOGIN_ERROR);
|
||||||
var loginSuccessAfter1 = prometheus.loginSuccess(realmId1);
|
var loginErrorAfter1 = prometheus.userEvent(EventType.LOGIN_ERROR, realmId1);
|
||||||
var loginSuccessAfter2 = prometheus.loginSuccess(realmId2);
|
var loginErrorAfter2 = prometheus.userEvent(EventType.LOGIN_ERROR, realmId2);
|
||||||
var loginFailureAfter = prometheus.loginFailure();
|
|
||||||
var loginFailureAfter1 = prometheus.loginFailure(realmId1);
|
|
||||||
var loginFailureAfter2 = prometheus.loginFailure(realmId2);
|
|
||||||
|
|
||||||
assertAll("promethus",
|
assertAll("prometheus",
|
||||||
() -> assertEquals(loginAttemptsBefore + 4, loginAttemptsAfter, "login attempts total"),
|
() -> assertEquals(loginBefore + 3, loginAfter, "login success total"),
|
||||||
() -> assertEquals(loginAttemptsBefore1 + 2, loginAttemptsAfter1, "login attempts #1"),
|
() -> assertEquals(loginBefore1 + 2, loginAfter1, "login success #1"),
|
||||||
() -> assertEquals(loginAttemptsBefore2 + 2, loginAttemptsAfter2, "login attempts #2"),
|
() -> assertEquals(loginBefore2 + 1, loginAfter2, "login success #2"),
|
||||||
() -> assertEquals(loginSuccessBefore + 3, loginSuccessAfter, "login success total"),
|
() -> assertEquals(loginErrorBefore + 1, loginErrorAfter, "login failure total"),
|
||||||
() -> assertEquals(loginSuccessBefore1 + 2, loginSuccessAfter1, "login success #1"),
|
() -> assertEquals(loginErrorBefore1 + 0, loginErrorAfter1, "login failure #1"),
|
||||||
() -> assertEquals(loginSuccessBefore2 + 1, loginSuccessAfter2, "login success #2"),
|
() -> assertEquals(loginErrorBefore2 + 1, loginErrorAfter2, "login failure #2"));
|
||||||
() -> assertEquals(loginFailureBefore + 1, loginFailureAfter, "login failure total"),
|
|
||||||
() -> assertEquals(loginFailureBefore1 + 0, loginFailureAfter1, "login failure #1"),
|
|
||||||
() -> assertEquals(loginFailureBefore2 + 1, loginFailureAfter2, "login failure #2"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client to access Prometheus metric values:
|
* Client to access Prometheus metric values:
|
||||||
*
|
*
|
||||||
|
@ -21,28 +23,21 @@ public class Prometheus {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int loginAttempts() {
|
public int userEvent(EventType type) {
|
||||||
return scrap("keycloak_login_attempts_total").intValue();
|
return state.stream()
|
||||||
|
.filter(metric -> Objects.equals(metric.name(), "keycloak_event_user_total"))
|
||||||
|
.filter(metric -> Objects.equals(metric.tags().get("type"), type.toString()))
|
||||||
|
.mapToInt(metric -> metric.value().intValue())
|
||||||
|
.sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int loginAttempts(String realmName) {
|
public int userEvent(EventType type, String realmName) {
|
||||||
return scrap("keycloak_login_attempts_total", "realm", realmName).intValue();
|
return state.stream()
|
||||||
}
|
.filter(metric -> Objects.equals(metric.name(), "keycloak_event_user_total"))
|
||||||
|
.filter(metric -> Objects.equals(metric.tags().get("type"), type.toString()))
|
||||||
public int loginSuccess() {
|
.filter(metric -> Objects.equals(metric.tags().get("realm"), realmName))
|
||||||
return scrap("keycloak_logins_total").intValue();
|
.mapToInt(metric -> metric.value().intValue())
|
||||||
}
|
.sum();
|
||||||
|
|
||||||
public int loginSuccess(String realmName) {
|
|
||||||
return scrap("keycloak_logins_total", "realm", realmName).intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int loginFailure() {
|
|
||||||
return scrap("keycloak_failed_login_attempts_total").intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int loginFailure(String realmName) {
|
|
||||||
return scrap("keycloak_failed_login_attempts_total", "realm", realmName).intValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrap() {
|
public void scrap() {
|
||||||
|
@ -63,19 +58,4 @@ public class Prometheus {
|
||||||
})
|
})
|
||||||
.forEach(state::add);
|
.forEach(state::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Double scrap(String name) {
|
|
||||||
return state.stream()
|
|
||||||
.filter(metric -> Objects.equals(metric.name(), name))
|
|
||||||
.mapToDouble(PrometheusMetric::value)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Double scrap(String name, String tag, String value) {
|
|
||||||
return state.stream()
|
|
||||||
.filter(metric -> Objects.equals(metric.name(), name))
|
|
||||||
.filter(metric -> Objects.equals(metric.tags().get(tag), value))
|
|
||||||
.mapToDouble(PrometheusMetric::value)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue