1 /*******************************************************************************
2 
3     Periodic console tracer
4 
5     Periodic console tracer - writes messages to the console limited to a
6     specified update interval. This can be used to safely limit the number of
7     writes to the console. The write is done using either Stderr or
8     ocean.util.log.StaticTrace, depending on the value of the struct's
9     static_display member.
10 
11 
12     Two global instances of this struct exist for convenience: PeriodicTrace and
13     StaticPeriodicTrace. The latter has the static_display flag set to true.
14 
15     Usage example with the global instance:
16 
17     ---
18 
19         import ocean.util.log.PeriodicTrace;
20 
21         const ulong trace_interval = 500_000; // only update display after at least half a second has passed
22 
23         for ( uint i; i < uint.max; i++ )
24         {
25             StaticPeriodicTrace.format(trace_interval, "{}", i);
26         }
27 
28     ---
29 
30     A local instance of the PeriodicTracer struct may be useful in situations
31     where two or more separate periodic outputs are required each with a
32     different update interval.
33 
34     Usage example with a local instance:
35 
36     ---
37 
38         import ocean.util.log.PeriodicTrace;
39 
40         PeriodicTracer trace1;
41         trace1.interval = 500_000; // only update display after at least half a second has passed
42 
43         PeriodicTracer trace2;
44         trace2.interval = 5_000_000; // only update display after at least 5 seconds have passed
45 
46         for ( uint i; i < uint.max; i++ )
47         {
48             trace1.format("{}", i);
49             trace2.format("{}", i);
50         }
51 
52     ---
53 
54     Note:
55         this struct automatically calls Trace.flush / StaticTrace.flush after
56         updating.
57 
58     Copyright:
59         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
60         All rights reserved.
61 
62     License:
63         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
64         Alternatively, this file may be distributed under the terms of the Tango
65         3-Clause BSD License (see LICENSE_BSD.txt for details).
66 
67 *******************************************************************************/
68 
69 module ocean.util.log.PeriodicTrace;
70 
71 import ocean.core.TypeConvert;
72 import ocean.io.Stdout;
73 import ocean.text.convert.Formatter;
74 import ocean.time.StopWatch;
75 import ocean.meta.types.Qualifiers;
76 import ocean.util.log.StaticTrace;
77 
78 
79 /*******************************************************************************
80 
81     Two shared instances of the PeriodicTracer struct, one with a normal
82     "streaming" display via Trace, and one with a static updating display via
83     StaticTrace.
84 
85 *******************************************************************************/
86 
87 public PeriodicTracer PeriodicTrace;
88 
89 public PeriodicTracer StaticPeriodicTrace;
90 
91 static this ( )
92 {
93     StaticPeriodicTrace.static_display = true;
94 }
95 
96 
97 
98 /*******************************************************************************
99 
100     PeriodicTracer struct.
101 
102 *******************************************************************************/
103 
104 struct PeriodicTracer
105 {
106     /***************************************************************************
107 
108         Minimum time between updates (microsec)
109 
110     ***************************************************************************/
111 
112     public ulong interval = 100_000; // defaults to 1/10 of a second
113 
114 
115     /***************************************************************************
116 
117         Toggles between static display (true) and line-by-line display (false)
118 
119     ***************************************************************************/
120 
121     public bool static_display;
122 
123 
124     /***************************************************************************
125 
126         Timer, shared by all instances of this struct (there's only one time!)
127 
128     ***************************************************************************/
129 
130     static public StopWatch timer;
131 
132 
133     /***************************************************************************
134 
135         Time of last update
136 
137     ***************************************************************************/
138 
139     private ulong last_update_time;
140 
141 
142     /***************************************************************************
143 
144         Time retrieved by the most recent call to timeToUpdate()
145 
146     ***************************************************************************/
147 
148     private ulong now;
149 
150 
151     /***************************************************************************
152 
153         Buffer for string formatting.
154 
155     ***************************************************************************/
156 
157     private mstring formatted;
158 
159 
160     /***************************************************************************
161 
162         Outputs a formatted string to the console if the update interval has
163         passed. The display is either static or adds a newline depending on the
164         this.static_display member.
165 
166         Params:
167             Args = Tuple of arguments to format
168             fmt = format string (same format as tanog.util.log.Trace)
169             args = variadic list of values referenced in format string
170 
171         Returns:
172             this instance for method chaining
173 
174     ***************************************************************************/
175 
176     public typeof(&this) format (Args...) ( cstring fmt, Args args )
177     {
178         if (this.timeToUpdate())
179         {
180             this.last_update_time = this.now;
181 
182             this.formatted.length = 0;
183             assumeSafeAppend(this.formatted);
184             sformat(this.formatted, fmt, args);
185 
186             if (this.static_display)
187                 StaticTrace.format("{}", this.formatted).flush;
188             else
189                 Stderr.formatln("{}", this.formatted).flush;
190         }
191         return &this;
192     }
193 
194 
195     /***************************************************************************
196 
197         Outputs a formatted string to the console if the specified update
198         interval has passed. The display is either static or adds a newline
199         depending on the this.static_display member.
200 
201         Params:
202             Args = Tuple of arguments to format
203             interval = minimum interval between display updates
204             fmt = format string (same format as tanog.util.log.Trace)
205             args = variadic list of values referenced in format string
206 
207         Returns:
208             this instance for method chaining
209 
210     ***************************************************************************/
211 
212     public typeof(&this) format (Args...) ( ulong interval, cstring fmt, Args args )
213     {
214         this.interval = interval;
215         return this.format(fmt, args);
216     }
217 
218 
219     // TODO: flush() method
220 
221 
222     /***************************************************************************
223 
224         Checks if it's time to update the display.
225 
226         Note: this method is public so that using classes can determine whether
227         they need to perform any internal update before calling display().
228 
229         TODO: this would be better done with a lazy char[] version of format(),
230         which only calls the lazy delegate if it *is* time to update.
231 
232         Returns:
233             true if the display update interval has passed
234 
235     ***************************************************************************/
236 
237     public bool timeToUpdate ( )
238     {
239         this.now = timer.microsec();
240         return this.now > this.last_update_time + this.interval;
241     }
242 
243 
244     /***************************************************************************
245 
246         Static constructor, starts the shared timer.
247 
248     ***************************************************************************/
249 
250     static this ( )
251     {
252         timer.start();
253     }
254 }