|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectbb.util.logging.LogUtil
public final class LogUtil
Provides static constants and utility methods relating to the java.util.logging
package.
The most important functionality offered by this class is the various makeLogger2
methods.
A default Logger2 may be obtained by calling getLogger2
.
The remaining public methods offer all kinds of other services.
The makeLogger2 methods are especially powerful because they can draw on new logging properties supported by this class. The next sections of documentation explain the background, motivation, and behavior of these new properties.
One characteristic of the java.util.logging
package
is that all named (i.e. non-anonymous) Loggers form a parent/child hierarchy.
From the LogManager
javadocs:
Loggers are organized into a naming hierarchy based on their dot separated names. Thus "a.b.c" is a child of "a.b", but "a.b1" and a.b2" are peers.One reason for this hierarchy is that some behavior (the
level
and handlers
properties)
in child Loggers can be inherited in some sense from its parents.
Another characteristic of the java.util.logging
package
is that much of this behavior can be specified outside of code.
In particular, the java.util.logging.config.file
System property
may be used to point to a logging properties file that specifies default behaviors.
Consult LogManager
as well as the links above for details.
A sample logging properties file is the .../script/logging.properties
file in this project.
useParentHandlers
property is never inherited at all.
A Logger is by default created with useParentHandlers
set to true.
To set useParentHandlers
set to false requires explicit configuration
(i.e. the logging properties file must have a <logger>.level
entry
where <logger>
exactly matches the name of the Logger,
or else there must be Java code somewhere which calls setUseParentHandlers
).
Unfortunately, even the "inheritable" properties (level
and handlers
) have issues.
From Jason Hunter's JDK logging tutorial:
A child inherits the properties of its parent, or its parents parent, going to the root for global default if necessary. Inheritance works a little differently than in object-oriented programs... The tricky part of parentage is understanding that setting a parent property doesn't push the setting on its children. Child loggers have their own settings but at runtime look up the tree for a value if they ever have an unset property.
This form of "inheritance" is not a problem for the logging Level
.
For example, suppose your logging properties file only specifies the property com.level = INFO
.
Next suppose you subsequently only create a Logger named com.xyz
,
and rely only on the logging properties file for configuration (i.e. no explicit configuration in code).
Then because there is no com.xyz.level
property,
the com.xyz
Logger will initially have null for its Level,
which means that every log request will cause a search up the Logger hierarchy until a Logger with a non-null Level is found.
(Note that when LogManager registers a Logger,
it will also create all parent Loggers which have a Level specified in the properties file.)
Thus, the net effect is that the com.xyz
Logger will at runtime always search for its Level,
which it will find in its immediate parent, the com
Logger.
In contrast, this form of "inheritance" may be a huge problem with certain logging Handlers.
For instance, suppose the com.xyz
package in some Java codebase has a bunch of classes inside it,
and each class needs its own dedicated Logger in order to classify events and so simplify analysis.
Furthermore, suppose that each of these per class Loggers needs a FileHandler
to persist all logs to a file.
Now if your logging properties file merely contained the entry com.xyz.handlers = java.util.logging.FileHandler
what would happen is that only the parent Logger (i.e. the one named com.xyz
)
would have that FileHandler.
All the child Loggers (e.g. com.xyz.foo
, com.xyz.bar
, etc)
would use the same FileHandler instance in their parent (assuming their useParentHandlers
is true).
This fails the per class FileHandler requirement.
You could achieve it by having dedicated property entries
(e.g. com.xyz.foo.handlers = java.util.logging.FileHandler
, com.xyz.bar.handlers = java.util.logging.FileHandler
, etc)
but this is very cumbersome and bug prone (e.g. if you refactor a class name, you have to remember to change the logging properties file too).
logDirectory-NS
.
Defines the path of the parent directory of all log files created by this class.
This directory will be created if currently unexisting.
Log files created by this class include those explicitly created by makeLogFile
,
as well as files implicitly created by FileHandler
s
specified by the <logger>.handlers-NS
property below.
<logger>.level-NS
.
Analogous to the existing <logger>.level
property
defined in LogManager
but with differences described below.
<logger>.handlers-NS
.
Analogous to the existing <logger>.handlers
property
defined in LogManager
but with differences described below.
<logger>.useParentHandlers-NS
.
Analogous to the existing <logger>.useParentHandlers
property
defined in LogManager
but with differences described below.
These properties must be defined in the same logging properties file
that is used to specify the java.util.logging
properties for your project.
These property names all end with -NS
because they are non-standard properties:
the JDK logging code (e.g. LogManager) does not understand them, only a special class like this one does.
This class's static initializer will reread any logging properties file and process the above properties.
Only Loggers created by the makeLogger2
method
will have these non-standard properties applied to them.
The <logger>.level-NS
, <logger>.handlers-NS
, and <logger>.useParentHandlers-NS
properties were introduced in order to fix the defects with the JDK's logging property "inheritance" noted above.
These properties behave as follows:
<logger>
part is actually a name prefix
(i.e. the <logger>
pattern is assumed to end with an implicit * wildcard).
For example, specifying com.level-NS = ...
will cause both a Logger named com
as well as Loggers named com123
and com.xyz
to match the pattern and be assigned the Level.
This wildcard matching thus transends Logger parent/child boundaries.
Continuing the example, the com
Logger is the parent of the com.xyz
Logger
but is not the parent of the com123
Logger (its parent is the root "" Logger).
This contrasts with the existing JDK logging properties, where the <logger>
part is the full name of some Logger.
<logger>
part may be a special universal match value of last resort.
The value "+" matches any non-empty Logger name if no other prefix matches.
For example, if your logging property file only defines +.level-NS = FINE
,
then every Logger (except the root Logger which has the empty name "")
will have FINE as its Level.
This pattern allows convenient setting of general behavior.
One major issue with using a name prefix,however, is how to handle multiple matches.
For example, if only the properties com.level-NS = INFO
and com.xyz.level-NS = FINE
are defined in the logging properties file, which one does a Logger named com.xyz.foo
receive?
After all, both the com
and com.xyz
prefixes match in this case.
This class's policy is to use the longest length matching prefix, since that is likely to be the most specific.
Continuing the example, the com.xyz.foo
Logger would be assigned the FINE Level
since com.xyz
is longer than com
.
In the case of multiple "best matches", one is arbitrarily chosen.
Note that the "+" universal match value mentioned above is only used if no other name prefix matched.
In Java we first saw variable substitution used for internationalization and localization, because in those cases simple string concatentation isn't possible. With simple log() methods you could rely on string concatentation like this:This seems like a really clever use of param substitution, but it is not obvious that will actually reduce the cpu impact of logging very much. Reason: it is almost impossible to use params in one of the log/logp methods of Logger without causing an Object[] to be created. (The sole exception is the rare situation when all the params that you need are already in an array.) Thus, you always take a hit from creating that Object[], which is about as bad as creating a new String. Furthermore, if the level check is passed, then the work that needs to be done to actually carry out the param substitution is somewhat greater than doing simple String concatenation. So, you may not gain much performance. Since param substitution makes your code look even worse, you should only use param substitution for what it was originally designed for, namely, to support pre-localized generic messages that can subsequently be localized and then have params substituted in. This is discussed further here.logger.log(Level.INFO, "Your file was " + size + " bytes long");
The problem with this approach is that the runtime must perform the string concatentation before executing the log() method call. That wastes CPU cycles and memory when the logger's going to reject the message for being at too low a level. The solution is to use variable substitution and avoid all concatentation:logger.log(Level.INFO, "Your file was {0} bytes long", size);
This call executes directly and the substitution only happens after the message has passed the logger's quick level check.
logger2_default
, which is protected by synchronized access.
Nested Class Summary | |
---|---|
private static class |
LogUtil.NodeProp
|
static class |
LogUtil.UnitTest
See the Overview page of the project's javadocs for a general description of this unit test class. |
Field Summary | |
---|---|
private static Level[] |
levels
|
private static File |
logDirectory
Directory where log files are to be stored. |
private static String |
logDirectory_key
|
private static String |
logDirectory_valueDefault
|
private static Logger2 |
logger2_default
A default Logger2 instance that any user of this class may draw on via getLogger2
if they do not want to bother creating their own Logger. |
private static ConcurrentMap<String,LogUtil.NodeProp> |
nameToNodeProps
|
private static String |
nsPatternUniversalMatch
Serves as the universal matching pattern of last resort. |
private static String |
nsSuffix
|
Constructor Summary | |
---|---|
private |
LogUtil()
This private constructor suppresses the default (public) constructor, ensuring non-instantiability. |
Method Summary | |
---|---|
static void |
addHandlers(Handler[] handlers,
Logger logger)
Adds every element of handlers to logger. |
static void |
close(Logger logger)
Immediately returns if logger == null. |
private static File |
extract_logDirectory(Properties properties)
|
static void |
flush(Logger logger)
Immediately returns if logger == null. |
static Level[] |
getLevels()
Returns an array of all the possible Levels, sorted into ascending order (according to Level.intValue ). |
static File |
getLogDirectory()
Accessor for logDirectory . |
static Logger2 |
getLogger2()
Accessor for logger2_default . |
static String |
getName(Class c,
String suffix)
Determines an appropriate Logger name for the arguments. |
static String |
getPath(Class c,
String suffix)
Returns . |
static String |
getPath(String name)
Returns an appropriate path to a log file for name. |
private static void |
init_nameToNodeProps(Properties properties)
|
private static Properties |
loadProperties()
|
static File |
makeLogFile(String childPath)
Returns a new File instance which points to a location inside the log directory . |
static Logger2 |
makeLogger2(Class c)
Returns . |
static Logger2 |
makeLogger2(Class c,
String suffix)
Returns . |
static Logger2 |
makeLogger2(String name)
Returns . |
static Logger2 |
makeLogger2(String name,
String resourceBundleName)
Returns a new Logger2 created by a call to . |
static PrintWriter |
makeLogWriter(File directory,
String prefix,
boolean autoFlush)
Returns a new PrintWriter that ultimately writes to a file located in directory. |
static PrintWriter |
makeLogWriter(String prefix)
Returns (i.e. no auto flushing). |
static PrintWriter |
makeLogWriter(String prefix,
boolean autoFlush)
Returns (i.e. the default log directory is used). |
static void |
nsHandlers(Logger logger)
Calls NodeProp.findBest to get the NodeProp
whose associated key best matches logger's name and whose handlerClasses field is non-null. |
static void |
nsLevel(Logger logger)
Calls NodeProp.findBest to get the NodeProp
whose associated key best matches logger's name and whose level field is non-null. |
static void |
nsUseParentHandlers(Logger logger)
Calls NodeProp.findBest to get the NodeProp
whose associated key best matches logger's name and whose useParentHandlers field is non-null. |
static Level |
parseLevel(String s)
Returns the Level that corresponds to s. |
static Handler[] |
removeHandlers(Logger logger)
Removes all of logger's Handlers. |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Field Detail |
---|
private static final Level[] levels
private static final String logDirectory_key
private static final String logDirectory_valueDefault
private static final File logDirectory
Internally used by this class's getPath(String)
method,
and therefore implicitly used by the various makeLogger2 methods.
May be used by external users in arbitrary ways.
Contract: is never null, and always exists.
private static final String nsPatternUniversalMatch
-NS
properties as well as findBest
.
private static final String nsSuffix
private static final ConcurrentMap<String,LogUtil.NodeProp> nameToNodeProps
private static Logger2 logger2_default
getLogger2
if they do not want to bother creating their own Logger.
This Logger always has the name "default".
Note: while Logger.global
is a similar default Logger,
there are several reasons why this field was introduced:
makeLogger2
("default")
,
this means that the logging properties file can specify -NS
properties
for this field (use the Logger name "default");
this is particularly valuable if want it to write to a log file
located in logDirectory
, and with a timestamp in its file name.
Constructor Detail |
---|
private LogUtil()
Method Detail |
---|
private static Properties loadProperties() throws Exception
Exception
private static File extract_logDirectory(Properties properties) throws SecurityException, IllegalStateException
SecurityException
IllegalStateException
private static void init_nameToNodeProps(Properties properties) throws IllegalStateException, ClassNotFoundException
IllegalStateException
ClassNotFoundException
public static Level[] getLevels()
Level.intValue
).
Contract: the result is a clone of an internal field, so the caller may safely modify the result.
public static File getLogDirectory()
logDirectory
.
public static Logger2 getLogger2() throws IllegalStateException, SecurityException, RuntimeException
logger2_default
.
Lazy initializes it if necessary.
IllegalStateException
- if a Logger with the name "default" already exists
SecurityException
- if a security manager exists and if the caller does not have LoggingPermission("control")
RuntimeException
- (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwablepublic static File makeLogFile(String childPath) throws IllegalArgumentException, SecurityException, RuntimeException
log directory
.
The childPath arg is interpreted as a path relative to the log directory. It can be either a simple file name, or it can include one or more subdirtories before the file name. The only restriction in the latter case is that the canonical path that it resolves to must be inside the log directory (i.e. using ".." to move out of the log directory will be dtected and rejected).
IllegalArgumentException
- if childPath is blank
; childPath resolves to a path that falls outside the log directory
SecurityException
- if a security manager exists and denies read access to the file
RuntimeException
- (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwablepublic static PrintWriter makeLogWriter(String prefix) throws IllegalArgumentException, RuntimeException
makeLogWriter
(prefix, false)
(i.e. no auto flushing).
IllegalArgumentException
RuntimeException
public static PrintWriter makeLogWriter(String prefix, boolean autoFlush) throws IllegalArgumentException, RuntimeException
makeLogWriter
(logDirectory
, prefix, autoFlush)
(i.e. the default log directory is used).
IllegalArgumentException
RuntimeException
public static PrintWriter makeLogWriter(File directory, String prefix, boolean autoFlush) throws IllegalArgumentException, RuntimeException
The PrintWriter immediately writes to a BufferedWriter to ensure top performance, and that in turn writes to an OutputStreamWriter which uses the platform's default Charset.
The file's name starts with prefix, and ends with an underscore ('_') followed by a timestamp followed by the extension ".txt". So, the complete filename is typically unique each time that this method is called, which obviates the need for a file append mode (this method always overwrites the file if it already exists).
directory
- the parent directory of the file that will be written to by the resultprefix
- the prefix of the filename that the result will write toautoFlush
- specifies wheter or not automatic flushing is enabled in the result
IllegalArgumentException
- if prefix is blank
RuntimeException
- (or some subclass) if any other probolem occurspublic static Logger2 makeLogger2(Class c) throws IllegalArgumentException, IllegalStateException, SecurityException, RuntimeException
makeLogger2
(c, null) )
.
IllegalArgumentException
- if c == null
IllegalStateException
- if a Logger with getName(c, null) already exists
SecurityException
- if a security manager exists and if the caller does not have LoggingPermission("control")
RuntimeException
- (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwablepublic static Logger2 makeLogger2(Class c, String suffix) throws IllegalArgumentException, IllegalStateException, SecurityException, RuntimeException
makeLogger2
( getName
(c, suffix) )
.
IllegalArgumentException
- if c == null
IllegalStateException
- if a Logger with getName(c, suffix) already exists
SecurityException
- if a security manager exists and if the caller does not have LoggingPermission("control")
RuntimeException
- (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwablepublic static Logger2 makeLogger2(String name) throws IllegalArgumentException, IllegalStateException, SecurityException, RuntimeException
makeLogger2
( name, null )
.
IllegalArgumentException
- if name is blank
IllegalStateException
- if a Logger with name already exists
SecurityException
- if a security manager exists and if the caller does not have LoggingPermission("control")
RuntimeException
- (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwablepublic static Logger2 makeLogger2(String name, String resourceBundleName) throws IllegalArgumentException, IllegalStateException, SecurityException, RuntimeException
Logger2.getLogger2
(name, resourceBundleName)
.
So, only if this is the first Logger with name known by LogManager is it created.
The result is a Logger2
instance because of that subclass's advanced features.
If the Logger2 is created, then nsLevel
,
nsHandlers
, and nsUseParentHandlers
are called on it.
No other configuration is subsequently performed (e.g. for the Level or Formatter of Logger2's handlers; all are left with their default settings, such as whatever has been specified by some logging properties file).
This method only throws unchecked Exceptions so that it can be used to initialize a field at its declaration.
IllegalArgumentException
- if name is blank
IllegalStateException
- if a Logger with name already exists
SecurityException
- if a security manager exists and if the caller does not have LoggingPermission("control")
RuntimeException
- (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwablepublic static String getName(Class c, String suffix) throws IllegalArgumentException
Motivation: Loggers are often static fields of a class, so it is convenient to simply supply the Class object and have this method determine the name. The suffix arg allows optional further discrimination (e.g. a class may have multiple loggers for different kinds of events, or, if each instance has its own Logger, then each one's name should have some instance specific part).
IllegalArgumentException
- if c == nullpublic static String getPath(Class c, String suffix) throws IllegalArgumentException, SecurityException, IllegalStateException
getPath
( getName
(c, suffix) )
.
IllegalArgumentException
- if c == null
SecurityException
- if a security manager exists and its SecurityManager.checkWrite method does not permit directoryLogDefault and all necessary parent directories to be created
IllegalStateException
- if an attempt to create directoryLogDefault is made but failspublic static String getPath(String name) throws IllegalArgumentException, SecurityException, IllegalStateException
This is typically used with makeLogger2(Class, String)
.
The result is located in the directory returned by getLogDirectory
(see javadocs for possible side effects), and consists of name, a '_', a timestamp, and then ".log".
IllegalArgumentException
- if name is blank
SecurityException
- if a security manager exists and its SecurityManager.checkWrite method does not permit directoryLogDefault and all necessary parent directories to be created
IllegalStateException
- if an attempt to create directoryLogDefault is made but failspublic static void nsLevel(Logger logger) throws IllegalArgumentException, Exception
NodeProp.findBest
to get the NodeProp
whose associated key best matches logger's name and whose level field is non-null.
If a non-null result is found, then its level field is assigned to logger.
IllegalArgumentException
- if logger == null
Exception
- (or some subclass) if some other problem occurspublic static void nsHandlers(Logger logger) throws IllegalArgumentException, Exception
NodeProp.findBest
to get the NodeProp
whose associated key best matches logger's name and whose handlerClasses field is non-null.
If a non-null result is found, then its handlerClasses field is used to create new Handler instances that are added to logger.
Special case: if a FileHandler
Class is encountered,
its String arg constructor is called instead of its default constructor.
The param passed to that constructor is a call to getPath
( logger.getName() ).
IllegalArgumentException
- if logger == null
Exception
- (or some subclass) if some other problem occurspublic static void nsUseParentHandlers(Logger logger) throws IllegalArgumentException, Exception
NodeProp.findBest
to get the NodeProp
whose associated key best matches logger's name and whose useParentHandlers field is non-null.
If a non-null result is found, then its useParentHandlers field is assigned to logger.
If an appropriate match is found, then its useParentHandlers boolean value is assigned to logger.
IllegalArgumentException
- if logger == null
Exception
- (or some subclass) if some other problem occurspublic static Level parseLevel(String s) throws IllegalArgumentException
IllegalArgumentException
- if s is blank or no Level corresponds to spublic static void close(Logger logger)
Contract: this method should never throw a Throwable.
Any Throwable that is raised is caught and logged robustly
to the default Logger
.
public static void flush(Logger logger) throws IllegalArgumentException
Contract: this method should never throw a Throwable.
Any Throwable that is raised is caught and logged robustly
to the default Logger
.
IllegalArgumentException
public static void addHandlers(Handler[] handlers, Logger logger) throws IllegalArgumentException
IllegalArgumentException
- if handlers is null; logger is nullpublic static Handler[] removeHandlers(Logger logger) throws IllegalArgumentException
IllegalArgumentException
- if logger == null
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |