Append column to QuickFix/J Logs database (Custom QuickFix/J Logger) - java

QuickFix/J provides functionality to store its logs in database.
Is it possible to append another column (business ID) to one of its table in such a way that it does not cause problems in QuickFix/J's internal message logging?
If it is possible kindly mention the procedure to do it too.

The solution is to create your own Logger and LoggerFactory similar to the ones provided by QuickFix/J.
You can create a Logger by implementing the "quickfix.Log" interface, and a LoggerFactory by implementing the "quickfix.LogFactory" interface.
The easiest approach would be to use the private AbstractLog from QuickFix/J.
Creating the Log class:
Copy the AbstractLog class as it is from QuickFix/J's source and include it in your project.
Create a class which extends the AbstractLog class and implement all the abstract methods.
Create member variables for any extra field you want to append to the logs (e.g. business ID), and provide a constructor which takes is
as an argument and sets its value.
The "logIncoming" and "logOutgoing" methods take a String parameter. This is the data you want to log. At this point you can
append your own fields (added in point 3) to the logs. You can format
the log as you wish and you are free to use any method of output, i.e.
Console, database etc. as you will have to implement it yourself.
Creating the LoggerFactory:
Create a LoggerFactory that implements the quickfix.LogFactory interface.
In the "create" method, create and return the instance of the Logger you created before using the constructor you require.
The values that you need to be passed to the constructor can be kept as member variables of the LoggerFactory and set in
LoggerFactory's constructor.
You have a custom Logger now and can use it as QuickFix/J's own loggers are used, and QuickFix/J will automatically log using your logger.
ApplicationAdapter application = new FixInitiator();
SessionSettings settings = new SessionSettings("./config/initiator.cfg");
CustomLogFactory customLogFactory = new CustomLogFactory(settings, myCustomID);
DefaultMessageFactory messageFactory = new DefaultMessageFactory();
FileStoreFactory fileStoreFactory = new FileStoreFactory(settings);
socketInitiator = new SocketInitiator(application, fileStoreFactory, settings, customLogFactory, messageFactory);
socketInitiator.start();
Taking a look at QuickFix/J's own Logger and LoggerFactory implementations for help would be a good idea.
e.g. The Logger that Logs on Console: ScreenLog, ScreenLogFactory
QuickFix/J source:
https://github.com/quickfix-j/quickfixj

Related

How to get class name in GetLogger and print correct class name in log4j 2

I have created a wrapper for logging using log4j2. This is a customClass creating a object of :
logger = LogManager.getLogger(caller);
The problem is I am not getting correct class name in source when I am printing LogEvent.
For eg. If abc.java is logging log.info, I need to capture abc.java in source instead of customClass name.
You can't create the Logger just once in your wrapper object, because it is created with a specific class name (not the one you want).
So the quick and dirty way is to call LogManager.getLogger(caller).info(message) on every log call, and pass the calling class as the caller. Log4J keeps a hashtable of already created Loggers so it will not create a new Logger in case one already exists for the same caller class.
However, I don't think this is optimized enough for this use case, so I would probably roll my own caching in the wrapper object using a ConcurrentHashMap and computeIfAbsent( ), so that retrievals of existing Logger objects don't block others.
So my pattern would be:
Logger logger = loggersPerClass.get(callerClassName);
if (logger == null) {
logger = loggersPerClass.computeIfAbsent(callerClassName, k -> LogManager.getLogger(callerClass));
}

How to customize the default java logging?

