Use Asynchronous appender with JMS appender in log4j - multithreading

I want to know more details about AsyncAppender
lyk
Can we control retries if attached appender is not working?
How does it handle error if attached appender encounters the error
N how can I remove attached appender from async appender....

As you know the AsyncAppender is just a wrapper for other appenders. When you create an AsyncAppender you have attach your "real" appenders to it.
The AsyncAppender will buffer your log messages until it has reached your configured limit and publish those log message to the attached appenders all at once.
From the code for AsyncAppender, it is looks like when the buffer is full it simply calls the doAppend method for each attached appender (simple for loop through all appenders). It would be up to the attached appenders to handle error conditions and retries.
I suggest implementing a custom appender by extending SkeletonAppender and handling everything yourself in the methods you override.
To answer your last question, AsyncAppender has a removeAppender method:
public void removeAppender(final Appender appender) {

Related

logging/error handling inside a custom log4net appender

We have a internally-developed logging system, and we wanted to gain the benefits of log4net such as being able to use multiple appenders simultaneously. We decided to move the legacy logging system into a custom log4net appender. That worked great when the legacy system was configured correctly, but the legacy logger actually writes to a WCF client (making it slow for verbose logging, the reason we wanted to engage other log4net appenders), and recently the WCF configuration was not quite what it should have been. When this happened the application seemed to run ok, but obviously the custom appender for the legacy system was not working. The other appenders worked fine, but there was no output from the custom legacy appender and no error messages indicating that there was a problem.
What is the proper way to handle "recursive" logging, that is logging from inside a custom log4net appender? I tried a naive solution: grabbing log4net from its static object and logging inside the appender:
public class MyCustomAppender : AppenderSkeleton
{
protected override void Append(log4net.Core.LoggingEvent loggingEvent)
{
try
{
// something which throws here...
}
catch (Exception ex)
{
log4net.LogManager.GetLogger(this.GetType()).Error(this.GetType().ToString() + ": error during append", ex);
}
}
}
This was an abject failure, "System.Threading.LockRecursionException: Recursive read lock acquisitions not allowed in this mode." Fair enough; it was the naive solution.
I also tried:
this.ErrorHandler.Error(this.GetType().ToString() + ": error during append", ex);
in the exception handler, as it seemed possible it might be the right thing. Nothing obvious happened.
Is there a way to log errors through log4net inside an appender? Or do I have to do something like hard-code writing to the windows event log directly? Obviously there is great danger of infinite recursive descent, but I would think there would be some way of letting the operator know that one of the appenders failed to run.
JR
Of course as soon as I posted the question, I remembered something. log4net has a debugging switch inside the appSettings. I used this code:
this.ErrorHandler.Error(this.GetType().ToString() + ": error during append", ex);
inside the catch block, then set the magic setting on:
<appSettings>
<!--this is wonderful magic-->
<add key="log4net.Internal.Debug" value="true" />
</appSettings>
Now the ConsoleAppender (the other appender I was using) put out the custom appender exception message.
JR
Make sure you have this line at top:
using System.Threading;

Log4j log custom level for specific user(s) in production

In production environment I'm only logging WARN and ERROR log messages.
I need to implement a mechanism that logs ALL log levels for (a) specific user(s).
I'm handling requests coming in from outside (not necessarily a servlet) and from the beginning I can retrieve the users name, so I'm able to put users name in MDC and it will go to the log messages.
However, I need to check if users name is in a predefined list of users (this list can actually dynamically be updated during runtime) and if the users name is in this list then I need to enable (for this user only) logging to be from fx. TRACE level (meaning all TRACE, DEBUG, INFO, WARN and ERROR).
Is this possible?
UPDATE #1: We're using Spring Boot Log4j
UPDATE #2: Log4j config
LOG_PATTERN=%d{yyyy-MM-dd HH:mm:ss.SSS} %X{context} ${PID} %5p %-10X{username} [%t] - %c{1}(%L): %m%n
log4j.rootCategory=INFO, amqp
log4j.category.org.springframework=WARN
log4j.category.com.acme=DEBUG
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/app.log
log4j.appender.file.MaxFileSize=100MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=${LOG_PATTERN}
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=${LOG_PATTERN}
log4j.appender.amqp=org.springframework.amqp.rabbit.log4j.AmqpAppender
log4j.appender.amqp.host=localhost
log4j.appender.amqp.port=5671
log4j.appender.amqp.username=username
log4j.appender.amqp.password=password
log4j.appender.amqp.virtualHost=vhost
log4j.appender.amqp.exchangeName=logging.pub
log4j.appender.amqp.exchangeType=topic
log4j.appender.amqp.routingKeyPattern=%c.%p
log4j.appender.amqp.declareExchange=false
log4j.appender.amqp.durable=true
log4j.appender.amqp.autoDelete=false
log4j.appender.amqp.contentType=text/plain
log4j.appender.amqp.generateId=false
log4j.appender.amqp.senderPoolSize=2
log4j.appender.amqp.maxSenderRetries=30
log4j.appender.amqp.layout=org.apache.log4j.PatternLayout
log4j.appender.amqp.layout.ConversionPattern=${LOG_PATTERN}
After all the clarifications, the short answer is: no.
The very first check that is done by log4j's trace/debug/.../error methods is the logger level. If the log event level is below the logger configured level -- nothing else is done, the logging method returns. Message formatting, processing the diagnostic context, triggering appenders -- all come later, provided that there is anything to process. In log4j 1 it cannot be changed by configuration.
In log4j 2 we have global filters, which are checked prior to the logger level test -- although I don't know if they'd be fit for this kind of task, I'd definitely start looking there. If you can upgrade to log4j 2, check them.

How to trace log level changes caused by .ConfigureAndWatch

Using .ConfigureAndWatch we have configured log4net to reconfigure its logging level without restarting application processes.
A process start we trace the currently configured log level to our trace file.
How can we detect and trace to our trace files the log level changes happening through .ConfigureAndWatch?
I just (re)discovered that ILoggerRepository has a ConfigurationChanged event.
Tracing log level changes from there works fine.
private static void repository_ConfigurationChanged(object sender, System.EventArgs e)
{
var currentTracelLevel = ((log4net.Repository.Hierarchy.Logger)_trace.Logger).EffectiveLevel;
_trace.InfoFormat("----------------------------- log4net level={0}", currentTracelLevel);
}
You can activate the log4net internal debugging, then you will receive messages from the ConfigureAndWatchHandler class when a change occurs, either on the console or in the trace output (see link for more information)
re your comment: In order to pull back the event into your own code and not rely on the console or trace output I'd recommend creating a custom trace appender that loops back into log4net. This way you can filter out messages you want to bring back into your own logs.

Using Log4j JMS appender to connect with ActiveMQ

I am trying to add logging to our Mule application with ActiveMQ so all Messages are written to a SQL Server Database. THis is the content of my Log4j properties file in Mule:
log4j.rootCategory=INFO, console, jms
log4j.logger.org.apache.activemq=INFO, stdout
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%-5p %d [%t] %c: %m%n
# Mule classes
log4j.logger.org.mule=INFO
log4j.logger.com.mulesoft=INFO
# Your custom classes
log4j.logger.com.mycompany=DEBUG
# JMS Appender
log4j.appender.jms=org.apache.log4j.net.JMSAppender
log4j.appender.jms.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory
log4j.appender.jms.ProviderURL=tcp://sjc04-wduatesb1:9162
#tcp//localhost:61616
log4j.appender.jms.TopicBindingName=logTopic
log4j.appender.jms.TopicConnectionFactoryBindingName=ConnectionFactory
As you can see I am trying to use the JMS appender . I also have a JNDI config file to point to the topic to read off , the contents of that file stored in $MULE_HOME/conf/ is
topic.logTopic=logTopic
However I find even though messages are getting enqueued & dequeued on the topic , it is not being written to the database. Does anybody have any ideas or suggestions as to where I am going wrong?
One way to solve this is to create a subscriber to the topic that puts the message in a queue and then have a queue consumer put the message in the DB.

log4j with liferay and tomcat

I have a Liferay setup on the Tomcat6. I used Log4j initially for portlets/webapps by adding log4j.properties file in the classes folder and log4j.jar file in the web-inf/lib.
Now we have few quartz jobs which are available in the tomcat/lib folder and I want to enable log4j logging for these jobs as well.
For these quartz jobs I have copied same log4j.properties file in the tomcat/lib.
With the new configuration I keep getting the following error:
Could not instantiate appender named "JOBS"
A "org.apache.log4j.RollingFileAppender" is not assignable a "org.apache.log4j.Appender" variable.
The class "org.apache.log4j.Appender" was loaded by ...
How to remove this error?
Is there any way I can keep a single log4j.properties file which can be used by both shared/lib as well as portlet/webapps.
According to this post the problem is that you have configured your log4j twice.
My solution would be that you create the logger instance like this
private static Logger logger = Logger.getLogger(FooBar.class.getName());
and not to have log4j.properties under any lib or class folder but having it under a folder named resources.
AFAIK this should be a place where the both parts of your application may have access to.
Known issue in Liferay: http://issues.liferay.com/browse/LPS-9376
In my case, the problem occurred once I started to use ServiceBuilder services, and added Log4J Logger to one of my ...LocalServiceImpl classes.
Once I removed Log4J logging from there everything got back to norm - exception disappeared.
(I made service methods throw exceptions, and was catching them in a code not related to ServiceBuilder read/generated classes, but it could also be done by changing Log4J logger calls to Liferay's LogUtil calls).

Resources