Repository: tapestry-5 Updated Branches: refs/heads/master 265d23fcb -> 1cb63a12b
TAP5-2333: Optimize OperationTracker logging for normal (success, no logging) case This avoids the use of a per-thread variable. Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/1cb63a12 Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/1cb63a12 Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/1cb63a12 Branch: refs/heads/master Commit: 1cb63a12b4887b54210d91fe4a9863cf3dad93b5 Parents: 265d23f Author: Howard M. Lewis Ship <hls...@apache.org> Authored: Fri Oct 3 17:19:25 2014 -0700 Committer: Howard M. Lewis Ship <hls...@apache.org> Committed: Fri Oct 3 17:19:25 2014 -0700 ---------------------------------------------------------------------- .../apache/tapestry5/ioc/RegistryBuilder.java | 2 +- .../ioc/internal/OperationException.java | 29 +++- .../ioc/internal/OperationTrackerImpl.java | 144 +++++++++---------- .../ioc/internal/PerThreadOperationTracker.java | 90 ------------ 4 files changed, 93 insertions(+), 172 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1cb63a12/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/RegistryBuilder.java ---------------------------------------------------------------------- diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/RegistryBuilder.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/RegistryBuilder.java index d68b2da..493ea3b 100644 --- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/RegistryBuilder.java +++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/RegistryBuilder.java @@ -176,7 +176,7 @@ public final class RegistryBuilder { lock.lock(); - PerThreadOperationTracker tracker = new PerThreadOperationTracker(loggerSource.getLogger(Registry.class)); + OperationTracker tracker = new OperationTrackerImpl(loggerSource.getLogger(Registry.class)); RegistryImpl registry = new RegistryImpl(modules, proxyFactory, loggerSource, tracker); http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1cb63a12/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationException.java ---------------------------------------------------------------------- diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationException.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationException.java index d8b97e0..695fc91 100644 --- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationException.java +++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationException.java @@ -1,5 +1,3 @@ -// Copyright 2008, 2010 The Apache Software Foundation -// // Licensed 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 @@ -14,7 +12,9 @@ package org.apache.tapestry5.ioc.internal; +import org.apache.tapestry5.ioc.internal.util.CollectionFactory; import org.apache.tapestry5.ioc.internal.util.TapestryException; +import org.apache.tapestry5.ioc.util.Stack; /** * An exception caught and reported by an {@link org.apache.tapestry5.ioc.OperationTracker}; the trace property @@ -24,17 +24,36 @@ public class OperationException extends TapestryException { private static final long serialVersionUID = -7555673473832355909L; - private final String[] trace; + private final Stack<String> operations = CollectionFactory.newStack(); - public OperationException(Throwable cause, String[] trace) + public OperationException(Throwable cause, String description) { super(cause.getMessage(), cause); - this.trace = trace; + operations.push(description); } public String[] getTrace() { + Object[] snapshot = operations.getSnapshot(); + + String[] trace = new String[snapshot.length]; + + for (int i = 0; i < snapshot.length; i++) + { + trace[i] = snapshot[i].toString(); + } + return trace; } + + /** + * Invoked while unwinding the stack to add descriptions for each OperationTracker run/invoke/perform + * operation. + * + */ + public void push(String description) + { + operations.push(description); + } } http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1cb63a12/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationTrackerImpl.java ---------------------------------------------------------------------- diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationTrackerImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationTrackerImpl.java index 6bc462b..ca6c8a1 100644 --- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationTrackerImpl.java +++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/OperationTrackerImpl.java @@ -1,5 +1,3 @@ -// Copyright 2008-2013 The Apache Software Foundation -// // Licensed 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 @@ -17,26 +15,24 @@ package org.apache.tapestry5.ioc.internal; import org.apache.tapestry5.ioc.IOOperation; import org.apache.tapestry5.ioc.Invokable; import org.apache.tapestry5.ioc.OperationTracker; -import org.apache.tapestry5.ioc.internal.util.CollectionFactory; import org.apache.tapestry5.ioc.internal.util.InternalUtils; import org.apache.tapestry5.ioc.util.ExceptionUtils; -import org.apache.tapestry5.ioc.util.Stack; import org.slf4j.Logger; import java.io.IOException; +import java.util.Arrays; +import java.util.List; /** * Core implementation that manages a logger and catches and reports exception. - * - * @see org.apache.tapestry5.ioc.internal.PerThreadOperationTracker */ public class OperationTrackerImpl implements OperationTracker { - private final Logger logger; + private static final String CLASS_NAME = OperationTrackerImpl.class.getName(); - private final Stack<String> operations = CollectionFactory.newStack(); + private static final List<String> OPERATIONS = Arrays.asList("run", "invoke", "perform"); - private boolean logged; + private final Logger logger; public OperationTrackerImpl(Logger logger) { @@ -57,15 +53,12 @@ public class OperationTrackerImpl implements OperationTracker finish(description, startNanos); - } catch (RuntimeException ex) - { - logAndRethrow(ex); - } catch (Error ex) + } catch (OperationException oe) { - handleError(ex); - } finally + captureDescription(oe, description); + } catch (Throwable ex) { - handleFinally(); + throwNewOperationException(ex, description); } } @@ -85,15 +78,12 @@ public class OperationTrackerImpl implements OperationTracker return result; - } catch (RuntimeException ex) + } catch (OperationException oe) { - return logAndRethrow(ex); - } catch (Error ex) + return captureDescription(oe, description); + } catch (Throwable ex) { - return handleError(ex); - } finally - { - handleFinally(); + return throwNewOperationException(ex, description); } } @@ -113,39 +103,13 @@ public class OperationTrackerImpl implements OperationTracker return result; - } catch (RuntimeException ex) - { - return logAndRethrow(ex); - } catch (Error ex) + } catch (OperationException oe) { - return handleError(ex); - } finally - { - handleFinally(); - } - } - - private void handleFinally() - { - operations.pop(); - - // We've finally backed out of the operation stack ... but there may be more to come! - - if (operations.isEmpty()) - { - logged = false; - } - } - - private <T> T handleError(Error error) - { - if (!logged) + return captureDescription(oe, description); + } catch (Throwable ex) { - log(error); - logged = true; + return throwNewOperationException(ex, description); } - - throw error; } private void finish(String description, long startNanos) @@ -155,7 +119,7 @@ public class OperationTrackerImpl implements OperationTracker long elapsedNanos = System.nanoTime() - startNanos; double elapsedMillis = ((double) elapsedNanos) / 1000000.d; - logger.debug(String.format("[%3d] <-- %s [%,.2f ms]", operations.getDepth(), description, elapsedMillis)); + logger.debug(String.format("[%3d] <-- %s [%,.2f ms]", getDepth(), description, elapsedMillis)); } } @@ -165,48 +129,76 @@ public class OperationTrackerImpl implements OperationTracker if (logger.isDebugEnabled()) { + logger.debug(String.format("[%3d] --> %s", getDepth(), description)); startNanos = System.nanoTime(); - logger.debug(String.format("[%3d] --> %s", operations.getDepth() + 1, description)); } - operations.push(description); return startNanos; } - private <T> T logAndRethrow(RuntimeException ex) + private <T> T captureDescription(OperationException oe, String description) { - if (!logged) - { - String[] trace = log(ex); + oe.push(description); - logged = true; + return logOrRethrow(oe); + } - throw new OperationException(ex, trace); - } + private <T> T throwNewOperationException(Throwable ex, String description) + { + OperationException oe = new OperationException(ex, description); - throw ex; + return logOrRethrow(oe); } - private String[] log(Throwable ex) + /** + * So, when an exception occurs, at the deepest level, an OperationException is thrown via + * {@link #throwNewOperationException(Throwable, String)}. Each perform/run/invoke call + * catches the OperationException, invokes {@link #captureDescription(OperationException, String)} to + * add a message to it, and rethrows it. This method checks to see if it is the first invocation + * of perform/run/invoke on the stack and, if so, it logs the operation trace (this is a difference + * from 5.3, which logged the trace much earlier). After logging, the OperationException, or + * the cause of the OE, is rethrown. + * + * @param oe + * @param <T> + * @return + */ + private <T> T logOrRethrow(OperationException oe) { - logger.error(ExceptionUtils.toMessage(ex)); - logger.error("Operations trace:"); + if (getDepth() == 1) + { + logger.error(ExceptionUtils.toMessage(oe.getCause())); + logger.error("Operations trace:"); - Object[] snapshot = operations.getSnapshot(); - String[] trace = new String[snapshot.length]; + String[] trace = oe.getTrace(); - for (int i = 0; i < snapshot.length; i++) - { - trace[i] = snapshot[i].toString(); + for (int i = 0; i < trace.length; i++) + { + logger.error(String.format("[%2d] %s", i + 1, trace[i])); + } - logger.error(String.format("[%2d] %s", i + 1, trace[i])); + if (oe.getCause() instanceof Error) + { + throw (Error) oe.getCause(); + } } - return trace; + throw oe; } - boolean isEmpty() + private int getDepth() { - return operations.isEmpty(); + int result = 0; + + for (StackTraceElement ste : new Throwable().getStackTrace()) + { + if (ste.getClassName().equals(CLASS_NAME) && OPERATIONS.contains(ste.getMethodName())) + { + ++result; + } + } + + return result; } + } http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1cb63a12/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/PerThreadOperationTracker.java ---------------------------------------------------------------------- diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/PerThreadOperationTracker.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/PerThreadOperationTracker.java deleted file mode 100644 index db2d310..0000000 --- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/PerThreadOperationTracker.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2008-2013 The Apache Software Foundation -// -// Licensed 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 org.apache.tapestry5.ioc.internal; - -import org.apache.tapestry5.ioc.IOOperation; -import org.apache.tapestry5.ioc.Invokable; -import org.apache.tapestry5.ioc.OperationTracker; -import org.slf4j.Logger; - -import java.io.IOException; - -/** - * Manages a per-thread OperationTracker using a ThreadLocal. - */ -public class PerThreadOperationTracker implements OperationTracker -{ - private final Logger logger; - - private final ThreadLocal<OperationTrackerImpl> perThread = new ThreadLocal<OperationTrackerImpl>() - { - @Override - protected OperationTrackerImpl initialValue() - { - return new OperationTrackerImpl(logger); - } - }; - - public PerThreadOperationTracker(Logger logger) - { - this.logger = logger; - } - - OperationTracker get() - { - return perThread.get(); - } - - void cleanup() - { - if (perThread.get().isEmpty()) perThread.remove(); - } - - @Override - public void run(String description, Runnable operation) - { - try - { - get().run(description, operation); - } finally - { - cleanup(); - } - } - - @Override - public <T> T invoke(String description, Invokable<T> operation) - { - try - { - return get().invoke(description, operation); - } finally - { - cleanup(); - } - } - - @Override - public <T> T perform(String description, IOOperation<T> operation) throws IOException - { - try - { - return get().perform(description, operation); - } finally - { - cleanup(); - } - } -}