Using log4j to send email reports via the SMTPAppender - java

I'm trying to use log4j to send emailable reports that contain the logging statements from a background process. I want one email sent for each process run, not one email for each logging statement. I've looked at the SMTPAppender, but don't see a way to manually send the report when the process completes. I believe the TriggeringEventEvaluator may be the key, but one issue I'm running into is how to get a handle to the TriggeringEventEvaluator instance. I'm stuck using log4j 1.2.14 and the SMTPAppender.getEvaluator() method was introduced in 1.2.15. Any thoughts? Am I even on the right track? Does the SMTPAppender.close() method come into play here?
I want to be able to do this:
log.info(message1);
log.info(message2);
log.info(message3);
log.sendMail();
After thinking about this some more, I think I need to clarify what I'm hoping to accomplish. I'm trying to capture the logging from running a quartz job and send the resulting log as an email. The quartz job makes a bunch of service method calls into various services. I want the to include any logging those service methods perform as well as the logging of the quartz jobs itself. I was thinking I could do something like the following for capturing all the logging, but it isn't working.
// at the beginning of quartz job
Logger logger = Logger.getRootLogger();
StringWriter sw = new StringWriter();
WriterAppender wa = new WriterAppender(new SimpleLayout(), sw);
logger.addAppender(wa);
// at the end of the quartz job
String report = sw.toString();

You shouldn't use any of log4j's methods, you should configure it properly instead.
First of all, define in your log4j.properties file your appender properly:
#CONFIGURE SMTP
log4j.appender.email=org.apache.log4j.net.SMTPAppender
log4j.appender.email.SMTPHost=mail.mydomain.com
log4j.appender.email.SMTPUsername=myuser#mydomain.com
log4j.appender.email.SMTPPassword=mypw
log4j.appender.email.From=myuser#mydomain.com
log4j.appender.email.To=myuser#mydomain.com
log4j.appender.email.Subject=Log of messages
log4j.appender.email.BufferSize=1
log4j.appender.email.EvaluatorClass=TriggerLogEvent
log4j.appender.email.layout=org.apache.log4j.PatternLayout
log4j.appender.email.layout.ConversionPattern=%m
Note: code taken from this post. More information can be obtained in SMTPAppender API.
Next, make a special class that will be used just for sending email. Example:
package com.foo.mailer;
import org.apache.log4j.Logger;
public class Mailer {
private static final Logger logger = Logger.getLogger(Mailer.class);
public void logMail(String mailString) {
logger.info(mailString);
}
}
Next, put in log4j.properties configuration for this class:
# INFO level will be logged
log4j.logger.com.foo.mailer = INFO, email
# turn off additivity
log4j.additivity.com.foo.mailer = false
Now, whenever you want to send an email using log4j, put this in your code:
new Mailer().logMail("This mail should be sent");
Disclaimer: I haven't tested any of this code.

If you are using an XML configuration file, the following should be helpful.
<appender name="ErrorEmailAppender" class="org.apache.log4j.net.SMTPAppender">
<param name="SMTPHost" value="mail.mydomain.com" />
<param name="SMTPUsername" value="myuser#mydomain.com" />
<param name="SMTPPassword" value="password" />
<param name="From" value="myuser#mydomain.com" />
<param name="To" value="myuser#mydomain.com" />
<param name="Subject" value="Log of messages" />
<param name="BufferSize" value="1" />
<param name="EvaluatorClass" value="TriggerLogEvent" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%t %m%n"/>
</layout>
</appender>
<logger name="com.foo.mailer">
<level value="INFO" />
<appender-ref ref="ErrorEmailAppender"/>
</logger>

