in the following code example the handler does not repect the call to setLevel.
Logger globalLogger = Logger.getLogger("");
Handler handler = new LogMailHandler();
handler.setLevel(Level.SEVERE);
globalLogger.addHandler(handler);
Logger local = Logger.getLogger(LogMailHandlerTest.class.getName());
local.severe("Test message severe...");
local.info("Test message info...");
LogMailHandler is defined as follows:
public class LogMailHandler extends Handler {
#Override
public void publish(LogRecord pRecord) {
System.out.println("Error registered..." + pRecord.getLevel().getName());
}
#Override
public void flush() {
}
#Override
public void close() throws SecurityException {
}
}
The output is:
Jan 19, 2015 5:20:33 PM com.idmedia.fts.exchange.helper.LogMailHandlerTest main
SEVERE: Test message severe...
Error registered...SEVERE
Jan 19, 2015 5:20:33 PM com.idmedia.fts.exchange.helper.LogMailHandlerTest main
INFO: Test message info...
Error registered...INFO
In my opinion the "Error registered...INFO" should not be there since the level of the handler was set to SEVERE.
Any suggestions?
You must test the LogRecord on publish method with boolean isLoggable(LogRecord) before printing on console. This method tests, among other things, if the LogRecord level is higher that the minimum (in your case SEVERE)
Related
The task of job is simple, just output the time in console. I need a button for excuting the logic immediately.
XxxService.class
#Override
#Transactional(rollbackFor = Exception.class)
public void run(Long[] jobIds) {
for(Long jobId : jobIds){
ScheduleUtils. run(scheduler, this.getById(jobId));
}
}
ScheduleUtils.class
public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
try {
JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);
System.out.println(Thread.currentThread()+"need hello..."+new Date());
scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
} catch (SchedulerException e) {
throw new RRException("run fail", e);
}
}
MyJob:
#Data
#Component("HelloJob")
public class HelloJob extends CustomJob {
public String jobDescription = "演示Job";
#Override
public void run(String params) {
System.out.println(
Thread.currentThread() +"hello..."+new Date() + "params: " + params);
}
}
When I add #Transactional on the run(Long[] jobIds), the job does not run immediately and often run delay 15~20 seconds.
Thread[http-nio-8500-exec-1,5,main]need hello...Mon Jun 27 12:55:02 CST 2022
2022-06-27 12:55:24 INFO [bear] QuartzScheduler_BearScheduler-DESKTOP-521BOOH1656305645549_MisfireHandler org.springframework.scheduling.quartz.LocalDataSourceJobStore Handling 1 trigger(s) that missed their scheduled fire-time.
Thread[BearScheduler_Worker-3,5,main]hello...Mon Jun 27 12:55:24 CST 2022params: aaaa,bbbbbbb
When I delete #Transactional on the run(Long[] jobIds), the job will run immediately.
hread[http-nio-8500-exec-4,5,main]need hello...Mon Jun 27 12:59:40 CST 2022
Thread[BearScheduler_Worker-6,5,main]hello...Mon Jun 27 12:59:40 CST 2022params: aaaa,bbbbbbb
Why my job is handled by MisfireHandler? Why the job can not run immediately when #Transactional on the method? If you know the reason,please help me. Thanks
I made a custom logger for my project by using java.util.logging:
public class SpotifyLogger {
private static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
Handler[] handlers = myLogger.getHandlers();
if (handlers[0] instanceof ConsoleHandler) { //exception occurs here
myLogger.removeHandler(handlers[0]);
}
// set level
LOGGER.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
LOGGER.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
LOGGER.log(Level.SEVERE, user, e);
}
}
For the client and the server parts of my program, I create two separate Logger objects:
// class member initialized as null, because of exception handling
private SpotifyLogger logger = null;
//...
//in constructor:
this.logger = new SpotifyLogger(LOGGER_FILE_NAME); // the LOGGER_FILE_NAME is different for the client and the server
When I test my program manually, the loggers seem to work (the two log files contain exceptions that I have caused). Then, I wrote automatic tests. For each class that I am testing (a total of 5), I create a separate logger object with a different destination path. The tests (for whichever class comes first) work correctly. All other tests fail because I get an ArrayIndexOutOfBoundsException, when I initialize the logger for that particular class. The reason is that I am trying to access handlers[0], when handlers has 0 length. From what I understood after searching the web, this is because the logger is using parent handlers. I tried this:
public SpotifyLogger(String loggerFilePath) throws IOException {
Logger myLogger = Logger.getLogger("");
// suppress console messaging
myLogger.setUseParentHandlers(false);
Handler[] handlers = myLogger.getHandlers();
if (handlers.length > 0) {
if (handlers[0] instanceof ConsoleHandler) {
myLogger.removeHandler(handlers[0]);
}
}
//etc
}
I don't get an exception anymore but the logging doesn't work. What am I doing wrong?
If you want different Loggers, you need to supply different names for each. Hence this line of your code (in SpotifyLogger constructor) always returns the same Logger.
Logger myLogger = Logger.getLogger("");
This actually returns the java.util.logging.LogManager.RootLogger which has a single Handler which is an instance of ConsoleLogger. You subsequently remove that Handler in the first invocation of SpotifyLogger constructor, hence in every subsequent invocation, method getHandlers returns an empty array.
Since you only ever add Handlers to the global Logger, another FileHandler is added to the global logger every time SpotifyLogger constructor is called. I have not verified but I believe that a Logger will use the first, appropriate Handler in the array returned by method getHandlers, hence the behavior you are seeing whereby only the first log file is being written to, i.e. the file that you passed to the first invocation of SpotifyLogger constructor.
Note that you have not provided a reproducible example so I cannot verify any of the above with regard to your context. I only tested the code in your question in order to arrive at the above.
Consider the following rewrite of class SpotifyLogger – including a main method for testing purposes only.
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class SpotifyLogger {
private static final String[] NAMES = {"First", "Second"};
private static int count;
private int index;
public SpotifyLogger(String loggerFilePath) throws IOException {
index = count;
Logger myLogger = Logger.getLogger(NAMES[count++]);
myLogger.setUseParentHandlers(false);
// set level
myLogger.setLevel(Level.SEVERE);
// create a txt handler
FileHandler textFileHandler = new FileHandler(loggerFilePath);
SimpleFormatter simpleFormatter = new SimpleFormatter();
textFileHandler.setFormatter(simpleFormatter);
myLogger.addHandler(textFileHandler);
}
public void log(String user, Exception e) {
Logger myLogger = Logger.getLogger(NAMES[index]);
myLogger.log(Level.SEVERE, user, e);
}
public static void main(String[] args) {
try {
SpotifyLogger x = new SpotifyLogger("spotifyx.log");
SpotifyLogger y = new SpotifyLogger("spotifyy.log");
x.log("George", new Exception());
y.log("Martha", new RuntimeException());
}
catch (IOException x) {
x.printStackTrace();
}
}
}
Note that you are correct regarding parent Handlers, hence the following line in the above code:
myLogger.setUseParentHandlers(false);
After running the above code, the contents of file spotifyx.log is:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: George
java.lang.Exception
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:38)
And the contents of file spotifyy.log is:
Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: Martha
java.lang.RuntimeException
at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:39)
And no log messages are written to the console.
This doesn't make much sense to me
Logger myLogger = Logger.getLogger("")
Reference: Oracle java docs
I want to change the format of the log but after I overrided the formatter, it still outputs in the old way.
private void initializeLogger() {
logger = Logger.getLogger("gameLogger");
try {
Handler handler = new FileHandler("/home/bobby/IdeaProjects/GoFishBobby/src/logs/log1.txt");
MyFormatter myFormatter = new MyFormatter();
handler.setFormatter(myFormatter);
logger.addHandler(handler);
for (Handler parentHandler : logger.getParent().getHandlers())
logger.removeHandler(parentHandler);
}catch(IOException e){
System.out.println("IO exception");
}
logger.info("Game starting : ");
}
I overrided the default loggerFormatter
public class MyFormatter extends Formatter{
public String format(LogRecord record){
return record.getMessage();
}
}
and the output still contains the info line with the information I dont need
SEVERE: NativePlayer : 0 Scored A Book of rank 3
Mar 05, 2017 1:15:44 PM Game logAfterPlay
INFO: The transfer hand is:
4D 4H 4S
Edit
I just found out the log in the file is correct, but why its still like this on the console? how do I get rid of it on the console. I already removed all the parent logger.
You can locate all handlers by using the following method to walk all currently created loggers.
public static void printLoggerTree() {
final LogManager manager = LogManager.getLogManager();
synchronized (manager) {
final Enumeration<String> e = manager.getLoggerNames();
while (e.hasMoreElements()) {
final Logger l = manager.getLogger(e.nextElement());
if (l != null) {
for (Handler h : l.getHandlers()) {
System.out.println(l.getName() + "=" + h);
}
}
}
}
}
The logger tree changes over time when different parts of your code requests new loggers. So it matters when you call this method.
A better practice is to setup your own logging.properties file and configure your project to use that on startup. You also don't need to create your own formatter as you can change the format pattern of the SimpleFormater on the command line by using the following:
-Djava.util.logging.SimpleFormatter.format="%5$s%n"
I implemented two customized handler to log information on DB and an additional flat file (DBHandler and MyFileHandler). This new log handlers will be used by a single class on a specific package.
I attached the two new loggers to a specific package only.
The idea is to switch between this two handlers (file and database)for the classes contaiend on a specific package, but currently with the current configuration I could not do that, so either I am logging with both handler either there is no log at all.
I tried to set the log level for DB handler to off but it is still logging normally on DB.
below the configuration file is use logging.properties
############################################################
##### Global properties
############################################################
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler, com.test.logging.DBHandler, com.test.logging.MyFileHandler
.level = INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
java.util.logging.FileHandler.level = ALL
java.util.logging.FileHandler.pattern = %t/CLog%g.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
com.test.logging.MyFileHandler.level = ALL
com.test.logging.MyFileHandler.pattern = %t/custLog%g.log
com.test.logging.MyFileHandler.limit = 50000
com.test.logging.MyFileHandler.count = 1
com.test.logging.MyFileHandler.formatter = java.util.logging.SimpleFormatter
com.test.logging.DBHandler.level=OFF
com.test.ccb.mon.handlers=com.test.logging.DBHandler, com.test.logging.MyFileHandler
The class using the logger to track he inforamtion is below
package com.test.ccb.mon;
public class Utils {
public static final Logger logger = Logger.getLogger(Utils.class.getCanonicalName());
public void logging()
{
//processing
logger.info("message);
}
}
DBHandler class:
public class DBHandler extends Handler {
#Override
public void close() throws SecurityException {
}
#Override
public void flush() {
}
#Override
public void publish(LogRecord logRecord) {
if (isLoggable(logRecord))
{
try {
//SQL call to insert onDB
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
MyFileHandler class:
public class MyFileHandler extends FileHandler{
public MyileHandler() throws IOException, SecurityException {
super();
}
#Override
public void close() throws SecurityException {
super.close();
}
#Override
public void flush() {
super.flush();
}
#Override
public void publish(LogRecord record) {
super.publish(record);
}
}
The Handler class doesn't read any properties from the LogManager by default. You have to code that logic in all of your subclasses.
public class DBHandler extends Handler {
public DBHandler() {
LogManager m = LogManager.getLogManager();
String p = getClass().getName();
String v = m.getProperty(p + ".level");
try {
if (v != null) {
super.setLevel(Level.parse(v));
}
} catch (RuntimeException re) {
reportError(v, re, ErrorManager.OPEN_FAILURE);
}
//#todo create code to parse filter, formatter, encoding, etc.
}
#Override
public void close() throws SecurityException {
}
#Override
public void flush() {
}
#Override
public void publish(LogRecord logRecord) {
if (isLoggable(logRecord)) {
try {
//SQL call to insert onDB
} catch (Exception e) {
reportError("", e, ErrorManager.WRITE_FAILURE);
}
}
}
}
Reproducing your problem is not so easy for me. With handler classes similar to yours, changes to the configuration file have the expected effect. With the DBHandler.level=OFF setting, the database handler output is missing for me:
Aug 11, 2015 1:47:26 PM com.test.ccb.mon.Utils logging
DBHandler.publish - handler level: OFF; log record level: INFO
INFO: message
MyFileHandler - message
Logging handlers:
###java.util.logging.FileHandler-ALL
###java.util.logging.ConsoleHandler-ALL
###com.test.logging.DBHandler-OFF
###com.test.logging.MyFileHandler-ALL
Your debug code to print the logging handlers is now also included in the following main method to your Utils class. You could run this method yourself, to see whether this way of reading the configuration file works better for you:
public static void main(final String[] arguments) throws IOException
{
final String fileName = "logging.properties";
final InputStream propertiesStream = Utils.class.getResourceAsStream(fileName);
//final InputStream propertiesStream = new FileInputStream("path to file");
LogManager.getLogManager().readConfiguration(propertiesStream);
new Utils().logging();
System.out.println();
// No handlers for this logger directly, but four for its parent.
System.out.println("Logging handlers:");
for (final Handler handler : logger.getParent().getHandlers())
System.out.println("###" + handler.getClass().getName()
+ "-" + handler.getLevel());
}
A very simple version of your DBHandler class could look like this (please note the if (isLoggable(record)) check in the publish method):
package com.test.logging;
import java.util.logging.*;
/**
* Logging handler that stores logging in the database.
*/
public class DBHandler extends Handler {
#Override
public void publish(final LogRecord record) {
System.out.println("DBHandler.publish - handler level: " + getLevel()
+ "; log record level: " + record.getLevel());
if (isLoggable(record))
System.out.println(getClass().getSimpleName() + " - " + record.getMessage());
}
#Override
public void flush() {
// Empty.
}
#Override
public void close() throws SecurityException {
// Empty.
}
}
There is a interface DispatcherListener in Struts2. The docs says
"A interface to tag those that want to execute code on the init and
destroy of a Dispatcher."
But how to use this interface. If I create a class that implements this interface, how should I configure it to Struts2?
When a Dispatcher is instantiated, it could send to the listener notification when it's initialized or destroyed. The reference and code samples are from here.
The simple usage is to instantiate a bean by the container via bean tag and add themselves in the init method and remove themselves when destroyed like it did by the ClasspathConfigurationProvider.
The code is raw just for to show you the idea
struts.xml:
<bean type="com.opensymphony.xwork2.config.PackageProvider" name="myBean" class="jspbean.struts.MyBean" />
MyBean.java:
public class MyBean implements ConfigurationProvider, DispatcherListener {
public MyBean() {
System.out.println("!!! MyBean !!!");
}
#Override
public void dispatcherInitialized(Dispatcher du) {
System.out.println("!!! dispatcherInitialized !!!");
}
#Override
public void dispatcherDestroyed(Dispatcher du) {
System.out.println("!!! dispatcherDestroyed !!!");
}
#Override
public void destroy() {
System.out.println("!!! destroy !!!");
Dispatcher.removeDispatcherListener(this);
}
#Override
public void init(Configuration configuration) throws ConfigurationException {
System.out.println("!!! init !!!");
Dispatcher.addDispatcherListener(this);
}
#Override
public boolean needsReload() {
return false;
}
#Override
public void loadPackages() throws ConfigurationException {
}
#Override
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
}
}
The output:
15:27:50 INFO (org.apache.struts2.spring.StrutsSpringObjectFactory:42) - ... initialized Struts-Spring integration successfully
!!! MyBean !!!
!!! init !!!
jul 18, 2013 3:27:51 PM org.apache.catalina.startup.HostConfig deployDirectory
!!! dispatcherInitialized !!!
[2013-07-18 06:28:11,102] Artifact jspbean:war exploded: Artifact is deployed successfully
INFO: A valid shutdown command was received via the shutdown port. Stopping the Server instance.
INFO: Stopping service Catalina
!!! dispatcherDestroyed !!!
If you are using Spring then you can create bean of your listener and in constructor add it to dispatcherListeners list.
public YourDispatcherListener () {
Dispatcher.addDispatcherListener(this);
}
Another solution is to create ServletContextListener which creates your dispatcher listener and adds it to list.