Hello there,
I didnt send the source of the appender since I didnt think it is important.
The thing is the appender works correctly everything is fine. The problem
is that other ROOT appenders also receive messages for this logger.. even
if this logger has only one appender which is with additivity=false. The
same thing done with XML configuration works as expected.
I will paste all the source.. the custom appender is using Atmosphere
framework to send websocket messages,thats why I didnt send the source
before since it is not so important, you can ignore it if you like.
Appender :
package org.gochev.core.log.appender;
import org.gochev.core.log.data.LogEntryData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.util.Booleans;
import org.atmosphere.cpr.BroadcasterFactory;
import org.fusesource.jansi.HtmlAnsiOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Plugin(name = LogStreamAppender.NAME, category = "Core", elementType =
"appender")
public class LogStreamAppender extends AbstractAppender {
public final static String NAME = "LogStream";
private static final String BROADCASTER_ID_LOG =
"admin-queue-agent-logviewer";
protected final Logger LOG = LogManager.getLogger(getClass());
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
public LogStreamAppender(String name, Filter filter, Layout<? extends
Serializable> layout, boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
@Override
public void append(LogEvent event) {
if (BroadcasterFactory.getDefault() != null) {
try {
readLock.lock();
String message = new String(getLayout().toByteArray(event));
BroadcasterFactory.getDefault().lookup(LogStreamAppender.BROADCASTER_ID_LOG,
true).broadcast(new LogEntryData(message + "<br/>"));
} finally {
readLock.unlock();
}
}
}
@PluginFactory
public static LogStreamAppender createAppender(
@PluginAttribute("name") String name,
@PluginAttribute("pattern") String pattern,
@PluginAttribute("ignoreExceptions") String ignore,
@PluginElement("filters") Filter filter) {
return new LogStreamAppender(name, filter,
PatternLayout.createLayout(pattern, null, null,
Charset.forName("UTF-8"), true, true, null, null),
Booleans.parseBoolean(ignore, false));
}
}
Using XML Configuration where everything works as expected (after matt told
me what additivity is doing ): (only my appender is recieving messages for
this package.. and the root appender(s) doesnt)
<Configuration status="INFO" packages="org.gochev.core.log">
<Appenders>
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="%highlight{%d [%t] %-5level: %msg%n%throwable}" />
</Console>
<LogStream name="ADMINSTREAM" pattern="%highlight{%d [%t]
%-5level: %msg%n%throwable}">
</LogStream>
<!-- I have even more appenders here.. but this is a bit smaller config -->
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="CONSOLE"/>
<!-- I have even more appenders here.. but this is a bit smaller config -->
</Root>
<Logger name="org.gochev.core.service" level="DEBUG"
additivity="false">
<AppenderRef ref="ADMINSTREAM"/>
</Logger>
*This works *.. only my ADMINSTREAM is recieving messages from services...
the CONSOLE log is not receiving them
Using code... where it *doesn't work* and the additivity flag basically
makes no difference. This code is in service.. invoked from a Spring
Controller on demand(when user wants).
String loggerName = "org.gochev.core.service";
if (enable) {
final LoggerContext ctx = (LoggerContext)
LogManager.getContext(false);
Configuration cfg = ctx.getConfiguration();
if (cfg instanceof XmlConfiguration) {
//add appender if not added
Appender appender = cfg.getAppender(LogStreamAppender.NAME);
if (appender == null) {
appender =
LogStreamAppender.createAppender(LogStreamAppender.NAME, "%highlight{%d
[%t] %-5level: %msg%n%throwable}", "false", null);
appender.start();
cfg.addAppender(appender);
}
//add logger if not added
LoggerConfig logger = ((XmlConfiguration)
cfg).getLogger(loggerName);
if (logger == null) {
logger = new LoggerConfig(loggerName, Level.DEBUG,
false);
cfg.addLogger(loggerName, logger);
} else {
logger.setLevel(Level.DEBUG);
}
logger.addAppender(appender, Level.DEBUG, null);
ctx.updateLoggers(cfg);
}
} else {
this.deleteLogger(loggerName);
}
About your question if the logger is already loaded .. why I am not
changing the additivity .. yes .. this is how I want it .. if some user
specify manually in XML configuration that he wants additivity .. I dont
want to replace it ... also if he specify it with false.. he should not
have issues.. the problem is that in 99% of cases.. the user will use the
XML configuration only for his packages from his part and not for the core
stuff so I want to give him an ability in admin backend to be able to
start/stop this logging.
I will try with ThresholdFilter but this sounds more like a workaround ..
then a fix.
If I have to use Node and to create this configuration.. can someone tell
me .. after I create this Document ... can I merge it with the XML one ? or
can I somehow take the XML ones ... add a new node.. and then pass this to
the logger context ? somehow is there any doc or example about this ?
Thanks
On Wed, Jul 23, 2014 at 8:01 PM, Matt Sicker <[email protected]> wrote:
> Ah, good point.
>
>
> On 23 July 2014 11:51, Ralph Goers <[email protected]> wrote:
>
>> Well, you can say that about everything in core. Clearly there is some
>> stuff in core that we are going to carefully consider the impact to users
>> before we allow it to change. And saying “not supported” is much different
>> that saying “we may not maintain binary compatibility”.
>>
>> Ralph
>>
>> On Jul 23, 2014, at 8:56 AM, Matt Sicker <[email protected]> wrote:
>>
>> I say not supported because that API is subject to change in 2.x releases
>> since it's not log4j-api.
>>
>>
>> On 23 July 2014 10:46, Ralph Goers <[email protected]> wrote:
>>
>>> Matt,
>>>
>>> That isn’t quite true. What he is trying to do should work. We just
>>> want to find a better way to do it.
>>>
>>> As for the code below, I don’t see the source for LogStreamAppender, but
>>> it is never being started which will ultimately cause problems.
>>>
>>> Note also that if the LoggerConfig existed and had additivity = true the
>>> code below will not be changing that to false.
>>>
>>> Can you create a Jira issue and attach a sample project that
>>> demonstrates the problem?
>>>
>>> Ralph
>>>
>>> On Jul 23, 2014, at 8:30 AM, Matt Sicker <[email protected]> wrote:
>>>
>>> Programmatic configuration is not yet supported which is the biggest
>>> issue here. The only "supported" way of doing it would be through creating
>>> a hierarchy of Node objects that directly correspond to a parsed tree of an
>>> XML or JSON config file.
>>>
>>>
>>> On 23 July 2014 06:40, Remko Popma <[email protected]> wrote:
>>>
>>>> One thing you can try is in your log4j2.xml, add a threshold filter
>>>> that accepts INFO to the console appender. For an example, see
>>>> http://logging.apache.org/log4j/2.x/manual/filters.html#ThresholdFilter
>>>>
>>>> Sent from my iPhone
>>>>
>>>> On 2014/07/23, at 16:27, Nayden Gochev <[email protected]> wrote:
>>>>
>>>> Matt,
>>>> so you are right.. if I do a XML configuration and make additivity=
>>>> false and register my logger with appenderRef there.. everything works as
>>>> expected.. the ROOT appenders are not used.
>>>> However.. when I do it via code ( the one I send in the previous mail
>>>> ) the root appenders are working...
>>>> Do I do something wrong abotu creation ?
>>>> or is there some difference in how updateLoggers(cfg) works compared to
>>>> after parsing the log4j2.xml and again updating loggers ?
>>>>
>>>> Thanks,
>>>> Nayden
>>>>
>>>>
>>>> On Wed, Jul 23, 2014 at 9:55 AM, Nayden Gochev <[email protected]>
>>>> wrote:
>>>>
>>>>> Hello Matt,
>>>>> the problem is that I want to create the appender and the logger
>>>>> together via code.
>>>>> So the appender or the logger are not mentioned in the log4j2.xml file
>>>>>
>>>>> I am creating the appender via the createAppender factory method but
>>>>> in the code.
>>>>> Full code for enable the logger + appender I use is this :
>>>>> if (enable) {
>>>>> final LoggerContext ctx = (LoggerContext)
>>>>> LogManager.getContext(false);
>>>>> Configuration cfg = ctx.getConfiguration();
>>>>> if (cfg instanceof XmlConfiguration) {
>>>>> //add appender if not added
>>>>> Appender appender =
>>>>> cfg.getAppender(LogStreamAppender.NAME);
>>>>> if (appender == null) {
>>>>> appender
>>>>> = LogStreamAppender.createAppender(LogStreamAppender.NAME, "%highlight{%d
>>>>> [%t] %-5level: %msg%n%throwable}", "false", null);
>>>>> cfg.addAppender(appender);
>>>>> }
>>>>> //add logger if not added
>>>>> LoggerConfig logger = ((XmlConfiguration)
>>>>> cfg).getLogger(loggerName);
>>>>> if (logger == null) {
>>>>> logger = new LoggerConfig(loggerName, Level.DEBUG,
>>>>> false);
>>>>> cfg.addLogger(loggerName, logger);
>>>>> } else {
>>>>> logger.setLevel(Level.DEBUG);
>>>>> }
>>>>> logger.addAppender(appender, Level.DEBUG, null);
>>>>> ctx.updateLoggers(cfg);
>>>>> }
>>>>> }
>>>>>
>>>>> The problem is that indeed this LogStreamAppender is receiving
>>>>> everything correctly. However in the log4j2.xml I have several other
>>>>> appenders added at the ROOT log.
>>>>> <Appenders>
>>>>> <Console name="CONSOLE" target="SYSTEM_OUT">
>>>>> <PatternLayout pattern="%highlight{%d [%t] %-5level:
>>>>> %msg%n%throwable}" />
>>>>> </Console>
>>>>> .....
>>>>> </Appenders>
>>>>> <Root level="info">
>>>>> <AppenderRef ref="CONSOLE"/>
>>>>>
>>>>> ....
>>>>> </Root>
>>>>>
>>>>> All of which .. start automatically receive my Stream log ... which is
>>>>> what I don't want to happen :(
>>>>> So even if I pass additivity to false... the root appenders (which
>>>>> were info but are now as it looks are debug for this package (loggerName))
>>>>> are still receiving the messages
>>>>>
>>>>> Regards,
>>>>> Nayden
>>>>>
>>>>>
>>>>>
>>>>> On Tue, Jul 22, 2014 at 10:35 PM, Matt Sicker <[email protected]>
>>>>> wrote:
>>>>>
>>>>>> How are you creating the appender? It might work better using a
>>>>>> config file.
>>>>>>
>>>>>>
>>>>>> On 22 July 2014 02:47, Nayden Gochev <[email protected]> wrote:
>>>>>>
>>>>>>> Hello Matt,
>>>>>>>
>>>>>>> this sounds great however it doesn't work.
>>>>>>>
>>>>>>> Maybe it is a bug ?
>>>>>>>
>>>>>>> what I do is :
>>>>>>>
>>>>>>> final LoggerContext ctx = (LoggerContext)
>>>>>>> LogManager.getContext(false);
>>>>>>>
>>>>>>> Configuration cfg = ctx.getConfiguration();
>>>>>>>
>>>>>>> if (cfg instanceof XmlConfiguration) {
>>>>>>>
>>>>>>> //add logger if not added
>>>>>>>
>>>>>>> LoggerConfig logger = ((XmlConfiguration)
>>>>>>> cfg).getLogger(loggerName);
>>>>>>>
>>>>>>> if (logger == null) {
>>>>>>>
>>>>>>> logger = new LoggerConfig(loggerName,
>>>>>>> Level.DEBUG, false); //here this false is for the additive
>>>>>>>
>>>>>>> cfg.addLogger(loggerName, logger);
>>>>>>>
>>>>>>> } else { //change the level leave it as it is
>>>>>>>
>>>>>>> logger.setLevel(Level.DEBUG);
>>>>>>>
>>>>>>> }
>>>>>>>
>>>>>>> logger.addAppender(newAppender, Level.DEBUG, null);
>>>>>>>
>>>>>>> ctx.updateLoggers(cfg);
>>>>>>>
>>>>>>> }
>>>>>>>
>>>>>>>
>>>>>>> Still .. all root loggers also receive the log message(s) together
>>>>>>> with this new appender that is passed here.
>>>>>>>
>>>>>>> I am using 2.0 final
>>>>>>>
>>>>>>>
>>>>>>> On Jul 21, 2014 5:32 PM, "Matt Sicker" <[email protected]> wrote:
>>>>>>>
>>>>>>>> You should specify false for additivity to prevent the automatic
>>>>>>>> inheritance.
>>>>>>>>
>>>>>>>>
>>>>>>>> On 21 July 2014 09:20, Nayden Gochev <[email protected]> wrote:
>>>>>>>>
>>>>>>>>> In short:
>>>>>>>>> I am trying to add a custom appender + logger for some package for
>>>>>>>>> example "org.test"
>>>>>>>>> However the root appenders (appender refs) are always added as
>>>>>>>>> well automatically.. so even if I have a root appender(s) with level
>>>>>>>>> INFO
>>>>>>>>> defined in the log4j2.xml,
>>>>>>>>> ones I add a logger with level DEBUG.. with a new appender (only
>>>>>>>>> 1) .. the 2 root appenders that I have defined in the XML
>>>>>>>>> automatically
>>>>>>>>> starts receiving this DEBUG messages as well.
>>>>>>>>>
>>>>>>>>> Is it possible somehow to REMOVE this ROOT appends.. from my newly
>>>>>>>>> created logger ?
>>>>>>>>> newLoggerConfig.getAppenderRefs().clear(); doesn't work of course
>>>>>>>>> :)
>>>>>>>>>
>>>>>>>>> Thanks for the help,
>>>>>>>>> much appreciated.
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Matt Sicker <[email protected]>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Matt Sicker <[email protected]>
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>> --
>>> Matt Sicker <[email protected]>
>>>
>>>
>>>
>>
>>
>> --
>> Matt Sicker <[email protected]>
>>
>>
>>
>
>
> --
> Matt Sicker <[email protected]>
>