mirror of
https://github.com/keycloak/keycloak.git
synced 2026-04-26 00:30:35 -04:00
Introduce traceId to freemarker attributes
Closes #44090 Closes #34435 Signed-off-by: Simon Levermann <github@simon.slevermann.de>
This commit is contained in:
parent
5e12f2939e
commit
f4225b4f9b
7 changed files with 111 additions and 0 deletions
|
|
@ -183,4 +183,13 @@ You can filter out the required traces in your tracing backend based on their ta
|
|||
|
||||
NOTE: The `KC_TRACING_RESOURCE_ATTRIBUTES` variable always contains (if not overridden) the `k8s.namespace.name` attribute representing the current namespace.
|
||||
|
||||
== Including trace information in Freemarker templates
|
||||
|
||||
When tracing is enabled, you can include the trace ID in the Freemarker templates of the login theme by using the `traceId` variable.
|
||||
|
||||
By default, the base login theme includes the trace ID in the `error.ftl` template.
|
||||
With the trace ID included in the error page, users can report it when they encounter an error, which can help you quickly identify and investigate additional recorded information for that operation.
|
||||
|
||||
You will then find the trace ID in all logged messages for this request, and in your tracing system if this trace was sampled.
|
||||
|
||||
</@tmpl.guide>
|
||||
|
|
|
|||
|
|
@ -101,10 +101,12 @@ import org.keycloak.theme.beans.MessageBean;
|
|||
import org.keycloak.theme.beans.MessageFormatterMethod;
|
||||
import org.keycloak.theme.beans.MessagesPerFieldBean;
|
||||
import org.keycloak.theme.freemarker.FreeMarkerProvider;
|
||||
import org.keycloak.tracing.TracingProvider;
|
||||
import org.keycloak.userprofile.UserProfileContext;
|
||||
import org.keycloak.utils.MediaType;
|
||||
import org.keycloak.utils.MediaTypeMatcher;
|
||||
|
||||
import io.opentelemetry.api.trace.SpanContext;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PASSWORD;
|
||||
|
|
@ -141,6 +143,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
protected UriInfo uriInfo;
|
||||
|
||||
protected FreeMarkerProvider freeMarker;
|
||||
protected TracingProvider tracing;
|
||||
|
||||
protected UserModel user;
|
||||
|
||||
|
|
@ -152,6 +155,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
public FreeMarkerLoginFormsProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
this.freeMarker = session.getProvider(FreeMarkerProvider.class);
|
||||
this.tracing = session.getProvider(TracingProvider.class);
|
||||
this.attributes.put("scripts", new LinkedList<>());
|
||||
this.realm = session.getContext().getRealm();
|
||||
this.client = session.getContext().getClient();
|
||||
|
|
@ -596,6 +600,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
}
|
||||
|
||||
attributes.put("lang", lang);
|
||||
|
||||
SpanContext spanContext = tracing.getCurrentSpan().getSpanContext();
|
||||
if (spanContext.isValid()) {
|
||||
attributes.put("traceId", spanContext.getTraceId());
|
||||
}
|
||||
}
|
||||
|
||||
private UriBuilder getDefaultPageUriForLocale(URI baseUri) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.keycloak.testframework.ui.page;
|
|||
|
||||
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||
|
||||
import org.openqa.selenium.NoSuchElementException;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
|
|
@ -29,6 +30,9 @@ public class ErrorPage extends AbstractLoginPage {
|
|||
@FindBy(className = "instruction")
|
||||
private WebElement errorMessage;
|
||||
|
||||
@FindBy(id = "traceId")
|
||||
private WebElement traceIdMessage;
|
||||
|
||||
@FindBy(id = "backToApplication")
|
||||
private WebElement backToApplicationLink;
|
||||
|
||||
|
|
@ -40,6 +44,18 @@ public class ErrorPage extends AbstractLoginPage {
|
|||
return errorMessage.getText();
|
||||
}
|
||||
|
||||
public String getTraceId() {
|
||||
return traceIdMessage.getText();
|
||||
}
|
||||
|
||||
public boolean isTraceIdPresent() {
|
||||
try {
|
||||
return traceIdMessage.isDisplayed();
|
||||
} catch (NoSuchElementException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void clickBackToApplication() {
|
||||
backToApplicationLink.click();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package org.keycloak.tests.tracing;
|
||||
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.oauth.OAuthClient;
|
||||
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfig;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
|
||||
import org.keycloak.testframework.ui.annotations.InjectPage;
|
||||
import org.keycloak.testframework.ui.page.ErrorPage;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
@KeycloakIntegrationTest(config = TracingDisabledErrorMessageTest.ServerConfigWithoutTracing.class)
|
||||
public class TracingDisabledErrorMessageTest {
|
||||
|
||||
@InjectOAuthClient
|
||||
OAuthClient oauth;
|
||||
|
||||
@InjectPage
|
||||
ErrorPage errorPage;
|
||||
|
||||
@Test
|
||||
public void traceIdAbsentInErrorPage() {
|
||||
oauth.redirectUri("http://invalid");
|
||||
oauth.openLoginForm();
|
||||
errorPage.assertCurrent();
|
||||
|
||||
assertFalse(errorPage.isTraceIdPresent());
|
||||
}
|
||||
|
||||
public static class ServerConfigWithoutTracing implements KeycloakServerConfig {
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) {
|
||||
return config
|
||||
.option("tracing-enabled", "false");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.keycloak.tests.tracing;
|
||||
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.oauth.OAuthClient;
|
||||
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||
import org.keycloak.testframework.ui.annotations.InjectPage;
|
||||
import org.keycloak.testframework.ui.page.ErrorPage;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
|
||||
@KeycloakIntegrationTest(config = TracingProviderTest.ServerConfigWithTracing.class)
|
||||
public class TracingEnabledErrorMessageTest {
|
||||
|
||||
@InjectOAuthClient
|
||||
OAuthClient oauth;
|
||||
|
||||
@InjectPage
|
||||
ErrorPage errorPage;
|
||||
|
||||
@Test
|
||||
public void traceIdPresentInErrorPage() {
|
||||
oauth.redirectUri("http://invalid");
|
||||
oauth.openLoginForm();
|
||||
errorPage.assertCurrent();
|
||||
|
||||
assertThat(errorPage.getTraceId(), matchesPattern(".*: [0-9a-f]{32}"));
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,9 @@
|
|||
<#elseif section = "form">
|
||||
<div id="kc-error-message">
|
||||
<p class="instruction">${kcSanitize(message.summary)?no_esc}</p>
|
||||
<#if traceId??>
|
||||
<p class="instruction" id="traceId">${msg("traceIdSupportMessage", traceId)}</p>
|
||||
</#if>
|
||||
<#if skipLink??>
|
||||
<#else>
|
||||
<#if client?? && client.baseUrl?has_content>
|
||||
|
|
|
|||
|
|
@ -568,3 +568,5 @@ emailVerificationPending=A verification email was sent to {0}. You can submit wi
|
|||
orgMemberAlready=You are already a member of the {1} organization.
|
||||
orgDisabledMessage=The organization is not available at this time and cannot accept new members.
|
||||
staleInviteOrgLink=The link you clicked is no longer valid. It may have expired or already been used.
|
||||
|
||||
traceIdSupportMessage=If you contact support, please provide the following trace identifier: {0}
|
||||
|
|
|
|||
Loading…
Reference in a new issue