I'm using logback in spring-boot 2.2.5.RELEASE, I need to get the log object in memory so I can manipulate the info and proccess it.
What I would expect is something like this.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component
public class application {
Logger logger = LoggerFactory.getLogger(application.class);
public void executeTask(Integer queryMinutes) {
logger.info(INICIO_TRANSACCION, metodo);
try {
//Do something
//Log informative messages
} catch (DBException e) {
//Log ERROR messages
logger.error(MENSAJE_EXCEPCION + e, e);
logger.info(ROLBACK_TRANSACCION);
} finally {
//Here I need to call a method to further process the info printed in the log something like
logger.getMessage();
logger.getLineNumber();
logger.getThread();
callSomeMethod(logger);
logger.info(TIEMPO_PROCESO, (System.currentTimeMillis() - tiempoInicial));
logger.info(FIN_TRANSACCION, metodo);
}
}
}
I know that when you work with appenders we usually define a ILoggingEvent object and this give access to logger.getMessage() and more,
The question is how to get the log object in my java class so I can access the properties.
The basic design of logback (and log4j) is that it is part of your program but is not visible to the calling code. Hence what you want to do is basically against the spirit of the framework you want to use, which I suggest you reconsider.
The logback term for the component that actually ensures that the destination receives a log event is an "appender". http://logback.qos.ch/manual/appenders.html
The problem is that you want to send a mail using a non-standard API, otherwise you could just have used the standard SMTPAppender (http://logback.qos.ch/manual/appenders.html#SMTPAppender)
I would suggest either subclassing the SMTPAppender to do what you want, or write a javax.mail.Session object that implements the JavaMail API and provide it to a stock SMTPAppender using JNDI. Or a third thing. Just not in your own part of the code.
Related
I am interested in using log4j for audit logging, in other words, I will open my own file and start logging anything I want. Therefore I do NOT want to use warn, info or any log level. Something like that:
Log log = new Log("blah.txt");
log.log("Test");
log.close();
How can I do that with LOG4J?
A concrete example that I can run will be very appreciated.
Thanks!
You could create your own level AUDIT and use that to log everything. Logging without a level might not be too easily achieved.
If you're already using log4j, you don't need to re-invent the wheel. You could just configure a FileAppender and only specify the package or class that should log to it, so nothing else does. You're not forced to use any specific or all logging levels, just stick to one, like debug or info. You can take it a step further and configure your FileAppender to only capture info or debug, whichever you choose.
Unless there's some value in not using what log4j already has built, I'd stick with what they have. Could you elaborate on your use case perhaps so we understand why you don't want to use the built in functionality?
It is highly recommended to keep logging configuration outside of your code. Today you think that you have simple logging logic, however tomorrow it can become more and more complex.
Anyway, if you want a quick start and one simple log method - you can build a simple wrapper over log4j API:
class Log {
private final Logger logger = Logger.getLogger(Log.class);
public Log(String fileName) throws IOException {
// print just message
Layout layout = new PatternLayout("%m");
// log to file
FileAppender appender = new FileAppender(layout, fileName, true);
logger.addAppender(appender);
logger.setLevel(Level.INFO);
}
public void log(String string) {
logger.info(string);
}
public static void main(String[] args) {
try {
Log log = new Log("test.log.txt");
log.log("Test message");
} catch (IOException e) {
e.printStackTrace();
}
}
}
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.
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.)
I have a question regarding logging in a Java application. I want to log the main user actions and possible errors with user friendly messages. Therefore, I have two appenders for the logger: one shows errors (level = error) in dialogs and the other writes the logs of the current user session into an html-file so that the user could send this file back if something goes wrong.
To avoid having the logger creation in every class (private Logger logger = …) I have a static reference of the configured logger in a class App which has also the methods for accessing the logger:
public class App {
private static Logger logger = Logger.getLogger("logger name");
…
public static void logError(String message, Throwable cause) {
…
}
public static void logInfo(String message) {
…
}
}
The logging is mainly done in the UI classes:
class UIWidget extends UIFrameworkWidget {
void aMethod() {
try {
someBusinessLogic();
} catch (Exception e) {
App.logError(“log message”, e);
}
}
}
Is this a good practice? (Note that the widgets are created by the framework.)
Thanks in advance for answers, comments, or hints on this
Better would be to use a single static Logger instance but allow each class to create a private instance which is initialized with the name of the class (and maybe other info). This private instance then uses the static instance to actually perform its logging but can be omitted if necessary and reduces references to external classes.
This is how slf4j does it, which is a logging framework you should consider using - however you could roll your own in the same manner.
Note, however I don't know how to get your error messages to be displayed within a dialog box - that may need to be explicitly added.
It seems you're just one step away of subclassing the JDK Logger. Having only one static instance prevents you from targeting specific classes at runtime. If you subclass the Logger, then you can still have has many loggers as logged classes and yet keep your appender's peculiarities.
You can also craft your own layout (and, in log4j at least, even add placeholders - geronimo has an example of this).
I have a log statement in which I always use this.getClass().getSimpleName()as the 1st parameter.
I would like to put this in some sort of macro constant and use that in all my log statements.
But I learned that Java has no such simple mechanism unlike say C++.
What is the best way to achieve this sort of functionality in Java?
My example log statements (from Android) is as follows..
Log.v(this.getClass().getSimpleName(),"Starting LocIden service...");
Java doesn't have macros but you can make your code much shorter:
Log.v(this, "Starting LocIden service...");
And in the Log class:
public void v(Object object, String s)
{
_v(object.getClass().getSimpleName(), s);
}
Another approach could be to inspect the call stack.
Karthik, most logging tools allow you to specify the format of the output and one of the parameters is the class name, which uses the method Mark mentioned (stack inspection)
For example, in log4j the parameter is %C to reference a class name.
Another approach is to follow what android suggests for its logging functionality.
Log.v(TAG, "Message");
where TAG is a private static final string in your class.
Use a proper logging framework (e.g. slf4j). Each class that logs has its own logger, so there's no need to pass the class name to the log method call.
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.debug("Starting service");
//...
logger.debug("Service started");