Merge pull request #762 from rafaelweingartner/cglibThrowableRenderer

Proposal for an improved CglibThrowableRendererFollowing our discussions on mailing list, here is a PR with a proposal for an improvement on CglibThrowableRenderer. We would still have to define a number to limit the stack traces of causes (I randomly chose 3, just to get the code running). The test case is also not good in my opinion, but I ran out of ideas (waiting for suggestions on that).

* pr/762:
  Changed the test case to test each of the log traces in the array list.
  Proposal for an improved CglibThrowableRenderer

Signed-off-by: Rajani Karuturi <rajani.karuturi@citrix.com>
This commit is contained in:
Rajani Karuturi 2015-09-01 14:31:32 +05:30
commit e8979c0e65
2 changed files with 127 additions and 44 deletions

View File

@ -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<String> lines = new ArrayList<String>();
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<String> lines = new ArrayList<String>();
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("<generated>")) || (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<String> 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");
}
}
}

View File

@ -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);
}
}
}