diff --git a/utils/src/main/java/com/cloud/utils/log/CglibThrowableRenderer.java b/utils/src/main/java/com/cloud/utils/log/CglibThrowableRenderer.java index b102dc44a2c..9178c805536 100644 --- a/utils/src/main/java/com/cloud/utils/log/CglibThrowableRenderer.java +++ b/utils/src/main/java/com/cloud/utils/log/CglibThrowableRenderer.java @@ -19,16 +19,17 @@ package com.cloud.utils.log; -import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.spi.ThrowableRenderer; /** - * This renderer removes all the Cglib generated methods from the call + * This renderer removes all the CGLib generated methods from the call * - * Unfortunately, I had to copy out the EnhancedThrowableRenderer from - * the apach libraries because EnhancedThrowableRenderer is a final class. + * Unfortunately, I had to copy out there-write the EnhancedThrowableRenderer from + * the Apache libraries because EnhancedThrowableRenderer is a final class. * simply override doRender. Not sure what the developers are thinking there * making it final. * @@ -37,48 +38,45 @@ import org.apache.log4j.spi.ThrowableRenderer; * */ public class CglibThrowableRenderer implements ThrowableRenderer { - /** - * Construct new instance. - */ - public CglibThrowableRenderer() { - super(); + + private final static int MAX_NUMBER_OF_STACK_TRACES_ON_LOG_FOR_CAUSE = 3; + @Override + public String[] doRender(Throwable th) { + List lines = new ArrayList(); + lines.add(th.toString()); + addStackTraceToList(th, lines, 0); + do { + th = th.getCause(); + if (th != null) { + lines.add("Caused by: " + th.toString()); + addStackTraceToList(th, lines, MAX_NUMBER_OF_STACK_TRACES_ON_LOG_FOR_CAUSE); + } + } while (th != null); + return lines.toArray(new String[lines.size()]); } - @Override - public String[] doRender(final Throwable th) { - try { - ArrayList lines = new ArrayList(); - Throwable throwable = th; - lines.add(throwable.toString()); - int start = 0; - do { - StackTraceElement[] elements = throwable.getStackTrace(); - for (int i = 0; i < elements.length - start; i++) { - StackTraceElement element = elements[i]; - String filename = element.getFileName(); - String method = element.getMethodName(); - if ((filename != null && filename.equals("")) || (method != null && method.equals("invokeSuper"))) { - continue; - } - lines.add("\tat " + element.toString()); - } - if (start != 0) { - lines.add("\t... " + start + " more"); - } - throwable = throwable.getCause(); - if (throwable != null) { - lines.add("Caused by: " + throwable.toString()); - start = elements.length - 1; - } - } while (throwable != null); - return lines.toArray(new String[lines.size()]); - } catch (Exception ex) { - PrintWriter pw = new PrintWriter(System.err); - ex.printStackTrace(pw); - pw = new PrintWriter(System.out); - ex.printStackTrace(pw); - ex.printStackTrace(); - return null; + /** + * This method adds the stack traces retrieved from {@link Throwable#getStackTrace()} + * The maxNumberOfStack attribute indicates the number of stacks that will be added, + * if that value is 0, then all of the stack traces will be added, otherwise the stack traces will be limited to that number + * @param th + * @param lines + * @param maxNumberOfStack + */ + private void addStackTraceToList(Throwable th, List lines, int maxNumberOfStack) { + StackTraceElement[] elements = th.getStackTrace(); + if (maxNumberOfStack == 0 || maxNumberOfStack > elements.length) { + maxNumberOfStack = elements.length; + } + for (int i = 0; i < maxNumberOfStack; i++) { + StackTraceElement element = elements[i]; + if (StringUtils.contains(element.getClassName(), "net.sf.cglib.proxy")) { + continue; + } + lines.add("\tat " + element.toString()); + } + if (maxNumberOfStack < elements.length) { + lines.add("\t... " + (elements.length - maxNumberOfStack) + " more"); } } } diff --git a/utils/src/test/java/com/cloud/utils/log/CglibThrowableRendererTest.java b/utils/src/test/java/com/cloud/utils/log/CglibThrowableRendererTest.java new file mode 100644 index 00000000000..ae5d4870432 --- /dev/null +++ b/utils/src/test/java/com/cloud/utils/log/CglibThrowableRendererTest.java @@ -0,0 +1,85 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.utils.log; + +import java.lang.reflect.Method; + +import org.apache.commons.lang.StringUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +public class CglibThrowableRendererTest { + + CglibThrowableRenderer cglibThrowableRenderer = new CglibThrowableRenderer(); + + @Test + public void testDoRendere() { + SampleClass sampleClass = (SampleClass)Enhancer.create(SampleClass.class, new MyInvocationHandler()); + try { + sampleClass.theFirstMethodThatCapturesAnException(); + } catch (Exception e) { + String[] exceptions = cglibThrowableRenderer.doRender(e); + assertThatTheTraceListDoesNotContainsCgLibLogs(exceptions); + } + } + + private void assertThatTheTraceListDoesNotContainsCgLibLogs(String[] exceptions) { + for (String s : exceptions) { + Assert.assertEquals(false, isCgLibLogTrace(s)); + } + } + + private boolean isCgLibLogTrace(String s) { + return StringUtils.contains(s, "net.sf.cglib.proxy"); + } + + static class SampleClass { + public void theFirstMethodThatCapturesAnException() { + try { + methodThatCapturesAndThrowsException(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void methodThatCapturesAndThrowsException() throws Exception { + try { + methodThatThrowsAnError(); + } catch (Error e) { + throw new Exception("Throws an exception", e); + } + } + + private void methodThatThrowsAnError() { + throw new Error("Exception to test the CglibThrowableRenderer."); + } + } + + static class MyInvocationHandler implements MethodInterceptor { + @Override + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + return proxy.invoke(new SampleClass(), args); + } + } +}