Add a span per rest resource, thereby showing subresources (#35420)

* Add a span per rest resource, thereby showing subresources

Closes #35419

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Alexander Schwartz 2024-11-29 10:09:34 +01:00 committed by GitHub
parent 8cad78b1df
commit 909db9e65e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 103 additions and 2 deletions

View File

@ -80,6 +80,7 @@ import org.keycloak.config.HttpOptions;
import org.keycloak.config.ManagementOptions;
import org.keycloak.config.MetricsOptions;
import org.keycloak.config.SecurityOptions;
import org.keycloak.config.TracingOptions;
import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.connections.jpa.JpaConnectionSpi;
@ -104,6 +105,7 @@ import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakHandlerChainCustomizer;
import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakTracingCustomizer;
import org.keycloak.quarkus.runtime.services.health.KeycloakReadyHealthCheck;
import org.keycloak.quarkus.runtime.storage.database.jpa.NamedJpaConnectionProviderFactory;
import org.keycloak.quarkus.runtime.themes.FlatClasspathThemeResourceProviderFactory;
@ -636,13 +638,19 @@ class KeycloakProcessor {
LoadBalancerResource.class.getName())), false));
}
KeycloakHandlerChainCustomizer chainCustomizer = new KeycloakHandlerChainCustomizer();
ArrayList<HandlerChainCustomizer> chainCustomizers = new ArrayList<>();
chainCustomizers.add(new KeycloakHandlerChainCustomizer());
if (Configuration.isTrue(TracingOptions.TRACING_ENABLED)) {
chainCustomizers.add(new KeycloakTracingCustomizer());
}
scanner.produce(new MethodScannerBuildItem(new MethodScanner() {
@Override
public List<HandlerChainCustomizer> scan(MethodInfo method, ClassInfo actualEndpointClass,
Map<String, Object> methodContext) {
return List.of(chainCustomizer);
return chainCustomizers;
}
}));
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.quarkus.runtime.integration.resteasy;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import jakarta.enterprise.inject.spi.CDI;
import org.apache.commons.lang3.StringUtils;
import org.jboss.resteasy.reactive.common.model.ResourceClass;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.model.HandlerChainCustomizer;
import org.jboss.resteasy.reactive.server.model.ServerResourceMethod;
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;
import org.keycloak.common.Version;
import java.util.ArrayList;
import java.util.List;
public final class KeycloakTracingCustomizer implements HandlerChainCustomizer {
private static class StartHandler implements ServerRestHandler {
private final String className;
private final String methodName;
private final String spanName;
public StartHandler(String className, String methodName) {
this.className = className;
this.methodName = methodName;
this.spanName = StringUtils.substringAfterLast(className, ".") + "." + methodName;
}
@Override
public void handle(ResteasyReactiveRequestContext requestContext) {
if (requestContext.getProperty("span") != null) {
return;
}
OpenTelemetry openTelemetry = CDI.current().select(OpenTelemetry.class).get();
Tracer myTracer = openTelemetry.getTracer(this.getClass().getName(), Version.VERSION);
SpanBuilder spanBuilder = myTracer.spanBuilder(spanName);
spanBuilder.setParent(Context.current().with(Span.current()));
spanBuilder.setAttribute("code.function", methodName);
spanBuilder.setAttribute("code.namespace", className);
Span span = spanBuilder.startSpan();
requestContext.setProperty("span", span);
}
}
private static class EndHandler implements ServerRestHandler {
@Override
public void handle(ResteasyReactiveRequestContext requestContext) {
Span span = (Span) requestContext.getProperty("span");
if (span != null) {
span.end();
}
requestContext.removeProperty("span");
}
}
@Override
public List<ServerRestHandler> handlers(Phase phase, ResourceClass resourceClass,
ServerResourceMethod resourceMethod) {
List<ServerRestHandler> handlers = new ArrayList<>();
switch (phase) {
case BEFORE_METHOD_INVOKE:
handlers.add(new StartHandler(resourceClass.getClassName(), resourceMethod.getName()));
break;
case AFTER_METHOD_INVOKE:
handlers.add(new EndHandler());
break;
}
return handlers;
}
}