<html>
<head>
<style><!--
.hmmessage P
{
margin:0px;
padding:0px
}
body.hmmessage
{
font-size: 10pt;
font-family:Tahoma
}
--></style></head>
<body class='hmmessage'><div dir='ltr'>
I'm not sure if there is any real problem with line separators when I paste the code, but the archive shows my message totally screwed up, yet the email shows it correctly.<div>Just to be sure, I posted the code to git, if anybody is having problems: https://gist.github.com/1502077</div><div><br></div><div>Sorry for the trouble!</div><div><br><div><div id="SkyDrivePlaceholder"></div><hr id="stopSpelling">From: ym119162@hotmail.com<br>To: logback-user@qos.ch<br>Date: Tue, 20 Dec 2011 10:56:50 -0500<br>Subject: Re: [logback-user] User-filtered logging - can be done?<br><br>
<meta http-equiv="Content-Type" content="text/html; charset=unicode">
<meta name="Generator" content="Microsoft SafeHTML">
<style>
.ExternalClass .ecxhmmessage P
{padding:0px;}
.ExternalClass body.ecxhmmessage
{font-size:10pt;font-family:Tahoma;}
</style>
<div dir="ltr">
<div><div>Hello,</div><div><br></div><div>I tried the DynamicThresholdFilter suggestion, and it worked nice, so did the sifting appender. I didn't tested the ThresholdFilter, as in the end it seems it was not a good idea to forbid the user events to go to the STANDARD file... it is a "master log file" after all, better let it be complete. Thanks again for all the suggestions, Ralph.</div><div><br></div><div>However, the DynamicThresholdFilter generated more logging than desired for many less important loggers. As a refinement of the idea, I created a custom TurboFilter with similar capabilities, but specific per logger too. I'm posting below my new XML config and filters, someone else may use it, plus I'd like some suggestions on ugly parts of the code ;-) (see FIXME comments below).</div><div><br></div><div>Some of my new config XML:</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><configuration debug="true"></div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><turboFilter class="com.sample.project.CustomTurboFilter"></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><userCustomization></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><user>ID_USER_1</user></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><loggerLevel></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><logger>com.sample.project</logger></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><level>DEBUG</level></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></loggerLevel></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><loggerLevel></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><logger>com.sample.legacy</logger></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><level>INFO</level></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></loggerLevel></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></userCustomization></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><userCustomization></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><user>ID_USER_2</user></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><loggerLevel></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><logger>com.sample.project</logger></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><level>TRACE</level></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></loggerLevel></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>...</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></userCustomization></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></turboFilter></div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><appender name="FILTERED" class="ch.qos.logback.classic.sift.SiftingAppender"></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><key>userId</key></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><defaultValue>unknown</defaultValue></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></discriminator></div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><filter class="com.sample.project.CustomFilter"/></div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><sift> ... </sift></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></appender></div><div><br></div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><logger name="com.sample.project" level="WARN"></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><appender-ref ref="FILTERED" /></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></logger></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><root level="WARN"></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span><appender-ref ref="STANDARD" /></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></root></div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></configuration></div><div><br></div><div>The CustomTurboFilter:</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public class CustomTurboFilter extends TurboFilter {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private Map<String,Map<String,Level>> userCustomizationsMap = new HashMap<String, Map<String,Level>>();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private Set<String> customizedUsersSet;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable throwable) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (!isStarted())</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return FilterReply.NEUTRAL;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>String user = MDC.get("userId");</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (user == null)</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return FilterReply.NEUTRAL;</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>Map<String, Level> map = userCustomizationsMap.get(user);</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (map == null)</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return FilterReply.NEUTRAL; //undefined user</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//test each logger-level for this user, find the closest match to 'logger'</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>String loggerName = logger.getName();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>Level specificLoggerLevel = null;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>int specificLoggerNameLength = -1;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>for (Entry<String, Level> e : map.entrySet()) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>String name = e.getKey();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//FIXME This is awful code, I know, but I found no other way to check logger hierarchy (and LoggerComparator is not fit for this)</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//FIXME guess it could be a bit faster using only 'startsWith', 'length' and 'charAt', but still String manipulation</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (loggerName.equals(name)) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>specificLoggerLevel = e.getValue(); //exact logger match</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>break;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//FIXME Here I attempt to find the most specific logger definition for the current logger (awful too)</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>int length = name.length();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (loggerName.startsWith(name+".") && length > specificLoggerNameLength) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//the logger in this definition is a parent of 'logger', and longer name than the last one</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>specificLoggerNameLength = length; </div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>specificLoggerLevel = e.getValue();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (specificLoggerLevel != null && level.isGreaterOrEqual(specificLoggerLevel)) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return FilterReply.ACCEPT; //match found, force accept</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>} else {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return FilterReply.NEUTRAL; //no matching logger found, ignore</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public void addUserCustomization(UserLoggersMapping userLoggersMapping) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>String user = userLoggersMapping.getUser();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>user = (user == null ? "" : user.trim());</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (!user.isEmpty() && !userCustomizationsMap.containsKey(user)) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>Map<String,Level> loggerLevelMap = new HashMap<String, Level>();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>for (LoggerLevelPair llp : userLoggersMapping.getLoggerLevelList()) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>String logger = llp.getLogger();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>logger = (logger == null ? "" : logger.trim());</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (!logger.isEmpty() && !loggerLevelMap.containsKey(logger) && llp.getLevel() != null) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>addInfo("Logger["+logger+"] level customized to "+llp.getLevel()+" for user "+user);</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>loggerLevelMap.put(logger, llp.getLevel());</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (!loggerLevelMap.isEmpty()) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>userCustomizationsMap.put(user, loggerLevelMap);</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public Set<String> getCustomizedUsersSet() {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return customizedUsersSet;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public void start() {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>customizedUsersSet = Collections.unmodifiableSet(userCustomizationsMap.keySet()); //Will be used by CustomFilter</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>super.start();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//Just JavaBeans, also shortening the code :-P</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public static class UserLoggersMapping {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private String user;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private List<LoggerLevelPair> loggerLevelList = new ArrayList<LoggerLevelPair>();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private String getUser() {return user;}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public void setUser(String user) {this.user = user;}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private List<LoggerLevelPair> getLoggerLevelList() {return loggerLevelList;}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public void addLoggerLevel(LoggerLevelPair levelLoggerPair) {loggerLevelList.add(levelLoggerPair);}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public static class LoggerLevelPair {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private Level level;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private String logger;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private Level getLevel() {return level;}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public void setLevel(Level level) {this.level = level;}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private String getLogger() {return logger;}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public void setLogger(String logger) {this.logger = logger;}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><br></div><div>The CustomFilter. It is optional, used to deny sifted logging to users not specified by the CustomTurboFilter:</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public class CustomFilter extends Filter<ILoggingEvent> {</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>private Set<String> customizedUsersSet;</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public FilterReply decide(ILoggingEvent event) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//deny anything but a user filtered by CustomTurboFilter</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (!isStarted() || customizedUsersSet == null)</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return FilterReply.DENY;</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>String user = MDC.get("userId");</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (user == null)</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return FilterReply.DENY;</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>return customizedUsersSet.contains(user) ? FilterReply.NEUTRAL : FilterReply.DENY;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>public void start() {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>//FIXME is this safe? Joran's sequential processing seems to make it so</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>Iterator<TurboFilter> iterator = ((LoggerContext) getContext()).getTurboFilterList().iterator();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>while (iterator.hasNext()) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>TurboFilter tf = iterator.next();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (tf instanceof CustomTurboFilter) {</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>customizedUsersSet = ((CustomTurboFilter) tf).getCustomizedUsersSet();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>addInfo("Allowed users: " + customizedUsersSet);</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>break;</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>if (customizedUsersSet == null)</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>addWarn("No matching TurboFilter found");</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>super.start();</div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><br></div><div><span class="ecxApple-tab-span" style="white-space:pre"> </span>}</div><div><br></div><div>So, can I improve the code somehow, or is there another approach to evaluate? All contributions are most welcome.</div><div><br></div><div>Thanks again!</div></div><br><div><div id="ecxSkyDrivePlaceholder"></div><hr id="ecxstopSpelling">From: rgoers@apache.org<br>Date: Thu, 15 Dec 2011 07:32:10 -0800<br>To: logback-user@qos.ch<br>Subject: Re: [logback-user] User-filtered logging - can be done?<br><br>
I took a look at Logger.java. You are correct that the effective level is stored so there is no performance overhead. That still wouldn't change my recommendation though.<div><br></div><div>As for the SiftingAppender, yes you would get a file for each user. It would contain Warn events for those who pass the defaultThreshold and debug events, etc. for those explicitly configured. With the RollingFileAppender you will still get all those same events, just in a single file. </div><div><br></div><div>Ralph</div><div><br><div><div>On Dec 15, 2011, at 5:32 AM, Y M wrote:</div><br class="ecxApple-interchange-newline"><blockquote>
<div class="ecxhmmessage"><div dir="ltr">
<style>
.ExternalClass .ecxhmmessage P
{padding:0px;}
.ExternalClass body.ecxhmmessage
{font-size:10pt;font-family:Tahoma;}
</style>
<div dir="ltr"><div>Ralph,</div><div><br></div><div>Many thanks for your reply.</div><div><br></div><div>> Your configuration is not a great way to do it. Every event that passes the log level will get passed to the appender for filtering by user. This is more expensive than having the filter be a turbo filter.<br>> The way I would handle this is to use the DynamicThresholdFilter.</div><div><br></div><div>Yes, this turbo filter is precisely what I need to do the user level filtering. It was so briefly mentioned in the manual that I barely took notice of it, and never inspected its capabilities. It would further allow user filtered events in loggers other than 'com.sample.project', but not a problem, maybe even better.</div><div><br></div><div>>If you want each user to log to a separate file then use the SiftingAppender to route the log events to a log file for that user regardless of the logger.<br><br>Not a requirement right now, but I may try it once I get everything else right. But I a sifting would make all events from 'com.sample.project' log to separate user files (i.e. I'd get a file for user 999999 containing its WARN events, despite not targetting it for finer logging through the turbo filter), am I right? That's something I'd like to avoid, as the application currently handles a few hundred users at any time, and a dozen new user specific log files every day would not be desirable.</div><div><br>> The configuration below accepts the event if it is from a specific user regardless of its level. All others flow through unimpeded. Unfortunately, if you don't want debug events for the user logged to LEGACY or STDOUT then you will need to add a ThresholdFilter to those two appenders. However, this isn't as bad since they will only be for that single user.</div><div><br></div><div>If I understood correctly, to mimic this appending behavior I want: the ThresholdFilter on STANDARD (example) will need to "double filter" on WARN, removing the DEBUG+ events allowed by the turbo filter, is that it? I may think this is a bit inefficient, but more out-of-the-box than other solutions, like creating a custom logger (if that could do it somehow).</div><div><br>> Notice I specified the log level on all the loggers. IMO this is better in practice because a) the hierarchy doesn't have to be searched all the way to the parent on each event so it will perform better and b) it is less confusing to people looking a the configuration, especially when you specify additivity.<br></div><div><br></div><div>a) I'm afraid this is not correct, the 'Performance' section in Architecture Chapter of the manual says every logger always knows its effective level and it is notified when it changes; if some efficiency would be achieved, it would be during start up or during level changes while running (or so it seems :-)</div><div>b) you may be right on this... I usually go for a 'less code, more convention' rule, it also makes level changes simpler (also because I use conditional processing for test environment, and changing only the ROOT level is simpler)</div><div><br></div><div>Thanks for the great answer, I'll definitely try it and post a follow-up.</div><div>If anybody knows a different approach, I'd be glad to try it.</div><div><br></div><div>Thanks again!</div><div><br><div><div id="ecxSkyDrivePlaceholder"></div>> From: <a href="mailto:rgoers@apache.org">rgoers@apache.org</a><br>> Date: Wed, 14 Dec 2011 23:52:29 -0800<br>> To: <a href="mailto:logback-user@qos.ch">logback-user@qos.ch</a><br>> Subject: Re: [logback-user] User-filtered logging - can be done?<br>> <br>> Your configuration is not a great way to do it. Every event that passes the log level will get passed to the appender for filtering by user. This is more expensive than having the filter be a turbo filter.<br>> <br>> The way I would handle this is to use the DynamicThresholdFilter. If you want each user to log to a separate file then use the SiftingAppender to route the log events to a log file for that user regardless of the logger.<br>> <br>> The configuration below accepts the event if it is from a specific user regardless of its level. All others flow through unimpeded. Unfortunately, if you don't want debug events for the user logged to LEGACY or STDOUT then you will need to add a ThresholdFilter to those two appenders. However, this isn't as bad since they will only be for that single user.<br>> <br>> Notice I specified the log level on all the loggers. IMO this is better in practice because a) the hierarchy doesn't have to be searched all the way to the parent on each event so it will perform better and b) it is less confusing to people looking a the configuration, especially when you specify additivity.<br>> <br>> <configuration> <br>> <turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter"><br>> <Key>userId</Key><br>> <DefaultThreshold>WARN</DefaultThreshold><br>> <onLower>NEUTRAL</onLower><br>> <onHigherOrEqual>ACCEPT</onHigherOrEqual><br>> <MDCValueLevelPair><br>> <value>123456</value><br>> <level>DEBUG</level><br>> </MDCValueLevelPair><br>> </turboFilter><br>> <br>> <appender name="FILTERED" class="ch.qos.logback.core.rolling.RollingFileAppender"><br>> ...<br>> </appender><br>> <br>> <appender name="STDOUT"<br>> class="ch.qos.logback.core.ConsoleAppender"><br>> <layout class="ch.qos.logback.classic.PatternLayout"><br>> <Pattern>TEST %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern><br>> </layout><br>> </appender><br>> <br>> <logger name="com.sample.legacy" additivity="false" level="warn"><br>> <appender-ref ref="LEGACY"/><br>> </logger><br>> <br>> <logger name="com.sample.project" level="warn"> <!-- Add additivity="false" if you don't want these also logged to STDOUT --><br>> <appender-ref ref="FILTERED"./><br>> </logger><br>> <br>> <root level="warn" ><br>> <appender-ref ref="STDOUT" /><br>> </root><br>> </configuration><br>> <br>> Ralph<br>> <br>> On Dec 14, 2011, at 3:50 PM, Chris Pratt wrote:<br>> <br>> > You might look at using the User Name as a Marker. Then you could set up a filter that will filter on that User's Marker.<br>> > (*Chris*)<br>> > <br>> > On Wed, Dec 14, 2011 at 3:27 PM, Y M <<a href="mailto:ym119162@hotmail.com">ym119162@hotmail.com</a>> wrote:<br>> > Hello,<br>> > <br>> > I'm trying to obtain a certain logging configuration, but so far I'm unsuccessful. I've read through the Logback manual, but I don't know if I can do what I want.<br>> > <br>> > It is a web application, and my objective is to enable a finer logging level for specific users, getting written to a specific log file, allowing better debugging directly in production environment. When needed, someone will set a tuned XML config using JMX to enable logging for the duration of the tests/data gathering (JMX is working fine). This is the relevant part of my logback.xml file:<br>> > <br>> > <configuration debug="true"><br>> > <br>> > ...<br>> > <br>> > <appender name="FILTERED" class="ch.qos.logback.core.rolling.RollingFileAppender"><br>> > <filter class="com.sample.project.UserFilter"><br>> > <!--<br>> > //filter code<br>> > if (level != null && !event.getLevel().isGreaterOrEqual(level)) {<br>> > return FilterReply.DENY;<br>> > }<br>> > if (user.equals(MDC.get("userId"))) {<br>> > return FilterReply.ACCEPT;<br>> > } else {<br>> > return FilterReply.DENY;<br>> > }<br>> > <br>> > --><br>> > <level>DEBUG</level><br>> > <user>123456</user><br>> > </filter><br>> > <br>> > ...<br>> > </appender><br>> > <br>> > <logger name="com.sample.project"><br>> > <appender-ref ref="FILTERED" /><br>> > </logger><br>> > <logger name="com.sample.legacy" additivity="false"><br>> > <appender-ref ref="LEGACY" /><br>> > </logger><br>> > <root level="WARN"><br>> > <appender-ref ref="STANDARD" /><br>> > </root><br>> > <br>> > </configuration><br>> > <br>> > With this config, I intend to have:<br>> > - WARN+ level at root;<br>> > - events from legacy project tree only on 'LEGACY' (working fine);<br>> > - 'FILTERED' logging only DEBUG+ events from user '123456' under 'com.sample.project', while its level ramains at WARN (not OK).<br>> > <br>> > Sample:<br>> > getLogger("com.sample.abc").warn("..."); //logged to STANDARD<br>> > getLogger("com.sample.abc").debug("..."); //not logged<br>> > getLogger("com.sample.legacy").warn("..."); //logged to LEGACY<br>> > getLogger("com.sample.project").warn("..."); //logged to STANDARD; also logged to FILTERED if user is 123456<br>> > getLogger("com.sample.project").debug("..."); //logged to FILTERED if user is 123456<br>> > <br>> > As I tested, a 'logger.debug()' from the desired user does not get logged, the filter is not even called, and I suspect that the effective WARN level denies the event before reaching my filter (I hoped for the other way around). If I set DEBUG on 'com.sample.project', 'FILTERED' will get the correct logging, but 'STANDARD' will be flooded with DEBUG+ logging from 'com.sample.project'. And disabling additivity on it removes all logging belonging to this hierarchy from 'STANDARD'.<br>> > <br>> > So, I see Logback is working properly, my desired logging seems to be against the standard architecture. But is there a way to archive the described effect?<br>> > I was thinking of a TurboFilter, allowing lower level events from selected users (not tested yet), but I'm not sure the effective level would kick in first, also it wouldn't do the desired logging separation between 'STANDARD' and 'FILTERED' anyway. As another approach, maybe this would require a custom Logger class, handling specific appenders in a special way. Unfortunately, I have no idea if I can plug in custom Loggers, and also not sure how to code it (Logger class source is quite complex in a quick look).<br>> > <br>> > Sorry if it wasn't clear, I'm not sure how to present the situation. I can provide further details, if needed.<br>> > Any help is welcome. If someone could show me which way to follow, maybe classes or methods to extend, I'd be grateful.<br>> > <br>> > Many thanks!<br>> > <br>> > _______________________________________________<br>> > Logback-user mailing list<br>> > <a href="mailto:Logback-user@qos.ch">Logback-user@qos.ch</a><br>> > <a href="http://mailman.qos.ch/mailman/listinfo/logback-user" target="_blank">http://mailman.qos.ch/mailman/listinfo/logback-user</a><br>> > <br>> > _______________________________________________<br>> > Logback-user mailing list<br>> > <a href="mailto:Logback-user@qos.ch">Logback-user@qos.ch</a><br>> > <a href="http://mailman.qos.ch/mailman/listinfo/logback-user" target="_blank">http://mailman.qos.ch/mailman/listinfo/logback-user</a><br>> <br>> _______________________________________________<br>> Logback-user mailing list<br>> <a href="mailto:Logback-user@qos.ch">Logback-user@qos.ch</a><br>> <a href="http://mailman.qos.ch/mailman/listinfo/logback-user" target="_blank">http://mailman.qos.ch/mailman/listinfo/logback-user</a><br></div></div></div>
</div></div>
_______________________________________________<br>Logback-user mailing list<br><a href="mailto:Logback-user@qos.ch">Logback-user@qos.ch</a><br>http://mailman.qos.ch/mailman/listinfo/logback-user</blockquote></div><br></div><br>_______________________________________________
Logback-user mailing list
Logback-user@qos.ch
http://mailman.qos.ch/mailman/listinfo/logback-user</div> </div>
<br>_______________________________________________
Logback-user mailing list
Logback-user@qos.ch
http://mailman.qos.ch/mailman/listinfo/logback-user</div></div> </div></body>
</html>