/* * Copyright 2010 Guillaume Nodet. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.log4j.sift; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import org.apache.log4j.Appender; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.OptionFactory; /** * A log4j appender which splits the output based on an MDC key */ public class MDCSiftingAppender extends AppenderSkeleton { private String key; private String defaultValue = "default"; private OptionFactory appender; private Map appenders = new HashMap(); private Node head = null; private Node tail = null; private long lastCheck; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getDefault() { return defaultValue; } public void setDefault(String defaultValue) { this.defaultValue = defaultValue; } public OptionFactory getAppender() { return appender; } public void setAppender(OptionFactory appender) { this.appender = appender; } protected void append(LoggingEvent event) { Object value = event.getMDC(key); String valStr = value == null ? defaultValue : value.toString(); Appender app = getAppender(valStr); app.doAppend(event); } public synchronized void close() { for (Iterator it = appenders.values().iterator(); it.hasNext();) { Node node = (Node) it.next(); node.appender.close(); } appenders.clear(); } public boolean requiresLayout() { return false; } protected synchronized Appender getAppender(String valStr) { long timestamp = System.currentTimeMillis(); Node node = (Node) appenders.get(valStr); if (node == null) { node = new Node(); Properties props = new Properties(); props.put(key, valStr); node.next = head; node.prev = null; node.appender = (Appender) appender.create(props); node.appender.setName(getName() + "[" + valStr + "]"); node.timestamp = timestamp; head = node; if (tail == null) { tail = node; } appenders.put(valStr, node); } else { Node p = node.prev; Node n = node.next; node.next = head; node.prev = null; head = node; if (p != null) { p.next = n; } if (n != null) { n.prev = p; } else { tail = p; } node.timestamp = timestamp; } // Do not check too often if (timestamp - lastCheck > 1000) { Node n = tail; while (n != null && timestamp - n.timestamp > 30 * 60 * 1000) { n.appender.close(); n = n.prev; } if (n == null) { tail = head = null; } else { n.next = null; tail = n; } } return node.appender; } protected static class Node { Node next; Node prev; Appender appender; long timestamp; } }