View Javadoc

1   /*
2      Copyright 2009 Ramon Servadei
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15   */
16  package fulmine.util.log;
17  
18  import static fulmine.util.Utils.COMMA_SPACE;
19  
20  import java.util.TimerTask;
21  
22  import org.apache.commons.logging.Log;
23  
24  import fulmine.Domain;
25  import fulmine.Type;
26  import fulmine.event.EventFrameExecution;
27  import fulmine.event.IEventManager;
28  import fulmine.model.IModelManager;
29  import fulmine.model.container.IContainer;
30  import fulmine.model.field.LongField;
31  
32  /**
33   * A standard implementation. This logs statistics at the INFO level and updates
34   * an information container. The information container (or 'info record') is
35   * created lazily to get around some startup lifecycle sequence clashes between
36   * the {@link IEventManager} and {@link IModelManager} that compose the context.
37   * <p>
38   * The statistics are grouped into 'interval' and 'total' groups. The interval
39   * group is for statistics in the last sampling interval whilst the total group
40   * maintains statistics for all sampling intervals since sampling began.
41   * 
42   * @author Ramon Servadei
43   */
44  public final class Statistics extends TimerTask implements IStatistics,
45      Runnable
46  {
47      private final static Log LOG = new AsyncLog(Statistics.class);
48  
49      private static final String TOTAL_POPPED = "totalPopped";
50  
51      private static final String POPPED = "popped";
52  
53      private static final String QUEUE_SIZE = "Q size";
54  
55      private static final String MAX = "max Q time (usec)";
56  
57      /** The maximum latency on the queue in an interval, measured in microseconds */
58      long max;
59  
60      /** The number of items pushed onto the queue in an interval */
61      long queueSize;
62  
63      /** The number of items popped off the queue in an interval */
64      long popped;
65  
66      /** The number of items popped off the queue across all intervals */
67      long totalPopped;
68  
69      /** The last time stats were logged */
70      long lastStatisticsLogTime = System.currentTimeMillis();
71  
72      /**
73       * System property to define the time period (in milliseconds) for logging.
74       * 
75       * @see #DEFAUL_TIME_PERIOD
76       */
77      public final static String TIME_PERIOD_PROPERTY =
78          Statistics.class.getSimpleName() + ".timePeriod";
79  
80      /** The default log time period */
81      public final static String DEFAUL_TIME_PERIOD = "10000";
82  
83      /** The time period in milliseconds for logging statistics */
84      public static final long TIME_PERIOD =
85          Integer.parseInt(System.getProperty(TIME_PERIOD_PROPERTY,
86              DEFAUL_TIME_PERIOD));
87  
88      /** The information container identity */
89      private final String infoRecordIdentity;
90  
91      /**
92       * Used to create the info record, this is done lazily because a container
93       * can't be created during the initialisation phase of a context
94       */
95      private final IModelManager context;
96  
97      /** The information container to update */
98      private IContainer infoRecord;
99  
100     /**
101      * Standard constructor
102      * 
103      * @param infoRecordIdentity
104      *            the identity for the information container record
105      * @param context
106      *            the context, used to lazily create the info record
107      */
108     public Statistics(String infoRecordIdentity, IModelManager context)
109     {
110         super();
111         this.infoRecordIdentity = infoRecordIdentity;
112         this.context = context;
113     }
114 
115     public void processEvent(long elapsedTimeMicroSecs)
116     {
117         this.max = Math.max(elapsedTimeMicroSecs, this.max);
118     }
119 
120     public void intervalFinished(final int queueSize, final int popped)
121     {
122         this.popped += popped;
123         this.queueSize = queueSize;
124     }
125 
126     /**
127      * Helper method that performs all the work to update the 'info record' with
128      * the current statistics.
129      */
130     private void updateInfoRecord()
131     {
132         if (this.context != null && this.context.isActive())
133         {
134             if (this.infoRecord == null)
135             {
136                 this.infoRecord =
137                     this.context.getLocalContainer(this.infoRecordIdentity,
138                         Type.SYSTEM, Domain.FRAMEWORK);
139                 this.infoRecord.add(new LongField(MAX));
140                 this.infoRecord.add(new LongField(QUEUE_SIZE));
141                 this.infoRecord.add(new LongField(POPPED));
142                 this.infoRecord.add(new LongField(TOTAL_POPPED));
143             }
144             this.infoRecord.beginFrame(new EventFrameExecution());
145             try
146             {
147                 this.infoRecord.getLongField(MAX).set(this.max);
148                 this.infoRecord.getLongField(QUEUE_SIZE).set(this.queueSize);
149                 this.infoRecord.getLongField(POPPED).set(this.popped);
150                 this.infoRecord.getLongField(TOTAL_POPPED).set(this.totalPopped);
151             }
152             finally
153             {
154                 this.infoRecord.endFrame();
155             }
156         }
157     }
158 
159     /**
160      * @return a string with max, queueSize, popped, totalPopped
161      */
162     @Override
163     public final String toString()
164     {
165         StringBuilder sb = new StringBuilder(80);
166         sb.append(this.max).append(COMMA_SPACE);
167         sb.append(this.queueSize).append(COMMA_SPACE);
168         sb.append(this.popped).append(COMMA_SPACE);
169         sb.append(this.totalPopped).append(COMMA_SPACE);
170         return sb.toString();
171     }
172 
173     /**
174      * Handles updating the info record with the statistics.
175      */
176     public void run()
177     {
178         this.totalPopped += this.popped;
179 
180         if (LOG.isInfoEnabled())
181         {
182             final String data = toString();
183             StringBuilder sb =
184                 new StringBuilder(data.length()
185                     + this.infoRecordIdentity.length() + 8);
186             sb.append(infoRecordIdentity).append("(stats) ");
187             sb.append(data);
188             LOG.info(sb.toString());
189         }
190         updateInfoRecord();
191 
192         // reset stats
193         this.max = 0;
194         this.popped = 0;
195     }
196 }