springspring-bootthymeleafsplunkcustom-error-handling

Spring + Thymeleaf Engine Error Handling and Logging


Our company is in the process of switching our template engine from Velocity to Thymeleaf. We use Splunk for our logging, and with Velocity we were able to implement org.apache.velocity.runtime.log.LogChute to handle custom logging (format for and log to our splunk log), but I have been unable to find any similar class for Thymeleaf.

I've tried a couple of approaches so far. First, I tried to extend the actual Thymeleaf engine and add a try/catch wrapper around the process method, but that method is final, unfortunately. I saw a suggestion to add a filter to catch the thymeleaf errors, but something must be swallowing the error, because it never reaches that catch block.

The only option I can think of at this point is to simply pull org.thymeleaf.TemplateEngine logs into our splunk log, but then it won't be properly formatted for ingestion and I can't add any custom fields.

Does anybody have any ideas?

EDIT:

Wow, so I just retried the Filter approach and it worked, but the caught exception was org.springframework.web.util.NestedServletException, so JRebel must not have reloaded my changes to catch Exception instead of TemplateEngineException when I tried it last.

That said, if anybody has a better approach, I'd love to hear it. I'm new to the whole question posting thing; should I post an answer?


Solution

  • The filter approach did end up working, but the TemplateEngineException is wrapped around by a NestedServletException.

    import java.io.IOException;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import org.springframework.web.util.NestedServletException;
    import org.thymeleaf.exceptions.TemplateEngineException;
    
    /**
     * Filter to catch Thymeleaf template errors
     */
    public class ThymeleafErrorFilter implements Filter {
    
        @Override
        public void init(final FilterConfig filterConfig) {
    
        }
    
        @Override
        public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
            try {
                filterChain.doFilter(servletRequest, servletResponse);
            } catch (final NestedServletException nse) {
                if(nse.getCause() instanceof TemplateEngineException) {
                    //Do stuff here
                    ...
                }
    
                throw nse;
            }
        }
    
        @Override
        public void destroy() {
        }
    }
    

    And then registering the filter

        /**
         * @return thymeleaf error filter
         */
        @Bean
        public FilterRegistrationBean thymeleafErrorFilter() {
            FilterRegistrationBean thymeleafErrorFilter = new FilterRegistrationBean();
            thymeleafErrorFilter.setName("thymeleafErrorFilter");
            thymeleafErrorFilter.setFilter(new ThymeleafErrorFilter());
            thymeleafErrorFilter.addUrlPatterns("/*");
            return thymeleafErrorFilter;
        }