1 /*
2  * Created on 22-Sep-2004
3  * Created by Paul Gardner
4  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 package org.gudy.azureus2.core3.util;
21 
22 import java.io.ByteArrayOutputStream;
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.OutputStreamWriter;
26 import java.io.PrintWriter;
27 import java.text.SimpleDateFormat;
28 import java.util.Calendar;
29 import java.util.Date;
30 import java.util.GregorianCalendar;
31 import java.util.LinkedList;
32 import java.util.TimeZone;
33 
34 import org.gudy.azureus2.core3.logging.Logger;
35 
36 /**
37  * @author parg
38  *
39  */
40 public class
41 AEDiagnosticsLogger
42 {
43 	private static final int	MAX_PENDING = 8*1024;
44 
45 	private String 			name;
46 	private int				max_size;
47 	private File			debug_dir;
48 	private boolean			timestamp_enable				= true;
49 	private boolean			force;
50 
51 	private boolean			first_file				= true;
52 	private boolean			first_write			 	= true;
53 	private PrintWriter		current_writer;
54 
55 	private LinkedList<StringBuilder>	pending;
56 	private int							pending_size;
57 	private boolean						direct_writes;
58 
59 	private boolean		close_pws = false;
60 
61 	private static final String	start_date;
62 	private static final long	timezone_offset;
63 
64 	static{
65 		long		now = System.currentTimeMillis();
66 
67 		start_date = new SimpleDateFormat().format( new Date(now));
68 
69 		timezone_offset = TimeZone.getDefault().getOffset(now);
70 	}
71 
72 	protected
AEDiagnosticsLogger( File _debug_dir, String _name, int _max_size, boolean _direct_writes )73 	AEDiagnosticsLogger(
74 		File		_debug_dir,
75 		String		_name,
76 		int			_max_size,
77 		boolean		_direct_writes )
78 	{
79 		debug_dir		= _debug_dir;
80 		name			= _name;
81 		max_size		= _max_size;
82 		direct_writes	= _direct_writes;
83 
84 		try{
85 			File	f1 = getLogFile();
86 
87 			first_file = false;
88 
89 			File	f2 = getLogFile();
90 
91 			first_file = true;
92 
93 				// if we were writing to the second file, carry on from there
94 
95 			if ( f1.exists() && f2.exists()){
96 
97 				if ( f1.lastModified() < f2.lastModified()){
98 
99 					first_file = false;
100 				}
101 			}
102 		}catch( Throwable ignore ){
103 
104 		}
105 	}
106 
107 	public void
setForced( boolean _force )108 	setForced(
109 		boolean		_force )
110 	{
111 		force = _force;
112 	}
113 
114 	public boolean
isForced()115 	isForced()
116 	{
117 		return( force );
118 	}
119 
120 	protected String
getName()121 	getName()
122 	{
123 		return( name );
124 	}
125 
126 	public void
setMaxFileSize( int _max_size )127 	setMaxFileSize(
128 		int		_max_size )
129 	{
130 		max_size	= _max_size;
131 	}
132 
133 	public void
enableTimeStamp( boolean enable )134 	enableTimeStamp(
135 		boolean	enable )
136 	{
137 		timestamp_enable	= enable;
138 	}
139 
140 	public void
log( Throwable e )141 	log(
142 		Throwable				e )
143 	{
144 		try{
145 			ByteArrayOutputStream	baos = new ByteArrayOutputStream();
146 
147 			PrintWriter	pw = new PrintWriter( new OutputStreamWriter( baos ));
148 
149 			e.printStackTrace( pw );
150 
151 			pw.close();
152 
153 			log( baos.toString());
154 
155 		}catch( Throwable ignore ){
156 
157 		}
158 	}
159 
160 	public void
logAndOut( String str )161 	logAndOut(
162 		String		str )
163 	{
164 		logAndOut( str, false );
165 	}
166 
167 	public void
logAndOut( String str, boolean stderr )168 	logAndOut(
169 		String		str,
170 		boolean		stderr )
171 	{
172 		if ( stderr ){
173 
174 			System.err.println( str );
175 
176 			// Logger dumps the stderr, but if it's not setup, do it outselves
177 			if (Logger.getOldStdErr() == null) {
178 				log( str );
179 			}
180 
181 		}else{
182 
183 			System.out.println( str );
184 			log( str );
185 		}
186 
187 	}
188 
189 	public void
logAndOut( Throwable e )190 	logAndOut(
191 		Throwable 	e )
192 	{
193 		e.printStackTrace();
194 
195 		log( e );
196 	}
197 
198 	/*
199 	public static String
200 	getTimestamp()
201 	{
202 		Calendar now = GregorianCalendar.getInstance();
203 
204 		String timeStamp = "[" + format(now.get(Calendar.DAY_OF_MONTH))+format(now.get(Calendar.MONTH)+1) + " " +
205 						format(now.get(Calendar.HOUR_OF_DAY))+ ":" + format(now.get(Calendar.MINUTE)) + ":" + format(now.get(Calendar.SECOND)) + "] ";
206 
207 		return( timeStamp );
208 	}
209 	*/
210 
211 	public static String
getTimestamp()212 	getTimestamp()
213 	{
214 		long time = SystemTime.getCurrentTime();
215 
216 		time += timezone_offset;		// we'll live with this changing...
217 
218 		time /= 1000;
219 
220 	    int secs = (int)time % 60;
221 	    int mins = (int)(time / 60) % 60;
222 	    int hours = (int)(time /3600) % 24;
223 
224 	    char[]	chars = new char[11];
225 
226 	    chars[0] = '[';
227 	    format( hours, chars, 1 );
228 	    chars[3] = ':';
229 	    format( mins, chars, 4 );
230 	    chars[6] = ':';
231 	    format( secs, chars, 7 );
232 	    chars[9] = ']';
233 	    chars[10] = ' ';
234 
235 		return( new String( chars ));
236 	}
237 
238 	private static final void
format( int num, char[] chars, int pos )239 	format(
240 		int		num,
241 		char[]	chars,
242 		int		pos )
243 	{
244 		if ( num < 10 ){
245 			chars[pos] = '0';
246 			chars[pos+1] =(char)( '0' + num );
247 		}else{
248 			chars[pos] 		= (char)('0' + (num/10));
249 			chars[pos+1]	= (char)('0' + (num%10));
250 		}
251 	}
252 
253 	public void
log( String _str )254 	log(
255 		String	_str )
256 	{
257 		if ( !AEDiagnostics.loggers_enabled ){
258 
259 			if ( !force ){
260 
261 				return;
262 			}
263 		}
264 
265 		StringBuilder str = new StringBuilder( _str.length() + 20 );
266 
267 		final String timeStamp;
268 
269 		if ( timestamp_enable ){
270 
271 			timeStamp = getTimestamp();
272 
273 		}else{
274 
275 			timeStamp = null;
276 		}
277 
278 		synchronized( this ){
279 
280 			if ( first_write ){
281 
282 				first_write = false;
283 
284 				Calendar now = GregorianCalendar.getInstance();
285 
286 				str.append( "\r\n[" );
287 				str.append( start_date );
288 				str.append( "] Log File Opened for " );
289 				str.append(  Constants.APP_NAME );
290 				str.append( " " );
291 				str.append(  Constants.AZUREUS_VERSION );
292 				str.append( "\r\n" );
293 			}
294 
295 			if ( timeStamp != null ){
296 
297 				str.append( timeStamp );
298 			}
299 
300 			str.append( _str );
301 
302 			if ( !direct_writes ){
303 
304 				if ( pending == null ){
305 
306 					pending = new LinkedList<StringBuilder>();
307 				}
308 
309 				pending.add( str );
310 
311 				pending_size += str.length();
312 
313 				if ( pending_size > MAX_PENDING ){
314 
315 					writePending();
316 				}
317 
318 				return;
319 			}
320 
321 			write( str );
322 		}
323 	}
324 
325 	private void
write( StringBuilder str )326 	write(
327 		StringBuilder		str )
328 	{
329 		try{
330 			File	log_file	= getLogFile();
331 
332 				/**
333 				 *  log_file.length will return 0 if the file doesn't exist, so we don't need
334 				 *  to explicitly check for its existence.
335 				 */
336 
337 			if ( log_file.length() >= max_size ){
338 
339 				if ( current_writer != null ){
340 
341 					current_writer.close();
342 
343 					current_writer = null;
344 				}
345 
346 				first_file = !first_file;
347 
348 				log_file	= getLogFile();
349 
350 					// If the file doesn't exist, this will just return false.
351 
352 				log_file.delete();
353 			}
354 
355 			if ( current_writer == null ){
356 
357 				current_writer = new PrintWriter( new OutputStreamWriter( new FileOutputStream( log_file, true ), "UTF-8" ));
358 			}
359 
360 			current_writer.println( str );
361 
362 			current_writer.flush();
363 
364 		}catch( Throwable e ){
365 
366 		}finally{
367 
368 			if ( current_writer != null && close_pws ){
369 
370 				current_writer.close();
371 
372 				current_writer = null;
373 			}
374 		}
375 	}
376 
377 	protected void
writePending()378 	writePending()
379 	{
380 		synchronized( this ){
381 
382 			if ( pending == null ){
383 
384 				return;
385 			}
386 
387 			// System.out.println( getName() + ": flushing " + pending_size );
388 
389 			try{
390 				File	log_file	= getLogFile();
391 
392 					/**
393 					 *  log_file.length will return 0 if the file doesn't exist, so we don't need
394 					 *  to explicitly check for its existence.
395 					 */
396 
397 				if ( log_file.length() >= max_size ){
398 
399 					if ( current_writer != null ){
400 
401 						current_writer.close();
402 
403 						current_writer = null;
404 					}
405 
406 					first_file = !first_file;
407 
408 					log_file	= getLogFile();
409 
410 						// If the file doesn't exist, this will just return false.
411 
412 					log_file.delete();
413 				}
414 
415 				if ( current_writer == null ){
416 
417 					current_writer = new PrintWriter( new OutputStreamWriter( new FileOutputStream( log_file, true ), "UTF-8" ));
418 				}
419 
420 				for ( StringBuilder str: pending ){
421 
422 					current_writer.println( str );
423 				}
424 
425 				current_writer.flush();
426 
427 			}catch( Throwable e ){
428 
429 			}finally{
430 
431 				direct_writes 	= true;
432 				pending			= null;
433 
434 				if ( current_writer != null && close_pws ){
435 
436 					current_writer.close();
437 
438 					current_writer = null;
439 				}
440 			}
441 		}
442 	}
443 
444 	private File
getLogFile()445 	getLogFile()
446 	{
447 		return( new File( debug_dir, getName() + "_" + (first_file?"1":"2") + ".log" ));
448 	}
449 
450 	private static String
format( int n )451 	format(
452 		int 	n )
453 	{
454 		if (n < 10){
455 
456 			return( "0" + n );
457 	   }
458 
459 	   return( String.valueOf(n));
460 	}
461 }
462