/*********************************************************************************
* The contents of this file are subject to the Common Public Attribution
* License Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.openemm.org/cpal1.html. The License is based on the Mozilla
* Public License Version 1.1 but Sections 14 and 15 have been added to cover
* use of software over a computer network and provide for limited attribution
* for the Original Developer. In addition, Exhibit A has been modified to be
* consistent with Exhibit B.
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is OpenEMM.
* The Original Developer is the Initial Developer.
* The Initial Developer of the Original Code is AGNITAS AG. All portions of
* the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights
* Reserved.
*
* Contributor(s): AGNITAS AG.
********************************************************************************/
package org.agnitas.util;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.EmptyStackException;
import java.util.Stack;
/**
* This class provides a common logging interface with separate
* logging levels. Logfiles are typically written under the
* home directory of the user in var/log
*/
public class Log {
/**
* global error which may harm things beyond the application
*/
final public static int GLOBAL = 0;
/**
* fatal error which requires manual correction and termiantes
* the application
*/
final public static int FATAL = 1;
/**
* error which can require manual correction
*/
final public static int ERROR = 2;
/**
* warning which can be a hint for some problems
*/
final public static int WARNING = 3;
/**
* more important runtime information
*/
final public static int NOTICE = 4;
/**
* some general runtime information
*/
final public static int INFO = 5;
/**
* more verbose information
*/
final public static int VERBOSE = 6;
/**
* just debug output, in normal operation mostly useless
*/
final public static int DEBUG = 7;
/**
* textual represenation of loglevels
*/
final static String[] DESC = {
"GLOBAL",
"FATAL",
"ERROR",
"WARNING",
"NOTICE",
"INFO",
"VERBOSE",
"DEBUG"
};
/**
* the level up to we will write to the logfile
*/
private int level;
/**
* optional output print
*/
private PrintStream printer;
/**
* to provide some hirarchical log IDs
*/
private Stack <String> idc;
/**
* the base path to the log file directory
*/
private String path;
/**
* the part of the logfile after the current date
*/
private String append;
/**
* format of date for logfilename
*/
private SimpleDateFormat fmt_fname;
/**
* format of date to be written to logfile
*/
private SimpleDateFormat fmt_msg;
/**
* Find the numeric representation of a textual loglevel
*
* @param desc the textual loglevel
* @return its numeric value
* @throws java.lang.NumberFormatException
*/
static public int matchLevel (String desc) throws NumberFormatException {
for (int n = 0; n < DESC.length; ++n) {
if (DESC[n].equalsIgnoreCase (desc)) {
return n;
}
}
return Integer.parseInt (desc);
}
/**
* Make a textual description of the given loglevel
*
* @param loglvl the numeric loglevel
* @return its string version
*/
static public String levelDescription (int loglvl) {
if ((loglvl >= 0) && (loglvl < DESC.length)) {
return DESC[loglvl];
}
return "(" + loglvl + ")";
}
/**
* For pretty printing, this returns an empty string on 1,
* otherwise "s"
*
* @param nr the number to check
* @return the extension based on the number
*/
static public String exts (long nr) {
return (nr == 1) ? "" : "s";
}
/**
* Wrapper for integer input to exts
*
* @param nr the number to check
* @return the extension based on the number
*/
static public String exts (int nr) {
return exts ((long) nr);
}
/**
* Like exts, but for words like entry vs. entries
*
* @param nr the number to check
* @return the extension based on the number
*/
static public String exty (long nr) {
return (nr == 1) ? "y" : "ies";
}
/**
* Wrapper for integer input to exty
*
* @param nr the number to check
* @return the extension based on the number
*/
static public String exty (int nr) {
return exty ((long) nr);
}
/**
* Constructor for class
*
* @param program the name of the application
* @param level the maximum log level to report
*/
public Log (String program, int level) {
this.level = level;
printer = null;
String separator = System.getProperty ("file.separator");
String home = System.getProperty ("user.home", ".");
String logdir;
String hostname;
int idx;
idc = new Stack <String> ();
logdir = System.getProperty ("log.home", home + separator + "var" + separator + "log");
if (logdir == null) {
path = "";
} else {
path = logdir + separator;
}
try {
InetAddress addr = InetAddress.getLocalHost ();
try {
hostname = addr.getHostName ();
if ((idx = hostname.indexOf ('.')) != -1) {
hostname = hostname.substring (0, idx);
}
} catch (SecurityException e) {
hostname = addr.getHostAddress ();
}
} catch (UnknownHostException e) {
hostname = "unknown";
}
if ((idx = program.lastIndexOf (separator)) != -1) {
program = program.substring (idx + 1);
}
append = "-" + hostname + "-" + program + ".log";
fmt_fname = new SimpleDateFormat ("yyyyMMdd");
fmt_msg = new SimpleDateFormat ("[dd.MM.yyyy HH:mm:ss] ");
}
/**
* returns the current loglevel
*
* @return log level
*/
public int level () {
return level;
}
/**
* sets the current loglevel
*
* @param nlevel new log level
*/
public void level (int nlevel) {
level = nlevel;
}
/**
* returns the textual representation of the
* current loglevel
*
* @return current loglevel as string
*/
public String levelDescription () {
return levelDescription (level);
}
/**
* sets the loglevel using its textual representation
* @param desc the loglevel as string
*/
public void levelDescription (String desc) throws NumberFormatException {
level = matchLevel (desc);
}
/** sets the optional output stream
*
* @param nprinter new stream
*/
public void setPrinter (PrintStream nprinter) {
printer = nprinter;
}
/**
* Pushes a new id, so a chain of IDs is put into any
* logfile entry
*
* @param nid the new id
* @param separator howto separate the the IDs
*/
public void pushID (String nid, String separator) {
if ((separator != null) && (! idc.empty ())) {
idc.push (idc.peek () + separator + nid);
} else {
idc.push (nid);
}
}
/**
* Pushes a new id without separator
*
* @param nid the new id
*/
public void pushID (String nid) {
pushID (nid, null);
}
/**
* Removes to top element of the ID stack
*
* @return the top ID on the stack or null, if stack is empty
*/
public String popID () {
try {
return idc.pop ();
} catch (EmptyStackException e) {
;
}
return null;
}
/**
* Clear all stacked IDs
*/
public void clrID () {
idc.clear ();
}
/**
* Set ID after removing all existing IDs
*
* @param mid the ID to set
*/
public void setID (String mid) {
idc.clear ();
idc.push (mid);
}
/**
* check if the given level should be logged
*
* @param loglvl the level to check
* @return true, if logging is enabled for this level
*/
public boolean islog (int loglvl) {
return loglvl <= level;
}
/**
* writes an entry to the logfile
*
* @param loglvl the level of this message
* @param mid the ID of this message
* @param msg the message itself
*/
public void out (int loglvl, String mid, String msg) {
if (loglvl <= level) {
Date now = new Date ();
String fname = path + fmt_fname.format (now) + append;
String output = fmt_msg.format (now) + levelDescription (loglvl) + (mid != null ? "/" + mid : "") + ": " + msg + "\n";
try {
FileOutputStream file = new FileOutputStream (fname, true);
file.write (output.getBytes ());
file.close ();
if (printer != null) {
printer.println (msg);
}
} catch (Exception e) {
System.err.print (output);
}
}
}
/**
* writes an entry to the logfile using the stacked ID
*
* @param loglvl the level of this message
* @param msg the mesage itself
*/
public void out (int loglvl, String msg) {
String mid;
try {
mid = idc.peek ();
} catch (EmptyStackException e) {
mid = null;
}
out (loglvl, mid, msg);
}
}