First of all, the basic usage. The main appearances are the following three.
First, create a Logger instance.
Logger logger = Logger.getLogger("logger name")
Next, create a Handler instance and register it with logger. You can register multiple Handlers.
This time, create a Handler with C: \ sample \ sample.log
as the output destination. If you want to output to a file, use the Handler
interface implementation class FileHandler
.
Handler handler = new FileHandler("C:\\sample\\sample.log");
logger.addHandler(handler);
Finally, create a Formatter instance and register it with Handler.
This time, we will use SimpleFormatter
to format it in a human-readable format. SimpleFormatter
is an implementation class of the Formatter
interface.
Formatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
Now you are ready to go. Pass the message to logger and output the log.
logger.log(Level.INFO, "message");
As mentioned above, when outputting the log, specify the log level of the message. The following 7 types of log levels are prepared by java.util.logging. The lower you go, the more serious the log becomes.
--FINE ST-- Very detailed trace message --FINER --A fairly detailed trace message --FINE --Detailed trace message --CONFIG --Static configuration message --INFO --Information message --WARNING --Warning message --SEVERE --Critical message
You can set the logger to output only messages above a certain log level. For example, if you want to output only messages of INFO level or higher, set as follows.
logger.setLevel(Level.INFO);
The sample program of basic usage is described below.
BasicLoggingSample
package sample;
import java.io.IOException;
import java.util.function.Supplier;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class BasicLoggingSample {
public static void main(String[] arg) throws SecurityException, IOException {
//Get the logger and set the log level to INFO
Logger logger = Logger.getLogger(BasicLoggingSample.class.getName());
logger.setLevel(Level.INFO);
//Create a handler and register it with the logger
Handler handler = new FileHandler("C:\\sample\\sample.log");
logger.addHandler(handler);
//Create a formatter and register it as a handler
Formatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
//Output INFO message
logger.log(Level.INFO, "INFO message");
//There is a simple method to output each log level message.
logger.finest("FINEST message");
logger.finer("FINER message");
logger.fine("FINE message");
logger.config("CONFIG message");
logger.info("INFO message");
logger.warning("WARNING message");
logger.severe("SEVERE message");
//In addition to the method of passing the message as a character string, Supplier<String>There is also a way to pass.
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return "Supply message";
}
};
logger.info(supplier);
//The log output method when an Exception occurs is as follows. The Throwable stack trace passed as an argument is output.
logger.log(Level.WARNING, "An error has occurred.", new RuntimeException("Runtime error"));
}
}
Loggers have a hierarchical structure based on namespaces. For example, if you create the following logger,
Logger logger = Logger.getLogger("com.sample.App");
The hierarchical structure is as follows. The parent logger for com.sample.App
is com.sample
, and the parent logger for com.sample
is com
. In addition, the parent logger for com
will be the root logger.
Root
└─com
└─sample
└─App
If the logger prints a message, the output is propagated to the parent logger as well. In other words, in the above example, if you output a message to the com.sample.App
logger, the message will also be output from com.sample
, com
, and the root logger, respectively.
I don't think it's easy to explain in words, so let's take a look at the sample program.
NameSpaceSample
package sample;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class NameSpaceSample {
public static void main(String[] arg) throws SecurityException, IOException {
//Get each logger. To get the root logger, specify an empty string in the logger name.
Logger root = Logger.getLogger("");
Logger com = Logger.getLogger("com");
Logger sample = Logger.getLogger("com.sample");
Logger app = Logger.getLogger("com.sample.App");
// com.sample.App parents are com.sample、com.The parent of sample is com, and the parent of com is the root logger.
System.out.println(app.getParent() == sample);
System.out.println(app.getParent().getParent() == com);
System.out.println(app.getParent().getParent().getParent() == root);
//The parent of the root logger is null.
System.out.println(root.getParent());
//Set up a handler for each logger.
Formatter formatter = new SimpleFormatter();
Handler rootHandler = new FileHandler("C:\\sample\\root.log");
root.addHandler(rootHandler);
rootHandler.setFormatter(formatter);
Handler comHandler = new FileHandler("C:\\sample\\com.log");
com.addHandler(comHandler);
comHandler.setFormatter(formatter);
Handler sampleHandler = new FileHandler("C:\\sample\\sample.log");
sample.addHandler(sampleHandler);
sampleHandler.setFormatter(formatter);
Handler appHandler = new FileHandler("C:\\sample\\App.log");
app.addHandler(appHandler);
appHandler.setFormatter(formatter);
//When you output a message with the app, a message is also output from each parent logger.
app.info("INFO message");
}
}
When you run the sample program, the logs will be output to root.log
, com.log
, sample.log
, and ʻApp.log under the
C: \ sample` directory.
I introduced how to set the log level in the logger and control it so that only messages above a certain log level are output, but if you want more detailed control, use the Filter
class.
Filter filter = new Filter() {
@Override
public boolean isLoggable(LogRecord record) {
return record.getMessage().contains("Number of cases processed=");
}
};
logger.setFilter(filter);
If Filter is set as above, only logs that include " number of processes = "
in the message will be output.
The above explanation showed how to set a log level filter on the logger.
In fact, log levels and filters can also be set in Handler
.
The sample program is described below.
FiltringSample
package sample;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Filter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class FiltringSample {
public static void main(String[] arg) throws SecurityException, IOException {
//Create a logger and set handlers and formatters
Logger logger = Logger.getLogger("sample1");
Handler handler = new FileHandler("C:\\sample\\sample.log");
logger.addHandler(handler);
handler.setFormatter(new SimpleFormatter());
//Set logger log level to INFO
logger.setLevel(Level.INFO);
logger.info("Sample message"); //Output
//Set a filter on the logger
Filter filter1 = new Filter() {
@Override
public boolean isLoggable(LogRecord record) {
return record.getMessage().contains("Number of cases processed=");
}
};
logger.setFilter(filter1);
logger.info("Sample message"); //Not output
logger.info("Sample message,Number of cases processed=1"); //Output
//Set a filter on the handler
Filter filter2 = (record) -> record.getMessage().contains("Number of updates=");
handler.setFilter(filter2);
logger.info("Sample message"); //Not output
logger.info("Sample message,Number of cases processed=1"); //Not output
logger.info("Sample message,Number of cases processed=1,Number of updates=1"); //Output
//Set log level in handler
handler.setLevel(Level.WARNING);
logger.info("Sample message"); //Not output
logger.info("Sample message,Number of cases processed=1"); //Not output
logger.info("Sample message,Number of cases processed=1,Number of updates=1"); //Not output
logger.warning("Warning message,Number of cases processed=1,Number of updates=1"); //Output
}
}
If you execute the above, you can see that the log output is limited by the log level and filter set for the logger and handler.
We have introduced above that the logger has a hierarchical structure, and that when a message is output to the logger, it propagates to the parent logger. Actually, there is a trap in this propagation mechanism, and if you do not understand the mechanism properly, you may get stuck in a pot. (I fit in ...) It may be difficult to understand even if you explain it in words, but once you explain it in words, the specifications for propagation to the parent logger are as follows.
--The log level filter set for the logger is checked only by the directly called logger. --The log level filter check set in the handler is checked by all handlers including the handler of the parent logger.
Yes, I think it's hard to understand. I think you can understand this faster by looking directly at the logic. Roughly speaking, the log output logic is as shown in the figure below.
Did you understand? The log level filter set on the logger is checked only on the first logger that receives the call, whereas it is checked. The log level filter set in the handler is checked by all handlers.
Two samples to confirm this are described below.
NameSpaceSample2
package sample;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class NameSpaceSample2 {
public static void main(String[] arg) throws SecurityException, IOException {
//Get 3 loggers. Set the handler and formatter.
Logger root = Logger.getLogger("");
Logger sample = Logger.getLogger("sample");
Logger loggerTreeSample = Logger.getLogger("sample.NameSpaceSample2");
Handler rootHandler = new FileHandler("C:\\sample\\root.log");
Handler sampleHandler = new FileHandler("C:\\sample\\sample.log");
Handler NameSpaceSample2Handler = new FileHandler("C:\\sample\\NameSpaceSample2.log");
Formatter formatter = new SimpleFormatter();
rootHandler.setFormatter(formatter);
sampleHandler.setFormatter(formatter);
NameSpaceSample2Handler.setFormatter(formatter);
root.addHandler(rootHandler);
sample.addHandler(sampleHandler);
loggerTreeSample.addHandler(NameSpaceSample2Handler);
//Set the log level to the logger.
root.setLevel(Level.WARNING);
sample.setLevel(Level.INFO);
loggerTreeSample.setLevel(Level.FINE);
//The handler is set to allow all log levels. (Level.Setting ALL allows the output of all log-level messages. )
rootHandler.setLevel(Level.ALL);
sampleHandler.setLevel(Level.ALL);
NameSpaceSample2Handler.setLevel(Level.ALL);
//Output log
root.fine("Call from root.FINE message");
root.info("Call from root.INFO message");
root.warning("Call from root.WARNING message");
sample.fine("Send from sample.FINE message");
sample.info("Send from sample.INFO message");
sample.warning("Send from sample.WARNING message");
loggerTreeSample.fine("Called from NameSpaceSample2Handler.FINE message");
loggerTreeSample.info("Called from NameSpaceSample2Handler.INFO message");
loggerTreeSample.warning("Called from NameSpaceSample2Handler.WARNING message");
}
}
When the above is executed, the message output by loggerTreeSample.fine (...)
is C: \\ sample \\ root even though it is set as
root.setLevel (Level.WARNING); . You can see that it is output to .log
.
Here is another sample. The only difference from the sample above is the log level setting.
NameSpaceSample3
package sample;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class NameSpaceSample3 {
public static void main(String[] arg) throws SecurityException, IOException {
//Get 3 loggers. Set the handler and formatter.
Logger root = Logger.getLogger("");
Logger sample = Logger.getLogger("sample");
Logger loggerTreeSample = Logger.getLogger("sample.NameSpaceSample2");
Handler rootHandler = new FileHandler("C:\\sample\\root.log");
Handler sampleHandler = new FileHandler("C:\\sample\\sample.log");
Handler NameSpaceSample3Handler = new FileHandler("C:\\sample\\NameSpaceSample3.log");
Formatter formatter = new SimpleFormatter();
rootHandler.setFormatter(formatter);
sampleHandler.setFormatter(formatter);
NameSpaceSample3Handler.setFormatter(formatter);
root.addHandler(rootHandler);
sample.addHandler(sampleHandler);
loggerTreeSample.addHandler(NameSpaceSample3Handler);
//The logger is set to allow all log levels. (Level.Setting ALL allows the output of all log-level messages. )
root.setLevel(Level.ALL);
sample.setLevel(Level.ALL);
loggerTreeSample.setLevel(Level.ALL);
//Set the log level in the handler.
rootHandler.setLevel(Level.WARNING);
sampleHandler.setLevel(Level.INFO);
NameSpaceSample3Handler.setLevel(Level.FINE);
//Output log
root.fine("Call from root.FINE message");
root.info("Call from root.INFO message");
root.warning("Call from root.WARNING message");
sample.fine("Send from sample.FINE message");
sample.info("Send from sample.INFO message");
sample.warning("Send from sample.WARNING message");
loggerTreeSample.fine("Called from NameSpaceSample3Handler.FINE message");
loggerTreeSample.info("Called from NameSpaceSample3Handler.INFO message");
loggerTreeSample.warning("Called from NameSpaceSample3Handler.WARNING message");
}
}
If you execute the above, you can see that only WARNING level logs are output to C: \\ sample \\ root.log
.
As you may have seen in the sample programs so far, writing a lot of code to set up loggers and handlers can be tedious. After all, I want to push the setting part out of the program and write it in the setting file. Therefore, the next method is to read the configuration file from the outside.
The method to read the configuration file from the program is as follows. This time, I tried to read logging.properties
located in the same classpath of the sample program.
The configuration file should be read in the java.util.Properties
format.
LogManager.getLogManager().readConfiguration(SettingFileSample.class.getResourceAsStream("logging.properties"));
The sample configuration file is as follows. Please refer to {java.home} \ lib \ logging.properties
for a detailed explanation.
logging.properties
#Root logger handler log level setting ConsoleHandler has a standard error(System.err)Output the log to.
handlers=java.util.logging.ConsoleHandler
.level=INFO
#Logger used in the sample program"sample.SettingFileSample"settings of
sample.SettingFileSample.level=FINE
sample.SettingFileSample.handlers=java.util.logging.FileHandler
#ConsoleHandler settings
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
#FileHandler settings
java.util.logging.FileHandler.pattern=C:/sample/sample.log
java.util.logging.FileHandler.limit=1000
java.util.logging.FileHandler.count=1
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
The sample program using the above configuration file is described below.
SettingFileSample
package sample;
import java.io.IOException;
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class SettingFileSample {
public static void main(String[] arg) throws SecurityException, IOException {
LogManager.getLogManager().readConfiguration(SettingFileSample.class.getResourceAsStream("logging.properties"));
Logger logger = Logger.getLogger("sample.SettingFileSample");
logger.finer("FINER message");
logger.fine("FINE message");
logger.info("INFO message");
}
}
It's neat because there are no logger or handler settings.
java.util.logging can also support internationalization using ResourceBundle.
The sample program is described below. The sample uses resource_ja.properties
and resource_en.properties
in the same classpath as the Java class.
ResourceBundleSample
package sample;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Logger;
public class ResourceBundleSample {
public static void main(String[] arg) {
Logger lggr = Logger.getLogger(ResourceBundleSample.class.getName());
lggr.setResourceBundle(ResourceBundle.getBundle("sample.resource", Locale.JAPANESE));
lggr.info("ID1");
lggr.setResourceBundle(ResourceBundle.getBundle("sample.resource", Locale.ENGLISH));
lggr.info("ID1");
}
}
resource_ja.properties
ID1=Japanese message
resource_en.properties
ID1=english message
When the sample program is executed, the first log is output as Japanese message
and the second log is output as ʻenglish message`.
If you enable Security Manager, you will not be able to programmatically change the logger settings. When the following sample program is executed with Security Manager enabled, ʻAccessControlException` is executed.
LoggingPermissionSample
package sample;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingPermissionSample {
public static void main(String[] arg) {
Logger lg = Logger.getLogger("");
lg.setLevel(Level.FINE);
lg.fine("fine message");
lg.info("info message");
}
}
To allow logger changes when Security Manager is enabled, write the following in the policy file: For details, refer to the Javadoc of LoggerPermission
.
java.policy
grant {
permission java.util.logging.LoggingPermission "control", "";
}
To enable the security manager, specify java.security.manager
in the VM argument as shown below.
To specify the policy file explicitly, specify java.security.policy
in the VM argument as shown below.
java -Djava.security.manager -Djava.security.policy=policy file path sample.LoggingPermissionSample
Recommended Posts