Skip to content

Commit

Permalink
MYFACES-4701 - Add ability to skip logging of specific exceptions in …
Browse files Browse the repository at this point in the history
…the ExceptionHandler impl
  • Loading branch information
tandraschko committed Dec 18, 2024
1 parent baa9df7 commit 3251378
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.apache.myfaces.config.webparameters;

import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -784,6 +786,10 @@ public class MyfacesConfig
public static final String EL_RESOLVER_TRACING = "org.apache.myfaces.EL_RESOLVER_TRACING";
public static final boolean EL_RESOLVER_TRACING_DEFAULT = false;

@JSFWebConfigParam(name="org.apache.myfaces.EXCEPTION_TYPES_TO_IGNORE_IN_LOGGING", since="4.0.3, 4.1.1, 5.0", defaultValue = "")
public static final String EXCEPTION_TYPES_TO_IGNORE_IN_LOGGING =
"org.apache.myfaces.EXCEPTION_TYPES_TO_IGNORE_IN_LOGGING";

// we need it, applicationImpl not ready probably
private ProjectStage projectStage = ProjectStage.Production;
private boolean strictJsf2AllowSlashLibraryName;
Expand Down Expand Up @@ -862,7 +868,8 @@ public class MyfacesConfig
private ResourceBundle.Control resourceBundleControl;
private boolean automaticExtensionlessMapping = AUTOMATIC_EXTENSIONLESS_MAPPING_DEFAULT;
private boolean elResolverTracing = EL_RESOLVER_TRACING_DEFAULT;
private long faceletsRefreshPeriod = -1;
private long faceletsRefreshPeriod = -1;
private List<String> exceptionTypesToIgnoreInLogging = new ArrayList<>();

private static final boolean MYFACES_IMPL_AVAILABLE;
private static final boolean RI_IMPL_AVAILABLE;
Expand Down Expand Up @@ -1327,6 +1334,17 @@ else if (refreshTransientBuildOnPSS.equalsIgnoreCase("true") ||
ViewHandler.FACELETS_REFRESH_PERIOD_PARAM_NAME,
0);
}

String[] exceptionTypesToIgnoreInLogging = StringUtils.splitShortString(
getString(extCtx, EXCEPTION_TYPES_TO_IGNORE_IN_LOGGING, ""),
',');
for (String exceptionTypeToIgnoreInLogging : exceptionTypesToIgnoreInLogging)
{
if (StringUtils.isNotBlank(exceptionTypeToIgnoreInLogging))
{
cfg.exceptionTypesToIgnoreInLogging.add(exceptionTypeToIgnoreInLogging);
}
}

return cfg;
}
Expand Down Expand Up @@ -1793,5 +1811,9 @@ public long getFaceletsRefreshPeriod()
return faceletsRefreshPeriod;
}

public List<String> getExceptionTypesToIgnoreInLogging()
{
return exceptionTypesToIgnoreInLogging;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,16 @@ public void handle() throws FacesException
// jakarta.faces.event.AbortProcessingException
if (!shouldSkip(exception))
{
Throwable rootCause = getRootCause(exception);

// set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
handledAndThrown = event;

Throwable rootCause = getRootCause(exception);


throwableList.add(rootCause == null ? exception : rootCause);
}

ExceptionHandlerUtils.logException(context, log);
ExceptionHandlerUtils.logException(rootCause == null ? exception : rootCause,
context.getComponent(), context.getContext(), log);
}
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,21 +125,25 @@ public void handle() throws FacesException

// and call getException() on the returned result
Throwable exception = context.getException();

ExceptionHandlerUtils.logException(context, log);

// Upon encountering the first such Exception that is not an instance of
// jakarta.faces.event.AbortProcessingException
if (!shouldSkip(exception))
{
Throwable rootCause = getRootCause(exception);

// set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
handledAndThrown = event;

// Re-wrap toThrow in a ServletException or (PortletException, if in a portlet environment)
// and throw it
// FIXME: The spec says to NOT use a FacesException to propagate the exception, but I see
// no other way as ServletException is not a RuntimeException
toThrow = wrap(getRethrownException(exception));
toThrow = wrap(rootCause == null ? exception : rootCause);

ExceptionHandlerUtils.logException(rootCause == null ? exception : rootCause,
context.getComponent(), context.getContext(), log);

break;
}
}
Expand Down Expand Up @@ -188,20 +192,7 @@ public void processEvent(SystemEvent exceptionQueuedEvent) throws AbortProcessin

unhandled.add((ExceptionQueuedEvent)exceptionQueuedEvent);
}

protected Throwable getRethrownException(Throwable exception)
{
// Let toRethrow be either the result of calling getRootCause() on the Exception,
// or the Exception itself, whichever is non-null
Throwable toRethrow = getRootCause(exception);
if (toRethrow == null)
{
toRethrow = exception;
}

return toRethrow;
}


protected FacesException wrap(Throwable exception)
{
if (exception instanceof FacesException facesException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,93 @@
*/
package org.apache.myfaces.context;

import jakarta.el.ELException;
import jakarta.faces.FacesException;
import jakarta.faces.FacesWrapper;
import jakarta.faces.application.ProjectStage;
import jakarta.faces.application.ViewExpiredException;
import jakarta.faces.component.UIComponent;
import jakarta.faces.event.ExceptionQueuedEventContext;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.faces.context.FacesContext;
import org.apache.myfaces.config.webparameters.MyfacesConfig;
import org.apache.myfaces.view.facelets.LocationAware;
import org.apache.myfaces.view.facelets.el.ContextAware;

import java.util.logging.Level;
import java.util.logging.Logger;

public class ExceptionHandlerUtils
{
protected static boolean isLogStacktrace(FacesContext context, Throwable exception)
{
if (context.isProjectStage(ProjectStage.Production))
{
if (exception instanceof ViewExpiredException)
{
return false;
}
}

return true;
}

public static void logException(ExceptionQueuedEventContext event, Logger logger)
protected static boolean isLogException(FacesContext context, Throwable exception)
{
// unwrap to strip location aware stacktraces
Throwable exception = event.getException();
while (exception instanceof FacesWrapper)
if (context.isProjectStage(ProjectStage.Production))
{
if (exception != null)
{
MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(context);
for (String ignore : myfacesConfig.getExceptionTypesToIgnoreInLogging())
{
if (ignore.trim().equals(exception.getClass().getName()))
{
return false;
}
}
}
}

return true;
}

public static void logException(Throwable exception, UIComponent component, FacesContext context, Logger logger)
{
while (exception instanceof FacesWrapper ||
(exception.getClass().equals(FacesException.class) || exception.getClass().equals(ELException.class)))
{
if (exception instanceof FacesWrapper)
{
exception = (Throwable) ((FacesWrapper) exception).getWrapped();
}
else if (exception.getClass().equals(FacesException.class)
|| exception.getClass().equals(ELException.class))
{
exception = exception.getCause();
}
}

if (!isLogException(context, exception))
{
exception = (Throwable) ((FacesWrapper) exception).getWrapped();
return;
}

String msg = exception.getClass().getName() + " occurred while processing " + event.getPhaseId().getName();
String location = buildLocation(event.getException(), event.getComponent());
String msg = exception.getClass().getName() + " occurred while processing "
+ context.getCurrentPhaseId().getName();

String location = buildLocation(exception, component);
if (location != null)
{
msg += " [Location=" + location + "]";
}

logger.log(Level.SEVERE, msg, exception);
if (isLogStacktrace(context, exception))
{
logger.log(Level.SEVERE, msg, exception);
}
else
{
logger.log(Level.SEVERE, msg);
}
}

public static String buildLocation(Throwable ex, UIComponent component)
Expand All @@ -65,6 +124,7 @@ else if (ex instanceof LocationAware laex)
}
}

// unwrap to strip location aware stacktraces
while (ex.getCause() != null)
{
ex = ex.getCause();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,6 @@ public void handle() throws FacesException

// and call getException() on the returned result
Throwable exception = context.getException();

ExceptionHandlerUtils.logException(context, log);

// Upon encountering the first such Exception that is not an instance of
// jakarta.faces.event.AbortProcessingException
Expand All @@ -256,6 +254,9 @@ public void handle() throws FacesException

throwableList.add(rootCause == null ? exception : rootCause);
components.add(event.getContext().getComponent());

ExceptionHandlerUtils.logException(rootCause == null ? exception : rootCause,
context.getComponent(), context.getContext(), log);
}
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;
import org.apache.myfaces.el.resolver.EmptyStringToNullELResolver;
import org.apache.myfaces.el.resolver.LambdaBeanELResolver;
import org.apache.myfaces.util.ExternalSpecifications;

/**
* Create the el resolver for faces. see 1.2 spec section 5.6.2
Expand Down Expand Up @@ -152,14 +153,17 @@ public void build(FacesContext facesContext, CompositeELResolver compositeElReso
list.add(new ListELResolver());
list.add(new ArrayELResolver());

try
{
list.add(new OptionalELResolver());
list.add(new RecordELResolver());
}
catch (Throwable ex)
if (ExternalSpecifications.isEL6Available())
{
LOG.log(Level.WARNING, "Could not add OptionalELResolver / RecordELResolver!", ex);
try
{
list.add(new OptionalELResolver());
list.add(new RecordELResolver());
}
catch (Throwable ex)
{
LOG.log(Level.WARNING, "Could not add OptionalELResolver / RecordELResolver!", ex);
}
}

if (PropertyDescriptorUtils.isUseLambdaMetafactory(facesContext.getExternalContext()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,23 @@ public final class ExternalSpecifications

return available;
});


private static Lazy<Boolean> el6Available = new Lazy<>(() ->
{
boolean available;
try
{
available = ClassUtils.classForName("jakarta.el.OptionalELResolver") != null;
}
catch (Throwable t)
{
available = false;
}
log.info("MyFaces Core EL 6.0 support " + (available ? "enabled" : "disabled"));

return available;
});

/**
* This method determines if Bean Validation is present.
*
Expand All @@ -141,6 +157,11 @@ public static boolean isServlet6Available()
return sevlet6Available.get();
}

public static boolean isEL6Available()
{
return el6Available.get();
}

/**
* this class should not be instantiated.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ public void initFaces(ServletContext servletContext)
initContainerIntegration(servletContext, externalContext);

// log environment integrations
ExternalSpecifications.isServlet6Available();
ExternalSpecifications.isEL6Available();
ExternalSpecifications.isCDIAvailable(externalContext);
ExternalSpecifications.isBeanValidationAvailable();

Expand Down
2 changes: 1 addition & 1 deletion parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
<plugin>
<groupId>org.apache.myfaces.buildtools</groupId>
<artifactId>myfaces-builder-plugin</artifactId>
<version>1.0.12</version>
<version>1.0.13-SNAPSHOT</version>
</plugin>

<plugin>
Expand Down

0 comments on commit 3251378

Please sign in to comment.