I need to have custom log levels(other than java.util.logging.Level), and custom functions in the java util logger to log messages of these levels.
Custom levels:
1. DEBUG
2. ERROR
3. FATAL
So I created XYZLevel extended from Level as follows:
public class OKILevel extends Level {
public OKILevel(String name, int value) {
super(name, value);
}
/* OKI Log Levels */
public static final Level FATAL = new OKILevel("FATAL",
Level.SEVERE.intValue() + 100);
public static final Level ERROR = new OKILevel("ERROR",
Level.SEVERE.intValue());
public static final Level DEBUG = new OKILevel("DEBUG",
Level.FINE.intValue());
}
Custom functions required:
.debug(), .severe() etc similar to .info in logger.
For logger I do:
static final Logger logger = Logger.getLogger(ABC.class.getName());
I am unable to figure out:
static final XYZLogger logger = XYZLogger.getLogger(ABC.class.getName());
extending the Logger doesn't help.
Also, for this application the logging.properties need to be passed in.
How do I use these new levels in .level of logging.properties?
If you are creating log wrapper then you should leverage some of the standard frameworks before inventing something new.
If you want your application to use a new logger implementation then you have to implement and install a custom LogManager.
Also, for this application the logging.properties need to be passed in. How do I use these new levels in .level of logging.properties?
All of the .level parsing performed via the
Level.parse method. Per the docs:
The argument string may consist of either a level name or an integer value.
For example:
"SEVERE"
"1000"
So you should be able to write:
#Level.SEVERE + 100 = FATAL
some.logger.name.level = 1100
That said there are some known issues with finding the correct level value by int in some releases of Java.
Another option if you just want the rendered output of a level to be different then you should have a look at Best way to override java.util.logging Level class to change the level strings.

How to override java.util.logging.Logger to my private logger?

I have code fetched from jar that uses java.util.logging.Logger.
Jar contains about 1000 logger usages and each class start from:
private static final Logger LOG = Logger.getLogger(SomeClass.class.getName());
I want to handle all logs there, means, to point them to my Logger usage and not to java.util.logging.Logger.
Therefore I wrote my own logger.
So instead:
LOG.log(Level.SEVERE, "Error sleeping", e);
I can write:
MyLogger.toLog(TLogLevel.WFS_ERROR, "Monkey", "Error sleeping", e );
The problem is I need run over all java files and replace with mine.
Messy way, hmm
Does anyone know how can by easy way to convert java.util.logging.Logger to com.boo.MyLogger?
Thanks,
The SLF4J project has a jul-to-slf4j bridge that can be used to redirect java.util.logging.Logger calls to SLF4J. You could use that (by making your MyLogger implement the interface defined by SLF4J).
Note that, however, unlike all other logging libraries, j.u.l. is hard-wired into the Java class libraries and cannot be bridged without a performance penalty.
Also, I don't know what you are doing with MyLogger, but usually there is no need to write your own. There are plenty of logging implementations to choose from, and they can be configured in many different ways. And even if you do have to write your own Logger implementation, you should use an existing interface (such as SLF4J which seems to most popular these days).
Take a look at SLF4J:
The Simple Logging Facade for Java or (SLF4J) serves as a simple
facade or abstraction for various logging frameworks, e.g.
java.util.logging, log4j and logback, allowing the end user to plug in
the desired logging framework at deployment time.
Using that you could then also use logback (same author) to log to a common logging framework using the various bridges already available. Or, write your own, but either way you would not have to worry about replacing all that code...
Oracle's Java 7 Logger is configurable, its implementation is simply:
public static Logger getLogger(String name) {
// This method is intentionally not a wrapper around a call
// to getLogger(name, resourceBundleName). If it were then
// this sequence:
//
// getLogger("Foo", "resourceBundleForFoo");
// getLogger("Foo");
//
// would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null.
LogManager manager = LogManager.getLogManager();
return manager.demandLogger(name);
}
So you can also via code set a logging level; besides declarative.
LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.INFO);
Lars Vogel has a nice page, also with its own Logger class.
All put together is quite workable, but maybe sometimes somewhat hard to understand.

Custom Log Level

