001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/framework/log/LoggerService.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2006 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53115 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    package org.deegree.framework.log;
045    
046    // J2EE 1.3
047    import java.io.BufferedOutputStream;
048    import java.io.BufferedWriter;
049    import java.io.File;
050    import java.io.FileOutputStream;
051    import java.io.FileWriter;
052    import java.io.IOException;
053    import java.net.InetAddress;
054    import java.net.UnknownHostException;
055    import java.text.DateFormat;
056    import java.text.MessageFormat;
057    import java.util.Date;
058    import java.util.Enumeration;
059    import java.util.Map;
060    import java.util.Properties;
061    
062    import javax.mail.Session;
063    
064    import org.deegree.framework.jndi.JndiUtils;
065    import org.deegree.framework.mail.EMailMessage;
066    import org.deegree.framework.mail.MailHelper;
067    import org.deegree.framework.mail.MailMessage;
068    import org.deegree.framework.util.BootLogger;
069    import org.deegree.framework.version.Version;
070    import org.deegree.framework.xml.XMLFragment;
071    
072    /**
073     * The Logger is used to log messages to files. This service will use a logging service provided by the application
074     * server or a 3rd party logging service such as Apache Log4J to enable asychronous call of the method log(). The log
075     * server is configured by a set of Properties which are provided to the class init.
076     * <p>
077     * There are some global properties as well: <BR>
078     * <UL>
079     * <LI><B>log.class </B>: the logging class.
080     * <LI><B>log.active </B>: to enable or disabel the logging service
081     * <LI><B>log.mail.active </B>: to activate the email notification
082     * <LI><B>log.mail.to </B>: the mail address
083     * <LI><B>log.mail.session </B>: the mail session used to send mail
084     * </UL>
085     * <P>
086     * Messages are logged using log(). If an error occurs during creation or logging, the message will be written to the
087     * server BootLogger.
088     * 
089     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </A>
090     * 
091     * @author last edited by: UID=$Author: mschneider $
092     * 
093     * @version $Revision: 7597 $, $Date: 2007-06-19 15:29:49 +0200 (Di, 19 Jun 2007) $
094     * 
095     * @see LoggerFactory
096     * @see org.deegree.framework.util.BootLogger
097     */
098    abstract class LoggerService implements ILogger {
099    
100        protected static String defaultChannelName;
101    
102        private static String hostAddress;
103    
104        private static String hostName;
105    
106        private static String mailNotificationAddress;
107    
108        private static String mailSessionName;
109    
110        private static Session mailSession;
111    
112        private static String mailHostName;
113    
114        private static boolean sendMailIsActive;
115    
116        private static DateFormat df;
117    
118        private static final String ENTERING_METHOD = "-> {1}#{0}() with arguments {2}";
119    
120        private static final String EXITING_METHOD = "<- {1}#{0}()";
121    
122        public void init( Properties props ) {
123    
124            df = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT );
125    
126            try {
127    
128                // fetch all configuration parameters
129                LoggerService.defaultChannelName = props.getProperty( "log.channel.name" );
130                LoggerService.mailNotificationAddress = props.getProperty( "log.mail.to" );
131                String mailerActive = props.getProperty( "log.mail.active" );
132                LoggerService.mailSessionName = props.getProperty( "log.mail.session" );
133                LoggerService.mailHostName = props.getProperty( "log.mail.smtphost" );
134    
135                // set defaults if not set
136                if ( mailerActive == null || mailerActive.equalsIgnoreCase( "false" ) ) {
137                    LoggerService.sendMailIsActive = false;
138                }
139                if ( defaultChannelName == null ) {
140                    LoggerService.defaultChannelName = LoggerService.class.getName();
141                }
142                if ( mailNotificationAddress == null ) {
143                    LoggerService.mailNotificationAddress = "UNKNOWN@" + LoggerService.mailHostName;
144                }
145    
146                hostAddress = InetAddress.getLocalHost().getHostAddress();
147                hostName = InetAddress.getLocalHost().getHostName();
148    
149            } catch ( UnknownHostException ex ) {
150                BootLogger.log( "Unable to determine host: " + ex.getMessage() );
151            } catch ( Exception ex ) {
152                ex.printStackTrace();
153                BootLogger.log( "Error while initializing " + LoggerService.class.getName() + " : " + ex.getMessage() );
154            } finally {
155                BootLogger.log( "Using: defaultChannelName=" + defaultChannelName + ", mailNotificationAddress="
156                                + mailNotificationAddress + ", mailSessionName=" + mailSessionName + ", log.mail.active="
157                                + LoggerService.sendMailIsActive );
158            }
159        }
160    
161        /**
162         * Create logger instance
163         * 
164         */
165        protected LoggerService() {
166            // only callable for the package.
167        }
168    
169        /**
170         * Log error with exception
171         * 
172         * @param message
173         *            the log message
174         * @param e
175         *            the exception to be logged
176         * @param properties
177         *            a given Property file in which specific email notification possibilities are saved.
178         */
179        public final void logError( String message, Throwable e, Map properties ) {
180            this.logError( message, e );
181            if ( sendMailIsActive ) {
182                this.sendMail( message, e, properties );
183            }
184        }
185    
186        /**
187         * Log warning message
188         * 
189         * @param message
190         *            the log message
191         */
192        public abstract void logWarning( String message );
193    
194        /**
195         * Log warning message with exception
196         * 
197         * @param message
198         *            the log message
199         * @param e
200         *            the exception to be logged
201         */
202        public abstract void logWarning( String message, Throwable e );
203    
204        /**
205         * Log info message
206         * 
207         * @param message
208         *            the log message
209         */
210        public abstract void logInfo( String message );
211    
212        /**
213         * Log info message
214         * 
215         * @param message
216         *            the log message
217         * @param e
218         *            the exception to be logged
219         */
220        public abstract void logInfo( String message, Throwable e );
221    
222        /**
223         * Log debug message
224         * 
225         * @param message
226         *            the log message
227         */
228        public abstract void logDebug( String message );
229    
230        /**
231         * Log debug message.
232         * 
233         * @param message
234         *            the log message
235         * @param e
236         *            the exception to be logged
237         */
238        public abstract void logDebug( String message, Throwable e );
239    
240        /**
241         * Logs the given text to the specified file if log level is set to <code>LOG_DEBUG</code>.
242         * 
243         * @param file
244         *            file to log to
245         * @param content
246         *            text to be logged
247         */
248        public void logDebugFile( File file, String content ) {
249            if ( getLevel() == LOG_DEBUG ) {
250                logDebug( "Writing debug file '" + file.getAbsolutePath() + "'." );
251                BufferedWriter writer = null;
252                try {
253                    writer = new BufferedWriter( new FileWriter( file ) );
254                    writer.write( content );
255                } catch ( IOException e ) {
256                    String msg = "Could not write to debug file '" + file.getAbsolutePath() + "'.";
257                    logError( msg, e );
258                } finally {
259                    if ( writer != null ) {
260                        try {
261                            writer.close();
262                        } catch ( IOException e ) {
263                            String msg = "Error closing debug file '" + file.getAbsolutePath() + "'.";
264                            logError( msg, e );
265                        }
266                    }
267                }
268            }
269        }
270    
271        /**
272         * Logs the given text to a temporary file (created from specified prefix and suffix) if log level is set to
273         * <code>LOG_DEBUG</code>.
274         * 
275         * @see File#createTempFile(String, String)
276         * @param filePrefix
277         *            prefix for the temp file name
278         * @param fileSuffix
279         *            suffix for the temp file name, can be null (then ".tmp" is used)
280         * @param content
281         *            text to be logged
282         */
283        public void logDebugFile( String filePrefix, String fileSuffix, String content ) {
284            if ( getLevel() == LOG_DEBUG ) {
285                try {
286                    File tmpFile = File.createTempFile( filePrefix, fileSuffix );
287                    logDebugFile( tmpFile, content );
288                } catch ( IOException e ) {
289                    String msg = "Cannot create debug file for prefix '" + filePrefix + "' and suffix '" + fileSuffix + ".";
290                    logError( msg, e );
291                }
292            }
293        }
294    
295        /**
296         * Logs the given {@link XMLFragment} to a temporary file (created from specified prefix and suffix ".xml") if log
297         * level is set to <code>LOG_DEBUG</code>.
298         * 
299         * @param filePrefix
300         *            prefix for the temp file name
301         * @param fragment
302         *            XMLFragment to be logged (will be pretty-printed)
303         */
304        public void logDebugXMLFile( String filePrefix, XMLFragment fragment ) {
305            if ( getLevel() == LOG_DEBUG ) {
306                logDebugFile( filePrefix, ".xml", fragment.getAsPrettyString() );
307            }
308        }
309    
310        /**
311         * Logs the given binary data to the specified file if log level is set to <code>LOG_DEBUG</code>.
312         * 
313         * @param file
314         *            file to log to
315         * @param data
316         *            binary data to be logged
317         */
318        public void logDebugBinaryFile( File file, byte[] data ) {
319            if ( getLevel() == LOG_DEBUG ) {
320                logDebug( "Writing binary debug file '" + file.getAbsolutePath() + "'." );
321                BufferedOutputStream out = null;
322                try {
323                    out = new BufferedOutputStream( new FileOutputStream( file ) );
324                    out.write( data );
325                } catch ( IOException e ) {
326                    String msg = "Could not write to debug file '" + file.getAbsolutePath() + "'.";
327                    logError( msg, e );
328                } finally {
329                    if ( out != null ) {
330                        try {
331                            out.close();
332                        } catch ( IOException e ) {
333                            String msg = "Error closing debug file '" + file.getAbsolutePath() + "'.";
334                            logError( msg, e );
335                        }
336                    }
337                }
338            }
339        }
340    
341        /**
342         * Logs the given binary data to a temporary file (created from specified prefix and suffix) if log level is set to
343         * <code>LOG_DEBUG</code>.
344         * 
345         * @see File#createTempFile(String, String)
346         * @param filePrefix
347         *            prefix for the temp file name
348         * @param fileSuffix
349         *            suffix for the temp file name, can be null (then ".tmp" is used)
350         * @param data
351         *            binary data to be logged
352         */
353        public void logDebugBinaryFile( String filePrefix, String fileSuffix, byte[] data ) {
354            if ( getLevel() == LOG_DEBUG ) {
355                try {
356                    File tmpFile = File.createTempFile( filePrefix, fileSuffix );
357                    logDebugBinaryFile( tmpFile, data );
358                } catch ( IOException e ) {
359                    String msg = "Cannot create debug file for prefix '" + filePrefix + "' and suffix '" + fileSuffix + ".";
360                    logError( msg, e );
361                }
362            }
363        }
364    
365        /**
366         * {@inheritDoc}
367         */
368        public void entering() {
369            StackTraceElement[] stack = new Exception().getStackTrace();
370            StackTraceElement element = null;
371            if ( stack.length > 2 ) {
372                element = stack[1];
373                this.entering( element.getClassName(), element.getMethodName() );
374            }
375        }
376    
377        /**
378         * {@inheritDoc}
379         */
380        public void entering( String sourceClass, String sourceMethod, Object[] arguments ) {
381            this.logDebug( this.produceMessage( ENTERING_METHOD, new Object[] { sourceMethod, sourceClass, arguments } ) );
382        }
383    
384        /**
385         * 
386         * 
387         * 
388         */
389        public void entering( String sourceClass, String sourceMethod ) {
390            this.entering( sourceClass, sourceMethod, null );
391        }
392    
393        /*
394         * (non-Javadoc)
395         * 
396         * @see org.deegree_impl.log.ILogger#exiting(java.lang.String, java.lang.String, java.lang.Object[])
397         */
398        public void exiting( String sourceClass, String sourceMethod, Object[] arguments ) {
399            this.logDebug( this.produceMessage( EXITING_METHOD, new Object[] { sourceMethod, sourceClass } ) );
400        }
401    
402        /**
403         * {@inheritDoc}
404         */
405        public void exiting() {
406            StackTraceElement[] stack = new Exception().getStackTrace();
407            StackTraceElement element = null;
408            if ( stack.length > 2 ) {
409                element = stack[1];
410                this.exiting( element.getClassName(), element.getMethodName() );
411            }
412        }
413    
414        /**
415         * {@inheritDoc}
416         */
417        public void exiting( String sourceClass, String sourceMethod ) {
418            this.exiting( sourceClass, sourceMethod, null );
419        }
420    
421        /**
422         * Formats the provided string and the args array into a String using MessageFormat.
423         */
424        private String produceMessage( String pattern, Object[] args ) {
425            return new MessageFormat( pattern ).format( args );
426        }
427    
428        /**
429         * Sends email with exception string. If mail session or mail notification address is null no email is send.
430         * 
431         * @param message
432         *            message is in mail subject
433         * @param ex
434         *            full exception is displayed in body
435         * @param properties
436         *            list of properties listed in body
437         */
438        protected void sendMail( String message, Throwable ex, Map properties ) {
439            if ( sendMailIsActive && mailSessionName != null && mailNotificationAddress != null ) {
440                this.sendMailNotification( message, ex, properties );
441            }
442        }
443    
444        private void sendMailNotification( String message, Throwable ex, Map properties ) {
445            MailMessage mail;
446            StringBuffer emailBody = new StringBuffer();
447    
448            emailBody.append( "A critical error occured in " + Version.getVersion() );
449            emailBody.append( " running on " + hostName + "/" + hostAddress );
450            emailBody.append( " on " + df.format( new Date( System.currentTimeMillis() ) ) );
451            emailBody.append( "\n\n" );
452            if ( message != null ) {
453                emailBody.append( "\n\nThe error message: " + message );
454            }
455            if ( properties != null ) {
456                emailBody.append( "\n\nRequest data:\n--------------\n" + properties.toString() );
457            }
458            if ( ex != null ) {
459                emailBody.append( "\n\nException:\n---------\n" + ex.getMessage() );
460                emailBody.append( "\n\nStack Trace:\n------------\n\n" + ex.toString() );
461            }
462            emailBody.append( "\n\nSystem Info:\n--------------\n" + getSystemInfo() );
463    
464            String subject = "Critical Error:" + Version.getVersion() + " on " + hostName + "/" + hostAddress;
465    
466            mail = new EMailMessage( "DO_NOT_REPLY@" + hostName, mailNotificationAddress, subject, emailBody.toString() );
467            try {
468                if ( mailSession == null ) {
469                    mailSession = this.getMailSession();
470                }
471                new MailHelper().createAndSendMail( mail, mailSession );
472            } catch ( Exception e ) {
473                BootLogger.logError( "Can't send email notification: " + mail.getHeader(), e );
474            }
475        }
476    
477        /**
478         * Return system information (memory and properties) as string.
479         * 
480         * @return system information (memory and properties) as string.
481         */
482        private String getSystemInfo() {
483            StringBuffer buf = new StringBuffer();
484            buf.append( "Total Memory: " + Runtime.getRuntime().totalMemory() / 1024 + " Kilobyte\n" );
485            buf.append( "Free Memory: " + Runtime.getRuntime().freeMemory() / 1024 + " Kilobyte\n" );
486            java.util.Properties sysprops = System.getProperties();
487            for ( Enumeration e = sysprops.keys(); e.hasMoreElements(); ) {
488                String key = e.nextElement().toString();
489                String value = sysprops.getProperty( key );
490                buf.append( key + " : " + value + "\n" );
491            }
492            return buf.toString();
493        }
494    
495        /**
496         * 
497         */
498        private Session getMailSession() {
499            Session session = null;
500            try {
501                session = (Session) JndiUtils.lookup( mailSessionName, Session.class );
502            } catch ( Exception ex ) {
503                BootLogger.logError( "Error while initializing " + LoggerService.class.getName() + " : " + ex.getMessage(),
504                                     ex );
505            } finally {
506                if ( session == null ) {
507                    Properties p = System.getProperties();
508                    p.put( "mail.smtp.host", LoggerService.mailHostName );
509                    session = Session.getDefaultInstance( p, null );
510                }
511            }
512            return session;
513        }
514    
515    }