Log4j 2.x onwards, the following configuration for log4j.xml can be used. It's pretty straightforward and can be used to send emails.
You need to edit it and enter your smtp host, username, password, port and subject.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<SMTP>
<name>Mail1</name>
<subject>SUBJECT</subject>
<to>TO_EMAIL</to>
<from>FROM_EMAIL</from>
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>487</smtpPort>
<ignoreExceptions>false</ignoreExceptions>
<smtpUsername>username</smtpUsername>
<smtpPassword>password</smtpPassword>
<smtpProtocol>smtps</smtpProtocol>
<HtmlLayout charset="UTF-8" locationInfo="true" />
<ThresholdFilter level="ERROR"/>
</SMTP>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Mail1"/>
</Root>
</Loggers>
</Configuration>
Reference: Log4j SMTP Appender

Related

Can i run my log asynchronously using log4j 1.x with log4j.properties file?

I am currently working on performance of "log4j 1.x" vs "logback" i.e. (slf4j).
I can append async to my logback, but i am not able to find any link which can async my log4j.
Async is only introduced in log4j 2.x? or is there any way to make my log4j 1.x work async.
Please assist me.
Thank you.
Asynchronous logging is one of Log4j 2's strong points. Instead of the AsyncAppender, use Async Loggers: the performance is much better.
The Log4j 2 performance page compares Log4j-1.x, Logback and Log4j 2. Take a look and then decide what logging framework to use.
By the way, Log4j 2 has an adapter called log4j-1.2-api-2.6.jar which lets your application use the old log4j-1.2 API, but uses the new log4j 2 implementation.
(Log4j-1.x also has an AsyncAppender. Just like the Log4j 2 AsyncAppender and the Logback AsyncAppender, it uses a BlockingQueue. Log4j 2's Async Loggers use a non-blocking data structure which is much faster.)
log4j.properties does not handle the advanced configuration features
supported by the DOMConfigurator such as support custom ErrorHandlers,
nested appenders such as the AsyncAppender, etc.
Refer log4j API Documentation
There are two ways to configure AsyncAppender in log4j 1.x
Using log4j.xml
Configure AsyncAppender as below
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console1" class="org.apache.log4j.ConsoleAppender">
<param name="target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n" />
</layout>
</appender>
<!--wrap ASYNC around other appender if you want -->
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<param name="BufferSize" value="500" />
<!-- LocationInfo is Optional, use only if location info is required. (Default value : false)-->
<!-- <param name="LocationInfo" value="true" /> -->
<appender-ref ref="console1" />
<root>
<priority value="debug" />
<appender-ref ref="ASYNC" />
</root>
</log4j:configuration>
Programmatically
You can configure AsyncAppender programmatically as below
static {
enableAsyncAuditLog(Logger.getRootLogger());
}
private static void enableAsyncAuditLog(Logger targetLogger) {
Enumeration<Appender> appenders = targetLogger.getAllAppenders();
AsyncAppender asyncAppender = new AsyncAppender();
asyncAppender.setBufferSize(500);
asyncAppender.setLocationInfo(true); // Otherwise Class and Line info will not be available to logger
while (appenders.hasMoreElements()) {
Appender appender = appenders.nextElement();
if (!(appender instanceof AsyncAppender)) {
targetLogger.removeAppender(appender);
asyncAppender.addAppender(appender);
}
}
appenders = asyncAppender.getAllAppenders();
if (appenders != null && appenders.hasMoreElements()) {
targetLogger.addAppender(asyncAppender);
}
}
You can refer this project for reference

Log4j2 Database insert in Websphere?

In my local,
I created an log4j2.xml to config like that.
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
<FastFile name="AsyncFastFile" fileName="${sys:logFilename}"
immediateFlush="false" append="true">
<PatternLayout>
<pattern>test %m %m %ex%n</pattern>
</PatternLayout>
</FastFile>
<JDBC name="databaseAppender" tableName="mytablename">
<DriverManager url="jdbc:oracle:thin:ORCL_USERNAME/PASS#//MYSERVERNAME:PORTNO/DBNAME" />
<Column name="KEY1" pattern="%X{sayi1}" />
<Column name="KEY2" pattern="%X{sayi2}" />
</JDBC>
<JDBC name="databaseAppenderJNDI" tableName="mytablename">
<DataSource jndiName="java:/comp/env/jdbc/logWS" />
<Column name="KEY1" pattern="%X{sayi1}" />
<Column name="KEY2" pattern="%X{sayi2}" />
</JDBC>
</appenders>
<loggers>
<!-- pattern layout actually uses location, so we need to include it -->
<asyncLogger name="ASYNC" level="TRACE" additivity="false">
<appender-ref ref="databaseAppender"/>
</asyncLogger>
<!-- pattern layout actually uses location, so we need to include it -->
<asyncLogger name="ASYNCwithJNDI" level="TRACE" additivity="false">
<appender-ref ref="databaseAppenderJNDI"/>
</asyncLogger>
<root level="info" includeLocation="true">
<appender-ref ref="databaseAppender"/>
</root>
</loggers>
</configuration>
I can insert logs to text file and DB by switching loggername in my java class which I call log function. There no problem in my local.
In Webshere server,
I call a web service for logging. I am using same config file and same java codes which calls log function. I can insert logs to a file but I can't insert logs to database. I tried both driverManager url and JNDI. But nothing changed. In two ways, I can't insert log to DB and program doesn't throw any error in try-catch. So I can't find problem where is.
Note : There is no problem at JNDI url and drivermanager url. Because I can perfectly use these urls in different webservice in wepshere. Also I can insert log to Database in my local with same codes.
Note : There is no problem to insert logs to txt file. only DB insertion is problem in Websphere server. So there isn't problem at config file classpath.
For your info, I am using oracle DB but the problem isn't about DB.
I trace network packages in server and I realize that web service don't send any package to DB. Packages don't reach to DB.
How can I solve this problem?
THX.
Please report this in the log4j2 issue tracker so that the log4j team can look at it.

How to make 2 or more java project write in a single log file

I am using log4j to write all the logs in a file.
i have 2 different java projects say proj1 and proj2, where project1 is required project for proj2. I have added proj1 as a dependency for proj2.
proj1 has log4j setup done and is working fine.
Now my problem is when I am running a method in proj2, it will call proj1 as well.
So I want to have a single logfile for both the projects.
Any input please?
There are several ways to write to a single log file but which way is best depends on a couple of details which you omit.
If proj2 includes proj1 as a library, you can make it use proj1's log4j configuration file. This works because you only have a single VM. The most simple solution here is to either copy the first project's config into the other or not give the second project any log config; it will then read the config from its dependencies.
If proj2 starts proj1 as an external process, you need to configure both projects to use a SocketAppender since only a single Java VM can ever write to a single log file.
Related:
log4j: How to use SocketAppender?
I was also facing the same problem but i got the solution and
have configured my log4j.xml like this:
used a appender:
<appender name="FILE1" class="org.apache.log4j.RollingFileAppender">
<errorHandler class="org.apache.log4j.helpers.OnlyOnceErrorHandler" />
<param name="File" value="E:/OESController.log" />
<param name="Append" value="true" />
<param name="MaxFileSize" value="20KB" />
<param name="MaxBackupIndex" value="2" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %d{dd MMM yyyy HH:mm:ss.SSS} %-5l - %m%n%n" />
</layout>
</appender>
<!-- Root Logger -->
<root>
<priority value="error" />
<appender-ref ref="FILE" />
</root>
Note:*Root logger logs for entire application.*
let me know if you still face the problem.

How to make isInfoEnabled disabled in Log4J XML configuration

I have various log files (debug, error) and different configurations (e.g. dev, prod). In dev I'd like to have everything logged into my debug.log and errors written to error.log. However in production I'd like to switch the debug logging off. Right now I'm using this config:
<appender name="FileAppender" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="/logs/debug.log" />
<param name="Append" value="true" />
<param name="Threshold" value="OFF" />
...
So it will decide whether to write or not itself. But I'd like to check in the code sometimes, if the logger writes to log:
if(logger.isInfoEnabled()) {};
Even the threshold is set to 'OFF' it does says, that info is enabled.
What I'm trying to do is to configure the XML, so it would not only skip writing to the debug.log in the production, but also it would set the infoEnabled value to false, so I could use it in my code. Is there are any possibilities to do that?
Thanks in advance!
Configure your ROOT logger to 'WARN' level. In Log4J 1.x:
<root>
<level value="WARN" />
</root>
In Log4J 2.x:
<root level="WARN">
</root>

Creating multiple log files of different content with log4j

Is there a way to configure log4j so that it outputs different levels of logging to different appenders?
I'm trying to set up multiple log files. The main log file would catch all INFO and above messages for all classes. (In development, it would catch all DEBUG and above messages, and TRACE for specific classes.)
Then, I would like to have a separate log file. That log file would catch all DEBUG messages for a specific subset of classes, and ignore all messages for any other class.
Is there a way to get what I'm after?
This should get you started:
log4j.rootLogger=QuietAppender, LoudAppender, TRACE
# setup A1
log4j.appender.QuietAppender=org.apache.log4j.RollingFileAppender
log4j.appender.QuietAppender.Threshold=INFO
log4j.appender.QuietAppender.File=quiet.log
...
# setup A2
log4j.appender.LoudAppender=org.apache.log4j.RollingFileAppender
log4j.appender.LoudAppender.Threshold=DEBUG
log4j.appender.LoudAppender.File=loud.log
...
log4j.logger.com.yourpackage.yourclazz=TRACE
Perhaps something like this?
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- general application log -->
<appender name="MainLogFile" class="org.apache.log4j.FileAppender">
<param name="File" value="server.log" />
<param name="Threshold" value="INFO" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %t [%-40.40c] %x - %m%n"/>
</layout>
</appender>
<!-- additional fooSystem logging -->
<appender name="FooLogFile" class="org.apache.log4j.FileAppender">
<param name="File" value="foo.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %t [%-40.40c] %x - %m%n"/>
</layout>
</appender>
<!-- foo logging -->
<logger name="com.example.foo">
<level value="DEBUG"/>
<appender-ref ref="FooLogFile"/>
</logger>
<!-- default logging -->
<root>
<level value="INFO"/>
<appender-ref ref="MainLogFile"/>
</root>
</log4j:configuration>
Thus, all info messages are written to server.log; by contrast, foo.log contains only com.example.foo messages, including debug-level messages.
I had this question, but with a twist - I was trying to log different content to different files. I had information for a LowLevel debug log, and a HighLevel user log. I wanted the LowLevel to go to only one file, and the HighLevel to go to both a file, and a syslogd.
My solution was to configure the 3 appenders, and then setup the logging like this:
log4j.threshold=ALL
log4j.rootLogger=,LowLogger
log4j.logger.HighLevel=ALL,Syslog,HighLogger
log4j.additivity.HighLevel=false
The part that was difficult for me to figure out was that the 'log4j.logger' could have multiple appenders listed. I was trying to do it one line at a time.
Hope this helps someone at some point!
For the main logfile/appender, set up a .Threshold = INFO to limit what is actually logged in the appender to INFO and above, regardless of whether or not the loggers have DEBUG, TRACE, etc, enabled.
As for catching DEBUG and nothing above that... you'd probably have to write a custom appender.
However I'd recommend not doing this, as it sounds like it would make troubleshooting and analysis pretty hard:
If your goal is to have a single file where you can look to troubleshoot something, then spanning your log data across different files will be annoying - unless you have a very regimented logging policy, you'll likely need content from both DEBUG and INFO to be able to trace execution of the problematic code effectively.
By still logging all of your debug messages, you are losing any performance gains you usually get in a production system by turning the logging (way) down.
Demo link: https://github.com/RazvanSebastian/spring_multiple_log_files_demo.git
My solution is based on XML configuration using spring-boot-starter-log4j. The example is a basic example using spring-boot-starter and the two Loggers writes into different log files.

Categories

Resources