Compare commits

..

1 Commits

Author SHA1 Message Date
89d758ecae
feat: migrate to using an own SPI and implementing that 2024-08-24 17:18:14 +02:00
19 changed files with 151 additions and 79 deletions

View File

@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="keycloak-metrics [package]" type="MavenRunConfiguration" factoryName="Maven" nameIsGenerated="true"> <configuration default="false" name="keycloak-metrics [clean,package]" type="MavenRunConfiguration" factoryName="Maven" nameIsGenerated="true">
<MavenSettings> <MavenSettings>
<option name="myGeneralSettings" /> <option name="myGeneralSettings" />
<option name="myRunnerSettings" /> <option name="myRunnerSettings" />
@ -11,6 +11,7 @@
</option> </option>
<option name="goals"> <option name="goals">
<list> <list>
<option value="clean" />
<option value="package" /> <option value="package" />
</list> </list>
</option> </option>

27
.idea/workspace.xml generated
View File

@ -5,13 +5,17 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="0b19344d-a6be-4de4-b466-20f9855520b4" name="Changes" comment=""> <list default="true" id="0b19344d-a6be-4de4-b466-20f9855520b4" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/runConfigurations/keycloak_metrics__package_.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/runConfigurations/keycloak_metrics__clean_package_.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/encodings.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/runConfigurations/keycloak_metrics__package_.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/KeycloakMetricsServer.java" beforeDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/Metrics.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/Metrics.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpoint.java" beforeDir="false" />
<change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/MetricsEndpointFactory.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProvider.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerFactory.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/coffee/_finally/keycloak_metrics/MetricsEventListenerProviderFactory.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory" beforeDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -22,12 +26,18 @@
<option name="RECENT_TEMPLATES"> <option name="RECENT_TEMPLATES">
<list> <list>
<option value="Class" /> <option value="Class" />
<option value="Interface" />
</list> </list>
</option> </option>
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
<component name="HighlightingSettingsPerFile">
<setting file="jar://$MAVEN_REPOSITORY$/org/keycloak/keycloak-server-spi-private/24.0.0/keycloak-server-spi-private-24.0.0-sources.jar!/org/keycloak/events/admin/AdminEvent.java" root0="FORCE_HIGHLIGHTING" />
<setting file="jar://$MAVEN_REPOSITORY$/org/keycloak/keycloak-server-spi-private/24.0.0/keycloak-server-spi-private-24.0.0-sources.jar!/org/keycloak/events/admin/OperationType.java" root0="FORCE_HIGHLIGHTING" />
<setting file="jar://$MAVEN_REPOSITORY$/org/keycloak/keycloak-server-spi-private/24.0.0/keycloak-server-spi-private-24.0.0-sources.jar!/org/keycloak/events/admin/ResourceType.java" root0="FORCE_HIGHLIGHTING" />
</component>
<component name="MavenImportPreferences"> <component name="MavenImportPreferences">
<option name="importingSettings"> <option name="importingSettings">
<MavenImportingSettings> <MavenImportingSettings>
@ -52,6 +62,7 @@
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent"><![CDATA[{
"keyToString": { "keyToString": {
"Downloaded.Files.Path.Enabled": "false", "Downloaded.Files.Path.Enabled": "false",
"Maven.keycloak-metrics [clean,package].executor": "Run",
"Maven.keycloak-metrics [compile].executor": "Run", "Maven.keycloak-metrics [compile].executor": "Run",
"Maven.keycloak-metrics [package].executor": "Run", "Maven.keycloak-metrics [package].executor": "Run",
"Maven.keycloak-metrics.executor": "Run", "Maven.keycloak-metrics.executor": "Run",
@ -65,7 +76,7 @@
"project.structure.last.edited": "Libraries", "project.structure.last.edited": "Libraries",
"project.structure.proportion": "0.15", "project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.3908046", "project.structure.side.proportion": "0.3908046",
"settings.editor.selected.configurable": "preferences.lookFeel" "settings.editor.selected.configurable": "reference.settings.project.maven.repository.indices"
} }
}]]></component> }]]></component>
<component name="RecentsManager"> <component name="RecentsManager">

View File

@ -48,6 +48,11 @@
<artifactId>simpleclient_common</artifactId> <artifactId>simpleclient_common</artifactId>
<version>0.16.0</version> <version>0.16.0</version>
</dependency> </dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_httpserver</artifactId>
<version>0.16.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,2 +0,0 @@
public class KeycloakMetricsServer {
}

View File

@ -52,6 +52,10 @@ public class Metrics {
return METRICS_INSTANCE; return METRICS_INSTANCE;
} }
public CollectorRegistry getRegistry() {
return registry;
}
public static void increment(String metric) { public static void increment(String metric) {
Collector collector = metrics.get(metric); Collector collector = metrics.get(metric);
if (collector == null) { if (collector == null) {

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -4,7 +4,7 @@ import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.admin.AdminEvent; import org.keycloak.events.admin.AdminEvent;
public class MetricsEventListener implements EventListenerProvider { public class MetricsEventListenerProvider implements EventListenerProvider {
public static final String ID = "metrics-listener"; public static final String ID = "metrics-listener";

View File

@ -6,10 +6,10 @@ 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;
public class MetricsEventListenerFactory implements EventListenerProviderFactory { public class MetricsEventListenerProviderFactory implements EventListenerProviderFactory {
@Override @Override
public EventListenerProvider create(KeycloakSession keycloakSession) { public EventListenerProvider create(KeycloakSession keycloakSession) {
return new MetricsEventListener(); return new MetricsEventListenerProvider();
} }
@Override @Override
@ -29,6 +29,6 @@ public class MetricsEventListenerFactory implements EventListenerProviderFactory
@Override @Override
public String getId() { public String getId() {
return MetricsEventListener.ID; return MetricsEventListenerProvider.ID;
} }
} }

View File

@ -0,0 +1,8 @@
package coffee._finally.keycloak_metrics;
public class MetricsServerProvider implements ServerProvider {
@Override
public void close() {
}
}

View File

@ -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";
}
}

View File

@ -0,0 +1,6 @@
package coffee._finally.keycloak_metrics;
import org.keycloak.provider.Provider;
public interface ServerProvider extends Provider {
}

View File

@ -0,0 +1,6 @@
package coffee._finally.keycloak_metrics;
import org.keycloak.provider.ProviderFactory;
public interface ServerProviderFactory extends ProviderFactory {
}

View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Dependencies: org.keycloak.keycloak-services
Sealed: true

View File

@ -0,0 +1 @@
coffee._finally.keycloak_metrics.MetricsServerProviderFactory

View File

@ -1 +1 @@
coffee._finally.keycloak_metrics.MetricsEventListenerFactory coffee._finally.keycloak_metrics.MetricsEventListenerProviderFactory

View File

@ -0,0 +1 @@
coffee._finally.keycloak_metrics.ServerSpi

View File

@ -1 +0,0 @@
coffee._finally.keycloak_metrics.MetricsEndpointFactory