In my existing application "org.apache.log4j" API's have been used in java code.
Requirement :
I have to log some statement(say xyz) in log file in any case and should not dependent of log levels.For example : if my log level is error then also xyz should print, if my log level is debug then also xyz should print.
I cannot make log statement of xyz is debug because if i do this, other log statements apart from xyz will also start printing.
For this, I believe, I have to add some custom log level.Please help how to do it and how to set its level ordering so that in any case it should print.
Thanks in advance.
Best Regards
What you could do is create a different Logger for those statements (you are not restricted to use classes names when defining a logger)
// Standard logger
private static Logger log = Logger.getLogger(MyClass.class)
// XYZ logger
private static Logger logXYZ = Logger.getLogger("logs.xyz");
You can access the same logger from several class, you just have to pass the same label.
Then, in the configuration file, you can define a different log level for that category, and even output these logs in a different appender (different file, processing, etc.)
You could "hijack" the protected method Logger#forcedLog() to always print to the log.
You must place the hijacker class in the same package as Logger.
package org.apache.log4j;
/**
* #author maba, 2012-08-23
*/
public class LogOverride {
public static void print(Logger logger, String message) {
logger.forcedLog(logger.getName(), Priority.INFO, message, null);
}
}
And from your calling code
log.setLevel(Level.OFF); // Make sure logging is turned off
log.info("Normal logging"); // Will not be seen anywhere
LogOverride.print(log, "Overriding logger"); // Will still make it to your appender
This is what the log4j FAQ says about custom levels:
How do I add a custom level?
It is possible, but rarely appropriate. The request is commonly for a level named something like "audit" that doesn't obviously fit in the progression "trace", "debug", "info", "warn", "error" and "fatal". In that case, the request for a level is really a request for a mechanism to specify a different audience. The appropriate mechanism is to use a distinct logger name (or tree) for "audit" related messages.
So if you want to go with that suggestion then you should look at the answer from SJuan76.
If you do decide to go with the idea of creating a custom, you would need to create a subclass of Level to do this, because the Level constructor is protected.
/**
* Instantiate a Level object.
*/
protected Level(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
It looks like you should then chain to the Level constructor, passing it a suitable level value. Note that the larger the level number the higher the priority is. So for a Level that won't be blocked at any of the existing named levels, you want a value that is greater than Priority.FATAL_INT which is 50000.
(However, I'm not convinced that this is the right approach. For a start, you probably won't be able to refer to your custom level by name in a logging config file.)

How to configure the properties of a specific FileHandler

The Java logging configuration file lets me define the properties of a named logger, e.g.
name.heikoseeberger.heikotron.level = FINE
name.heikoseeberger.heikotron.handlers = java.util.logging.FileHandler
So far, so good. Now I would like to configure that particular FileHandler, e.g. with a specific output file. Unfortunately I only know how to configure the "global" FileHandler, which is already present in the configuration file:
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
I don't want to configure this one, but the instance which is associated with my custom Logger. I already tried the following, but without success:
name.heikoseeberger.heikotron.java.util.logging.FileHandler.pattern = %h/heikotron.log
name.heikoseeberger.heikotron.java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
Is it possible at all to set the properties of specific FileHandler instances? If yes, how should these be identified/named?
This is done by using the config option described in the top level class documentation of the LogManger. Create a public named class with a public constructor and invoke all of the java calls you need to make to configure your handler. Then in your logging properties direct the LogManager to load your class you created to configure your handler. Otherwise you can to subclass file handler which will create a custom namespace to configure.
I do not think it possible. If you review the source code for the FileHandler you will soon determine that it uses the string "java.util.logging.FileHandler.pattern" to determine the pattern of the file to use for logging purposes
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
limit = manager.getIntProperty(cname + ".limit", 0);
//...
}
As such, the configuration that you are putting in the file is not even been taken into account by the Handler.
It appears to me that handlers are unaware of the existence of any particular logger (i.e. name.heikoseeberger.heikotron), they just know how to publish a given LogRecord.
As far as I can see, the handlers of a particular logger are created by the LogManager, by reflectively invoking their default constructor, as such, when a given handler is being created, it is unaware of for which particular logger it has been requested, that is why all their properties are set through their own class names and not through the logger's name.
If I understand right you are trying to write in different log files using java.util.logging package. This can't be done out of the box without extending it.
If you can't switch to another logging framework like Logback, check answer to
java util logging.properties: How to log to two different files and see if it fits your needs.

Categories

Resources