From 7fd3380b19968c7e4ebf130335fd4a8f1047861f Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Tue, 22 Jul 2025 11:51:58 +0200 Subject: [PATCH] OpenTelemetry Tracing: Visualize JGroups communication (#39659) Closes #39658 Signed-off-by: Alexander Schwartz --- .../topics/changes/changes-26_4_0.adoc | 7 + .../factory/InfinispanTelemetryFactory.java | 34 +++ .../module/factory/OpenTelemetryService.java | 74 +++++++ .../module/factory/OpenTelemetrySpan.java | 42 ++++ .../keycloak/jgroups/header/TracerHeader.java | 98 +++++++++ .../jgroups/protocol/OPEN_TELEMETRY.java | 194 ++++++++++++++++++ ...ultCacheEmbeddedConfigProviderFactory.java | 1 + .../impl/embedded/JGroupsConfigurator.java | 19 +- .../org/keycloak/config/TracingOptions.java | 7 + .../mappers/CachingPropertyMappers.java | 2 +- .../mappers/TracingPropertyMappers.java | 11 +- ...andDistTest.testExportHelpAll.approved.txt | 3 + ...andDistTest.testImportHelpAll.approved.txt | 3 + ...dDistTest.testStartDevHelpAll.approved.txt | 3 + ...mandDistTest.testStartHelpAll.approved.txt | 3 + ...est.testStartOptimizedHelpAll.approved.txt | 3 + ...dateCompatibilityCheckHelpAll.approved.txt | 3 + ...eCompatibilityMetadataHelpAll.approved.txt | 3 + 18 files changed, 503 insertions(+), 7 deletions(-) create mode 100644 model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/InfinispanTelemetryFactory.java create mode 100644 model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetryService.java create mode 100644 model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetrySpan.java create mode 100644 model/infinispan/src/main/java/org/keycloak/jgroups/header/TracerHeader.java create mode 100644 model/infinispan/src/main/java/org/keycloak/jgroups/protocol/OPEN_TELEMETRY.java diff --git a/docs/documentation/upgrading/topics/changes/changes-26_4_0.adoc b/docs/documentation/upgrading/topics/changes/changes-26_4_0.adoc index 46b75ba6073..1919a771534 100644 --- a/docs/documentation/upgrading/topics/changes/changes-26_4_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-26_4_0.adoc @@ -63,6 +63,13 @@ In order to maintain backwards compatibility, {project_name}'s upgrade will modi For more information about client configuration, please see link:{adminguide_link}#_client-saml-configuration[Creating a SAML client] chapter in the {adminguide_name}. +=== Tracing extended for embedded Infinispan caches + +When tracing is enabled, now also calls to other nodes of a {project_name} cluster will create spans in the traces. + +To disable this kind of tracing, set the option `tracing-infinispan-enabled` to `false`. + + // ------------------------ Deprecated features ------------------------ // == Deprecated features diff --git a/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/InfinispanTelemetryFactory.java b/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/InfinispanTelemetryFactory.java new file mode 100644 index 00000000000..10ff59d99a5 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/InfinispanTelemetryFactory.java @@ -0,0 +1,34 @@ +package org.keycloak.infinispan.module.factory; + +import io.opentelemetry.api.OpenTelemetry; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.spi.CDI; +import org.infinispan.factories.AbstractComponentFactory; +import org.infinispan.factories.AutoInstantiableFactory; +import org.infinispan.factories.annotations.DefaultFactoryFor; +import org.infinispan.factories.scopes.Scope; +import org.infinispan.factories.scopes.Scopes; +import org.infinispan.telemetry.InfinispanTelemetry; +import org.infinispan.telemetry.impl.DisabledInfinispanTelemetry; + +@Scope(Scopes.GLOBAL) +@DefaultFactoryFor(classes = InfinispanTelemetry.class) +public class InfinispanTelemetryFactory extends AbstractComponentFactory implements AutoInstantiableFactory { + + @Override + public Object construct(String componentName) { + CDI current; + try { + current = CDI.current(); + } catch (IllegalStateException e) { + // No CDI context, assume tracing is not available + return new DisabledInfinispanTelemetry(); + } + Instance selector = current.select(OpenTelemetry.class); + if (!selector.isResolvable()) { + return new DisabledInfinispanTelemetry(); + } else { + return new OpenTelemetryService(selector.get()); + } + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetryService.java b/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetryService.java new file mode 100644 index 00000000000..90d5bbcdcf8 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetryService.java @@ -0,0 +1,74 @@ +package org.keycloak.infinispan.module.factory; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapGetter; +import org.infinispan.telemetry.InfinispanSpan; +import org.infinispan.telemetry.InfinispanSpanAttributes; +import org.infinispan.telemetry.InfinispanSpanContext; +import org.infinispan.telemetry.InfinispanTelemetry; + +public class OpenTelemetryService implements InfinispanTelemetry, TextMapGetter { + + private static final String INFINISPAN_SERVER_TRACING_NAME = "org.infinispan.server.tracing"; + private static final String INFINISPAN_SERVER_TRACING_VERSION = "1.0.0"; + + private final Tracer tracer; + private volatile String nodeName = "n/a"; + + public OpenTelemetryService(OpenTelemetry openTelemetry) { + this.tracer = openTelemetry.getTracer(INFINISPAN_SERVER_TRACING_NAME, INFINISPAN_SERVER_TRACING_VERSION); + } + + @Override + public InfinispanSpan startTraceRequest(String operationName, InfinispanSpanAttributes attributes) { + // The original Infininspan implementation allows for filtering via the trace attributes. We don't support this here and instead trace everything. + + var builder = tracer.spanBuilder(operationName) + .setSpanKind(SpanKind.SERVER); + // the parent context is inherited automatically, + // because the parent span is created in the same process + + return createOpenTelemetrySpan(builder, attributes); + } + + @Override + public InfinispanSpan startTraceRequest(String operationName, InfinispanSpanAttributes attributes, InfinispanSpanContext context) { + // The original Infinispan implementation allows for filtering via the trace attributes. We don't support this here and instead trace everything + + var builder = tracer.spanBuilder(operationName) + .setSpanKind(SpanKind.SERVER) + .setParent(Context.current().with(Span.current())); + + return createOpenTelemetrySpan(builder, attributes); + } + + @Override + public void setNodeName(String nodeName) { + if (nodeName != null) { + this.nodeName = nodeName; + } + } + + private InfinispanSpan createOpenTelemetrySpan(SpanBuilder builder, InfinispanSpanAttributes attributes) { + attributes.cacheName().ifPresent(cacheName -> builder.setAttribute("cache", cacheName)); + builder.setAttribute("category", attributes.category().toString()); + builder.setAttribute("server.address", nodeName); + return new OpenTelemetrySpan<>(builder.startSpan()); + } + + @Override + public Iterable keys(InfinispanSpanContext ctx) { + return ctx.keys(); + } + + @Override + public String get(InfinispanSpanContext ctx, String key) { + assert ctx != null; + return ctx.getKey(key); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetrySpan.java b/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetrySpan.java new file mode 100644 index 00000000000..5a51da7de10 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/infinispan/module/factory/OpenTelemetrySpan.java @@ -0,0 +1,42 @@ +package org.keycloak.infinispan.module.factory; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Scope; +import org.infinispan.telemetry.InfinispanSpan; +import org.infinispan.telemetry.SafeAutoClosable; + +import java.util.Objects; + +public class OpenTelemetrySpan implements InfinispanSpan { + + private final Span span; + private final Scope scope; + + public OpenTelemetrySpan(Span span) { + this.span = Objects.requireNonNull(span); + // TODO: This is actually wrong if you are doing asynchronous calls, but it allows the JGroups calls to be nested + // This should be fixed in ISPN 16+ so that it is no longer needed + // https://github.com/infinispan/infinispan/issues/15287 + this.scope = span.makeCurrent(); + } + + @Override + public SafeAutoClosable makeCurrent() { + //noinspection resource + Scope scope = span.makeCurrent(); + return scope::close; + } + + @Override + public void complete() { + scope.close(); + span.end(); + } + + @Override + public void recordException(Throwable throwable) { + span.setStatus(StatusCode.ERROR, "Error during the cache request processing"); + span.recordException(throwable); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/jgroups/header/TracerHeader.java b/model/infinispan/src/main/java/org/keycloak/jgroups/header/TracerHeader.java new file mode 100644 index 00000000000..b386eaf86a5 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/jgroups/header/TracerHeader.java @@ -0,0 +1,98 @@ +/* + * Copyright 2025 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.jgroups.header; + +import org.jgroups.Header; +import org.jgroups.util.Util; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +/** + * Header which carries an OpenTelemetry {@link io.opentelemetry.api.trace.Span} between requests and responses + * + * @author Bela Ban + * @since 1.0.0 + */ +public class TracerHeader extends Header { + public static final short ID = 1050; + protected final Map ctx = new HashMap<>(); + + public TracerHeader() { + } + + public short getMagicId() { + return ID; + } + + public Supplier create() { + return TracerHeader::new; + } + + public void put(String key, String value) { + ctx.put(key, value); + } + + public String get(String key) { + return ctx.get(key); + } + + public Set keys() { + return ctx.keySet(); + } + + public int serializedSize() { + int size = Integer.BYTES; + int num_attrs = ctx.size(); + if (num_attrs > 0) { + for (Map.Entry entry : ctx.entrySet()) { + String key = entry.getKey(); + String val = entry.getValue(); + size += Util.size(key) + Util.size(val); + } + } + return size; + } + + public void writeTo(DataOutput out) throws IOException { + out.writeInt(ctx.size()); + if (!ctx.isEmpty()) { + for (Map.Entry e : ctx.entrySet()) { + Util.writeString(e.getKey(), out); + Util.writeString(e.getValue(), out); + } + } + } + + public void readFrom(DataInput in) throws IOException { + int size = in.readInt(); + if (size > 0) { + for (int i = 0; i < size; i++) + ctx.put(Util.readString(in), Util.readString(in)); + } + } + + public String toString() { + return ctx.toString(); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/jgroups/protocol/OPEN_TELEMETRY.java b/model/infinispan/src/main/java/org/keycloak/jgroups/protocol/OPEN_TELEMETRY.java new file mode 100644 index 00000000000..168b364b698 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/jgroups/protocol/OPEN_TELEMETRY.java @@ -0,0 +1,194 @@ +/* + * Copyright 2025 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.jgroups.protocol; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.context.propagation.TextMapGetter; +import org.jgroups.Message; +import org.jgroups.Version; +import org.jgroups.annotations.MBean; +import org.jgroups.annotations.Property; +import org.jgroups.stack.Protocol; +import org.jgroups.util.MessageBatch; +import org.keycloak.jgroups.header.TracerHeader; + +import java.util.ArrayList; +import java.util.List; + +/** + * Provides Open Telemetry (https://opentelemetry.io/) tracing for JGroups. It should be placed just above the + * transport.
+ * When a message is sent, a {@link TracerHeader} is added with the (optional) parent span. + * When received a new span is started (as a child span, if the parent span in the header is non-null), and ended when + * the the thread returns. + * + * @author Bela Ban + * @since 1.0.0 + */ +@MBean(description = "Records OpenTelemetry traces of sent and received messages") +public class OPEN_TELEMETRY extends Protocol { + public static final short OPEN_TELEMETRY_ID = 1026; + protected OpenTelemetry otel; + protected Tracer tracer; + + @Property(description = "When active, traces are recorded, otherwise not") + protected boolean active = true; + + public boolean active() { + return active; + } + + public OPEN_TELEMETRY active(boolean f) { + active = activate(f); + return this; + } + + public void start() throws Exception { + super.start(); + activate(active); + } + + public Object down(Message msg) { + if (!active || !Span.current().getSpanContext().isValid()) + return down_prot.down(msg); + + SpanBuilder spanBuilder = tracer.spanBuilder("JGroups.sendSingleMessage"); + if (Span.current().isRecording()) { + if (msg.getDest() != null) { + spanBuilder.setAttribute("kc.jgroups.dest", msg.getDest().toString()); + } + if (msg.getSrc() != null) { + spanBuilder.setAttribute("kc.jgroups.src", msg.getSrc().toString()); + } + } + Span span = spanBuilder.startSpan(); + try (var ignored = span.makeCurrent()) { + TracerHeader hdr = new TracerHeader(); + populateHeader(hdr); // will populate if a span exists (created by the caller) + msg.putHeader(OPEN_TELEMETRY_ID, hdr); + return down_prot.down(msg); + } catch (Throwable t) { + span.setStatus(StatusCode.ERROR, String.format("failed delivering single message to %s", msg.dest())); + span.recordException(t); + throw t; + } finally { + span.end(); + } + } + + + public Object up(Message msg) { + if (!active) + return up_prot.up(msg); + + TracerHeader hdr = msg.getHeader(OPEN_TELEMETRY_ID); + if (hdr != null) { + Context extractedContext = otel.getPropagators().getTextMapPropagator() + .extract(Context.current(), hdr, TEXT_MAP_GETTER); + + Span span = tracer.spanBuilder("JGroups.deliverSingleMessage") + .setSpanKind(SpanKind.SERVER) + .setParent(extractedContext).startSpan(); + + try (Scope ignored = span.makeCurrent()) { + span.setAttribute("from", msg.src().toString()); + return up_prot.up(msg); + } catch (Throwable t) { + span.setStatus(StatusCode.ERROR, String.format("failed delivering single message from %s", msg.src())); + span.recordException(t); + throw t; + } finally { + span.end(); + } + } else { + return up_prot.up(msg); + } + } + + public void up(MessageBatch batch) { + if (!active) { + if (!batch.isEmpty()) + up_prot.up(batch); + return; + } + List spans = new ArrayList<>(batch.size()); + int index = 0, batch_size = batch.size(); + for (Message msg : batch) { + index++; + TracerHeader hdr = msg.getHeader(OPEN_TELEMETRY_ID); + if (hdr != null) { + Context extractedContext = otel.getPropagators().getTextMapPropagator() + .extract(Context.current(), hdr, TEXT_MAP_GETTER); + + Span span = tracer.spanBuilder("deliver-batched-msg") + .setSpanKind(SpanKind.SERVER) + .setParent(extractedContext).startSpan(); + span.setAttribute("batch-msg", String.format("%d/%d", index, batch_size)); + spans.add(span); + } + } + try { + if (!batch.isEmpty()) + up_prot.up(batch); + } catch (Throwable t) { + spans.forEach(s -> { + s.setStatus(StatusCode.ERROR, String.format("failed delivering batched message from %s", batch.sender())) + .recordException(t); + }); + throw t; + } finally { + spans.forEach(Span::end); + } + } + + protected static void populateHeader(TracerHeader hdr) { + // Inject the request with the *current* Context, which contains our current Span. + W3CTraceContextPropagator.getInstance().inject(Context.current(), hdr, (carrier, key, val) -> hdr.put(key, val)); + } + + protected static final TextMapGetter TEXT_MAP_GETTER = + new TextMapGetter<>() { + @Override + public String get(TracerHeader carrier, String key) { + return carrier.get(key); + } + + @Override + public Iterable keys(TracerHeader carrier) { + return carrier.keys(); + } + }; + + protected boolean activate(boolean flag) { + if (flag && otel == null) + otel = GlobalOpenTelemetry.get(); + if (flag && tracer == null) + tracer = otel.getTracer("org.jgroups.trace", Version.printVersion()); + return flag; + } + +} diff --git a/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/DefaultCacheEmbeddedConfigProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/DefaultCacheEmbeddedConfigProviderFactory.java index 26e846b7628..3cb833cb181 100644 --- a/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/DefaultCacheEmbeddedConfigProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/DefaultCacheEmbeddedConfigProviderFactory.java @@ -74,6 +74,7 @@ public class DefaultCacheEmbeddedConfigProviderFactory implements CacheEmbeddedC // Configuration public static final String CONFIG = "configFile"; private static final String METRICS = "metricsEnabled"; + public static final String TRACING = "tracingEnabled"; private static final String HISTOGRAMS = "metricsHistogramsEnabled"; public static final String STACK = "stack"; public static final String NODE_NAME = "nodeName"; diff --git a/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/JGroupsConfigurator.java b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/JGroupsConfigurator.java index 3af902bfc97..89bdb1ae430 100644 --- a/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/JGroupsConfigurator.java +++ b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/JGroupsConfigurator.java @@ -60,6 +60,8 @@ import org.keycloak.connections.jpa.JpaConnectionProviderFactory; import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.infinispan.util.InfinispanUtils; import org.keycloak.jgroups.protocol.KEYCLOAK_JDBC_PING2; +import org.keycloak.jgroups.protocol.OPEN_TELEMETRY; +import org.keycloak.jgroups.header.TracerHeader; import org.keycloak.models.KeycloakSession; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigurationBuilder; @@ -82,6 +84,8 @@ public final class JGroupsConfigurator { // Use custom Keycloak JDBC_PING implementation that workarounds issue https://issues.redhat.com/browse/JGRP-2870 // The id 1025 follows this instruction: https://github.com/belaban/JGroups/blob/38219e9ec1c629fa2f7929e3b53d1417d8e60b61/conf/jg-protocol-ids.xml#L85 ClassConfigurator.addProtocol((short) 1025, KEYCLOAK_JDBC_PING2.class); + ClassConfigurator.addProtocol((short) 1026, OPEN_TELEMETRY.class); + ClassConfigurator.add(TracerHeader.ID, TracerHeader.class); } /** @@ -97,7 +101,8 @@ public final class JGroupsConfigurator { transportOf(holder).stack(stack); } configureTransport(config); - configureDiscovery(holder, session); + boolean tracingEnabled = config.getBoolean(DefaultCacheEmbeddedConfigProviderFactory.TRACING, false); + configureDiscovery(holder, session, tracingEnabled); configureTls(holder, session); warnDeprecatedStack(holder); } @@ -177,7 +182,7 @@ public final class JGroupsConfigurator { return socketFactory; } - private static void configureDiscovery(ConfigurationBuilderHolder holder, KeycloakSession session) { + private static void configureDiscovery(ConfigurationBuilderHolder holder, KeycloakSession session, boolean tracingEnabled) { var stackXmlAttribute = transportStackOf(holder); if (stackXmlAttribute.isModified() && !isJdbcPingStack(stackXmlAttribute.get())) { logger.debugf("Custom stack configured (%s). JDBC_PING discovery disabled.", stackXmlAttribute.get()); @@ -194,7 +199,7 @@ public final class JGroupsConfigurator { var stackName = transportStackOf(holder).get(); var isUdp = stackName.endsWith("udp"); var tableName = JpaUtils.getTableNameForNativeQuery("JGROUPS_PING", em); - var stack = getProtocolConfigurations(tableName, isUdp); + var stack = getProtocolConfigurations(tableName, isUdp, tracingEnabled); var connectionFactory = (JpaConnectionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(JpaConnectionProvider.class); holder.addJGroupsStack(new JpaFactoryAwareJGroupsChannelConfigurator(stackName, stack, connectionFactory, isUdp), null); @@ -202,7 +207,7 @@ public final class JGroupsConfigurator { JGroupsConfigurator.logger.info("JGroups JDBC_PING discovery enabled."); } - private static List getProtocolConfigurations(String tableName, boolean udp) { + private static List getProtocolConfigurations(String tableName, boolean udp, boolean tracingEnabled) { var list = new ArrayList(udp ? 1 : 2); list.add(new ProtocolConfiguration(KEYCLOAK_JDBC_PING2.class.getName(), Map.of( @@ -224,6 +229,12 @@ public final class JGroupsConfigurator { if (!udp && InfinispanUtils.isVirtualThreadsEnabled()) list.add(new ProtocolConfiguration(TCP.class.getSimpleName(), Map.of("bundler_type", "per-destination"))); + if (tracingEnabled) { + list.add(new ProtocolConfiguration(OPEN_TELEMETRY.class.getName(), Map.of( + "stack.combine", "INSERT_ABOVE", + "stack.position", udp ? "UDP" : "TCP" + ))); + } return list; } diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/TracingOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/TracingOptions.java index 5bebe734939..84b538660f0 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/TracingOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/TracingOptions.java @@ -85,4 +85,11 @@ public class TracingOptions { .description("OpenTelemetry compression method used to compress payloads. If unset, compression is disabled.") .defaultValue(TracingCompression.none) .build(); + + public static final Option TRACING_INFINISPAN_ENABLED = new OptionBuilder<>("tracing-infinispan-enabled", Boolean.class) + .category(OptionCategory.TRACING) + .description("Enables the OpenTelemetry tracing for embedded Infinispan.") + .defaultValue(true) + .build(); + } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java index 20f452724d4..883430abc00 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java @@ -185,7 +185,7 @@ final class CachingPropertyMappers { return getOptionalKcValue(CachingOptions.CACHE_REMOTE_HOST_PROPERTY).isPresent(); } - private static boolean cacheSetToInfinispan() { + public static boolean cacheSetToInfinispan() { if (InfinispanUtils.isRemoteInfinispan()) { return false; } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TracingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TracingPropertyMappers.java index 30bb58ceb81..cf5c4dd695b 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TracingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TracingPropertyMappers.java @@ -18,6 +18,7 @@ package org.keycloak.quarkus.runtime.configuration.mappers; import org.keycloak.common.Profile; +import org.keycloak.config.TracingOptions; import org.keycloak.quarkus.runtime.cli.PropertyException; import org.keycloak.quarkus.runtime.configuration.Configuration; import org.keycloak.utils.StringUtil; @@ -29,6 +30,7 @@ import java.net.URL; import static org.keycloak.config.TracingOptions.TRACING_COMPRESSION; import static org.keycloak.config.TracingOptions.TRACING_ENABLED; import static org.keycloak.config.TracingOptions.TRACING_ENDPOINT; +import static org.keycloak.config.TracingOptions.TRACING_INFINISPAN_ENABLED; import static org.keycloak.config.TracingOptions.TRACING_JDBC_ENABLED; import static org.keycloak.config.TracingOptions.TRACING_PROTOCOL; import static org.keycloak.config.TracingOptions.TRACING_RESOURCE_ATTRIBUTES; @@ -91,6 +93,11 @@ public class TracingPropertyMappers { .mapFrom(TRACING_ENABLED) .isEnabled(TracingPropertyMappers::isTracingEnabled, TRACING_ENABLED_MSG) .to("quarkus.datasource.jdbc.telemetry") + .build(), + fromOption(TRACING_INFINISPAN_ENABLED) + .mapFrom(TracingOptions.TRACING_ENABLED) + .to("kc.spi-cache-embedded--default--tracing-enabled") + .isEnabled(TracingPropertyMappers::isTracingAndEmbeddedInfinispanEnabled, "tracing and embedded Infinispan is enabled") .build() }; } @@ -129,8 +136,8 @@ public class TracingPropertyMappers { return Configuration.isTrue(TRACING_ENABLED); } - public static boolean isTracingJdbcEnabled() { - return Configuration.isTrue(TRACING_JDBC_ENABLED); + public static boolean isTracingAndEmbeddedInfinispanEnabled() { + return Configuration.isTrue(TRACING_ENABLED) && CachingPropertyMappers.cacheSetToInfinispan(); } private static boolean isValidUrl(String url) { diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt index a1db7b066a0..a5a47cb6478 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testExportHelpAll.approved.txt @@ -368,6 +368,9 @@ Tracing: --tracing-endpoint OpenTelemetry endpoint to connect to. Default: http://localhost:4317. Available only when Tracing is enabled. +--tracing-infinispan-enabled + Enables the OpenTelemetry tracing for embedded Infinispan. Default: true. + Available only when tracing and embedded Infinispan is enabled. --tracing-jdbc-enabled Enables the OpenTelemetry JDBC tracing. Default: true. Available only when Tracing is enabled. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt index 34b82a7ddf6..bd3dd223241 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testImportHelpAll.approved.txt @@ -368,6 +368,9 @@ Tracing: --tracing-endpoint OpenTelemetry endpoint to connect to. Default: http://localhost:4317. Available only when Tracing is enabled. +--tracing-infinispan-enabled + Enables the OpenTelemetry tracing for embedded Infinispan. Default: true. + Available only when tracing and embedded Infinispan is enabled. --tracing-jdbc-enabled Enables the OpenTelemetry JDBC tracing. Default: true. Available only when Tracing is enabled. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt index d4ea1cc90d7..00492d532e9 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt @@ -599,6 +599,9 @@ Tracing: --tracing-endpoint OpenTelemetry endpoint to connect to. Default: http://localhost:4317. Available only when Tracing is enabled. +--tracing-infinispan-enabled + Enables the OpenTelemetry tracing for embedded Infinispan. Default: true. + Available only when tracing and embedded Infinispan is enabled. --tracing-jdbc-enabled Enables the OpenTelemetry JDBC tracing. Default: true. Available only when Tracing is enabled. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt index 870b52423a7..e7c786fbb9b 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt @@ -600,6 +600,9 @@ Tracing: --tracing-endpoint OpenTelemetry endpoint to connect to. Default: http://localhost:4317. Available only when Tracing is enabled. +--tracing-infinispan-enabled + Enables the OpenTelemetry tracing for embedded Infinispan. Default: true. + Available only when tracing and embedded Infinispan is enabled. --tracing-jdbc-enabled Enables the OpenTelemetry JDBC tracing. Default: true. Available only when Tracing is enabled. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt index 2e6ccf7d883..437a09b2603 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt @@ -531,6 +531,9 @@ Tracing: --tracing-endpoint OpenTelemetry endpoint to connect to. Default: http://localhost:4317. Available only when Tracing is enabled. +--tracing-infinispan-enabled + Enables the OpenTelemetry tracing for embedded Infinispan. Default: true. + Available only when tracing and embedded Infinispan is enabled. --tracing-protocol OpenTelemetry protocol used for the telemetry data. Possible values are: grpc, http/protobuf. Default: grpc. Available only when Tracing is enabled. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt index 18da075424a..93ae1871135 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt @@ -599,6 +599,9 @@ Tracing: --tracing-endpoint OpenTelemetry endpoint to connect to. Default: http://localhost:4317. Available only when Tracing is enabled. +--tracing-infinispan-enabled + Enables the OpenTelemetry tracing for embedded Infinispan. Default: true. + Available only when tracing and embedded Infinispan is enabled. --tracing-jdbc-enabled Enables the OpenTelemetry JDBC tracing. Default: true. Available only when Tracing is enabled. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt index 813bdffba8d..dd624e5dc8f 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt @@ -597,6 +597,9 @@ Tracing: --tracing-endpoint OpenTelemetry endpoint to connect to. Default: http://localhost:4317. Available only when Tracing is enabled. +--tracing-infinispan-enabled + Enables the OpenTelemetry tracing for embedded Infinispan. Default: true. + Available only when tracing and embedded Infinispan is enabled. --tracing-jdbc-enabled Enables the OpenTelemetry JDBC tracing. Default: true. Available only when Tracing is enabled.