diff --git a/.idea/runConfigurations/keycloak_metrics__package_.xml b/.idea/runConfigurations/keycloak_metrics__clean_package_.xml
similarity index 82%
rename from .idea/runConfigurations/keycloak_metrics__package_.xml
rename to .idea/runConfigurations/keycloak_metrics__clean_package_.xml
index 931995f..4afa760 100644
--- a/.idea/runConfigurations/keycloak_metrics__package_.xml
+++ b/.idea/runConfigurations/keycloak_metrics__clean_package_.xml
@@ -1,5 +1,5 @@
-
+
@@ -11,6 +11,7 @@
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 4b1bf9c..12885ff 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,13 +5,17 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -22,12 +26,18 @@
+
+
+
+
+
+
@@ -52,6 +62,7 @@
diff --git a/pom.xml b/pom.xml
index 5ecb3b5..7f735c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,6 +48,11 @@
simpleclient_common
0.16.0
+
+ io.prometheus
+ simpleclient_httpserver
+ 0.16.0
+
diff --git a/src/main/java/KeycloakMetricsServer.java b/src/main/java/KeycloakMetricsServer.java
deleted file mode 100644
index e158edb..0000000
--- a/src/main/java/KeycloakMetricsServer.java
+++ /dev/null
@@ -1,2 +0,0 @@
-public class KeycloakMetricsServer {
-}
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/Metrics.java b/src/main/java/coffee/_finally/keycloak_metrics/Metrics.java
index 8b9315d..814d57c 100644
--- a/src/main/java/coffee/_finally/keycloak_metrics/Metrics.java
+++ b/src/main/java/coffee/_finally/keycloak_metrics/Metrics.java
@@ -52,6 +52,10 @@ public class Metrics {
return METRICS_INSTANCE;
}
+ public CollectorRegistry getRegistry() {
+ return registry;
+ }
+
public static void increment(String metric) {
Collector collector = metrics.get(metric);
if (collector == null) {
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpoint.java b/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpoint.java
deleted file mode 100644
index 56f8e74..0000000
--- a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpoint.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package coffee._finally.keycloak_metrics;
-
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.core.*;
-import org.keycloak.services.resource.RealmResourceProvider;
-
-public class MetricsEndpoint implements RealmResourceProvider {
-
- public static final String ID = "metrics";
-
- @Override
- public Object getResource() {
- return this;
- }
-
- @Override
- public void close() {
-
- }
-
- @GET
- @Produces(MediaType.TEXT_PLAIN)
- public Response get(@Context HttpHeaders headers) {
- final StreamingOutput stream = outputStream -> Metrics.getInstance().export(outputStream);
- return Response.ok(stream).build();
- }
-}
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpointFactory.java b/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpointFactory.java
deleted file mode 100644
index 1b102a7..0000000
--- a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpointFactory.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package coffee._finally.keycloak_metrics;
-
-import org.keycloak.Config;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
-import org.keycloak.services.resource.RealmResourceProvider;
-import org.keycloak.services.resource.RealmResourceProviderFactory;
-
-public class MetricsEndpointFactory implements RealmResourceProviderFactory {
- @Override
- public RealmResourceProvider create(KeycloakSession keycloakSession) {
- return new MetricsEndpoint();
- }
-
- @Override
- public void init(Config.Scope scope) {
- // empty on purpose
- }
-
- @Override
- public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
- // empty on purpose
- }
-
- @Override
- public void close() {
- // empty on purpose as nothing needs to be closed manually
- }
-
- @Override
- public String getId() {
- return MetricsEndpoint.ID;
- }
-}
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListener.java b/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProvider.java
similarity index 94%
rename from src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListener.java
rename to src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProvider.java
index a5b1280..b4fbb32 100644
--- a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListener.java
+++ b/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProvider.java
@@ -4,7 +4,7 @@ import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.admin.AdminEvent;
-public class MetricsEventListener implements EventListenerProvider {
+public class MetricsEventListenerProvider implements EventListenerProvider {
public static final String ID = "metrics-listener";
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerFactory.java b/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProviderFactory.java
similarity index 76%
rename from src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerFactory.java
rename to src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProviderFactory.java
index 442561e..f9ca7d1 100644
--- a/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerFactory.java
+++ b/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProviderFactory.java
@@ -6,10 +6,10 @@ import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
-public class MetricsEventListenerFactory implements EventListenerProviderFactory {
+public class MetricsEventListenerProviderFactory implements EventListenerProviderFactory {
@Override
public EventListenerProvider create(KeycloakSession keycloakSession) {
- return new MetricsEventListener();
+ return new MetricsEventListenerProvider();
}
@Override
@@ -29,6 +29,6 @@ public class MetricsEventListenerFactory implements EventListenerProviderFactory
@Override
public String getId() {
- return MetricsEventListener.ID;
+ return MetricsEventListenerProvider.ID;
}
}
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/MetricsServerProvider.java b/src/main/java/coffee/_finally/keycloak_metrics/MetricsServerProvider.java
new file mode 100644
index 0000000..632556a
--- /dev/null
+++ b/src/main/java/coffee/_finally/keycloak_metrics/MetricsServerProvider.java
@@ -0,0 +1,8 @@
+package coffee._finally.keycloak_metrics;
+
+public class MetricsServerProvider implements ServerProvider {
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/MetricsServerProviderFactory.java b/src/main/java/coffee/_finally/keycloak_metrics/MetricsServerProviderFactory.java
new file mode 100644
index 0000000..f51f9e3
--- /dev/null
+++ b/src/main/java/coffee/_finally/keycloak_metrics/MetricsServerProviderFactory.java
@@ -0,0 +1,64 @@
+package coffee._finally.keycloak_metrics;
+
+import io.prometheus.client.exporter.HTTPServer;
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+import java.io.IOException;
+
+public class MetricsServerProviderFactory implements ServerProviderFactory {
+
+ private static HTTPServer metricsServer = null;
+
+ @Override
+ public Provider create(KeycloakSession session) {
+ return new MetricsServerProvider();
+ }
+
+ /**
+ * Only called once when the factory is first created. This config is pulled from keycloak_server.json
+ *
+ * @param config
+ */
+ @Override
+ public void init(Config.Scope config) {
+ // intentionally left blank
+ }
+
+ /**
+ * Called after all provider factories have been initialized
+ *
+ * @param factory
+ */
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ if (metricsServer == null) {
+ try {
+ metricsServer = new HTTPServer.Builder()
+ .withPort(9400)
+ .withRegistry(Metrics.getInstance().getRegistry())
+ .build();
+ } catch (IOException io) {
+ System.err.println("Unable to start HTTP server for metrics on port 9400: " + io.toString());
+ }
+ }
+ }
+
+ /**
+ * This is called when the server shuts down.
+ */
+ @Override
+ public void close() {
+ if (metricsServer != null) {
+ metricsServer.close();
+ }
+ }
+
+ @Override
+ public String getId() {
+ return "metrics-server-provider-spi";
+ }
+}
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/ServerProvider.java b/src/main/java/coffee/_finally/keycloak_metrics/ServerProvider.java
new file mode 100644
index 0000000..faa3c17
--- /dev/null
+++ b/src/main/java/coffee/_finally/keycloak_metrics/ServerProvider.java
@@ -0,0 +1,6 @@
+package coffee._finally.keycloak_metrics;
+
+import org.keycloak.provider.Provider;
+
+public interface ServerProvider extends Provider {
+}
\ No newline at end of file
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/ServerProviderFactory.java b/src/main/java/coffee/_finally/keycloak_metrics/ServerProviderFactory.java
new file mode 100644
index 0000000..a9449db
--- /dev/null
+++ b/src/main/java/coffee/_finally/keycloak_metrics/ServerProviderFactory.java
@@ -0,0 +1,6 @@
+package coffee._finally.keycloak_metrics;
+
+import org.keycloak.provider.ProviderFactory;
+
+public interface ServerProviderFactory extends ProviderFactory {
+}
diff --git a/src/main/java/coffee/_finally/keycloak_metrics/ServerSpi.java b/src/main/java/coffee/_finally/keycloak_metrics/ServerSpi.java
new file mode 100644
index 0000000..9ae7fc1
--- /dev/null
+++ b/src/main/java/coffee/_finally/keycloak_metrics/ServerSpi.java
@@ -0,0 +1,27 @@
+package coffee._finally.keycloak_metrics;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+public class ServerSpi implements Spi {
+ @Override
+ public boolean isInternal() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "metrics-server";
+ }
+
+ @Override
+ public Class extends Provider> getProviderClass() {
+ return ServerProvider.class;
+ }
+
+ @Override
+ public Class extends ProviderFactory> getProviderFactoryClass() {
+ return ServerProviderFactory.class;
+ }
+}
diff --git a/src/main/resources/META-INF/services/MANIFEST.MF b/src/main/resources/META-INF/services/MANIFEST.MF
new file mode 100644
index 0000000..0cf9c23
--- /dev/null
+++ b/src/main/resources/META-INF/services/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Dependencies: org.keycloak.keycloak-services
+Sealed: true
diff --git a/src/main/resources/META-INF/services/coffee._finally.keycloak_metrics.ServerSpi b/src/main/resources/META-INF/services/coffee._finally.keycloak_metrics.ServerSpi
new file mode 100644
index 0000000..e86a45f
--- /dev/null
+++ b/src/main/resources/META-INF/services/coffee._finally.keycloak_metrics.ServerSpi
@@ -0,0 +1 @@
+coffee._finally.keycloak_metrics.MetricsServerProviderFactory
\ No newline at end of file
diff --git a/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory b/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
index 04b5ccb..35e37d0 100644
--- a/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
+++ b/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
@@ -1 +1 @@
-coffee._finally.keycloak_metrics.MetricsEventListenerFactory
\ No newline at end of file
+coffee._finally.keycloak_metrics.MetricsEventListenerProviderFactory
\ No newline at end of file
diff --git a/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100644
index 0000000..2b581fd
--- /dev/null
+++ b/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1 @@
+coffee._finally.keycloak_metrics.ServerSpi
\ No newline at end of file
diff --git a/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
deleted file mode 100644
index e851359..0000000
--- a/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
+++ /dev/null
@@ -1 +0,0 @@
-coffee._finally.keycloak_metrics.MetricsEndpointFactory
\ No newline at end of file