View Javadoc

1   package org.e2etrace.trace;
2   
3   /*
4    * Copyright 2006 Gunther Popp
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.util.Iterator;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.e2etrace.timer.ITimer;
24  import org.e2etrace.timer.ITimerFactory;
25  
26  /**
27   * Default <code>ITraceStep</code> implementation.
28   * <p>
29   * 
30   * Trace steps may be serialized to remote clients. Please note that after
31   * serializiation <code>enter()</code> and <code>leave()</code> should be called
32   * anymore (the methods will handle this case gracefully and print a warning
33   * message).
34   * <p>
35   * 
36   * To enable logging of the trace-calls just set the log level for this class to
37   * <code>DEBUG</code>. For example, in a log4j configuration file the following
38   * entry activates log output for every call to <code>enter()</code> and
39   * <code>leave()</code>:
40   * <p>
41   * 
42   * <pre>
43   * log4j.category.e2etrace.trace.DefaultTraceStep = DEBUG
44   * </pre>
45   * 
46   * @author Gunther Popp
47   * 
48   */
49  public class DefaultTraceStep extends AbstractTraceStep {
50  
51    private static final long serialVersionUID = 1L;
52    private static final Log log = LogFactory.getLog(DefaultTraceStep.class);
53  
54    private long duration = 0L;
55    private boolean active = false;
56    private transient ITimer timer;
57    private transient ITimerFactory timerFactory;
58  
59    /**
60     * Constructor. New instances can only be created by factories
61     * {@link ITraceStepFactory}.
62     * 
63     * @param id Id of the new trace step
64     * @param timerFactory factory for the timer instance that is used to
65     *          calculate the duration between <code>enter</code> and
66     *          <code>leave</code>
67     */
68    DefaultTraceStep(ITraceStepId id, ITimerFactory timerFactory) {
69      super(id);
70  
71      if (timerFactory == null) {
72        throw new IllegalArgumentException("timerFactory must not be null");
73      }
74  
75      this.timerFactory = timerFactory;
76    }
77  
78    /** {@inheritDoc} */
79    public void enter() {
80      if (isActive()) {
81        // enter must only be called once for a trace step
82        log.warn("enter() has been called before for trace step '"
83            + this.getId().asString() + "'");
84  
85        return;
86      }
87  
88      if (timerFactory == null) {
89        // enter cannot be called after Serialization
90        log.warn("enter() cannot be invoked after Serialization of the trace step");
91  
92        return;
93      }
94  
95      if (log.isDebugEnabled()) {
96        log.debug(">>> Entering " + this.getId().asString());
97      }
98  
99      this.timer = this.timerFactory.newInstance();
100     this.timer.start();
101     this.active = true;
102 
103   }
104 
105   /** {@inheritDoc} */
106   public void leave() {
107     if (timerFactory == null) {
108       // leave cannot be called after Serialization
109       log.warn("leave() cannot be invoked after Serialization of the trace step");
110 
111       return;
112     }
113 
114     // Only leave a trace step once
115     if (!isActive()) {
116       // leave must only be called once for a trace step and only after a call
117       // to enter()
118       log
119           .warn("leave() has been called before OR leave() has been called without prior enter() for trace step '"
120               + this.getId().asString() + "'");
121 
122       return;
123     }
124 
125     // Forward the call to all children
126     leaveAllChildren();
127 
128     // Measure the duration of this trace step
129     this.duration = this.timer.measure();
130     this.active = false;
131 
132     if (log.isDebugEnabled()) {
133       log.debug("<<< Leaving " + this.getId().asString() + " (" + this.duration + "ms)");
134     }
135 
136   }
137 
138   /** {@inheritDoc} */
139   public boolean isActive() {
140     return this.active;
141   }
142 
143   /** {@inheritDoc} */
144   public long getDuration() {
145     return this.duration;
146   }
147 
148   /**
149    * Returns the isolated duration of this TraceStep.
150    * <p>
151    * 
152    * The duration is the time elapsed between the calls to <code>enter</code>
153    * and <code>leave</code> minus the durations of all children.
154    * <p>
155    * 
156    * Please note that the returned value only reflects the real execution time,
157    * if all children of this trace step really have been executed. That is, if
158    * trace steps are added manually, <code>getIsolatedDuration()</code> will
159    * return meaningless values. An example for this scenario are traces that are
160    * collected from parallel execution threads and manually added to the trace
161    * step of the method that spawned the threads.
162    * <p>
163    * 
164    * @return duration of this TraceStep in ms (-1: no valid duration exists for
165    *         this trace step)
166    */
167   public long getIsolatedDuration() {
168     long duration;
169 
170     duration = this.getDuration();
171 
172     // Subtract durations of all direct children
173     if (this.children != null) {
174       for (Iterator iter = this.children.iterator(); iter.hasNext();) {
175         ITraceStep element = (ITraceStep) iter.next();
176 
177         duration -= element.getDuration();
178       }
179     }
180 
181     // In certain situation duration may be invalid (e.g. if additional trace
182     // steps have been added manually. In this case the method returns a defined
183     // error value.
184     if (duration < 0) {
185       duration = -1;
186     }
187     return duration;
188   }
189 
190   /**
191    * Override toString.
192    * 
193    * @return the Objects String representation.
194    */
195   public String toString() {
196     StringBuffer buffer = new StringBuffer();
197 
198     buffer.append("[DefaultTraceStep:");
199     buffer.append(" id: ");
200     buffer.append(this.getId());
201     buffer.append(" duration: ");
202     buffer.append(duration);
203     buffer.append(" parent: ");
204     buffer.append(this.getParent());
205     buffer.append("]");
206 
207     return buffer.toString();
208   }
209 
210 }