/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.core.util.threadlog;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Appender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.olat.core.configuration.PersistedProperties;
/**
* This class manages the ip addresses and the associated loglevel/appender pairs
* in conjunction with the ThreadLocalLogLevelManager which it calls to do the
* actual threadlocal based log level controlling.
* <p>
* This class is basically a Map containing the ip addresses for which log levels/appenders
* are modified.
* <P>
* Initial Date: 13.09.2010 <br>
* @author Stefan
*/
public class RequestBasedLogLevelManager {
/** Name of the persistentproperty which contains the list of ip addresses to loglevel/appenders **/
private final String PROP_NAME_REQUESTBASED_IPS = "RequestBasedIps";
/** The core of this class is this map containing the list of ip addresses mapped to logconfigs **/
private final Map<String,LogConfig> remoteAddrs2LogConfigs = new ConcurrentHashMap<String,LogConfig>();
/** A reference to the persistentProperties used to persistent the ip address map **/
private PersistedProperties persistentProperties;
/** A reference to the ThreadLocalLogLevelManager is used to trigger the actual threadlocal based log level controlling**/
private final ThreadLocalLogLevelManager threadLocalLogLevelManager;
/** semi-old-school way of allowing controllers to access a manager - via this INSTANCE construct **/
private static RequestBasedLogLevelManager INSTANCE;
/** semi-old-school way of allowing controllers to access a manager - via this INSTANCE construct **/
public static RequestBasedLogLevelManager getInstance() {
return INSTANCE;
}
/**
* Creates the RequestBasedLogLevelManager - which is a SINGLETON and should only be installed
* once per VM.
* @param threadLocalLogLevelManager the ThreadLocalLogLevelManager is used to trigger the actual threadlocal based log level controlling
*/
public RequestBasedLogLevelManager(ThreadLocalLogLevelManager threadLocalLogLevelManager) {
if (threadLocalLogLevelManager==null) {
throw new IllegalArgumentException("threadLocalLogLevelManager must not be null");
}
this.threadLocalLogLevelManager = threadLocalLogLevelManager;
INSTANCE = this;
}
/** Sets the PersistedProperties of this manager **/
public void setPersistentProperties(PersistedProperties persistentProperties) {
if (persistentProperties==null) {
throw new IllegalArgumentException("persistentProperties must not be null");
}
this.persistentProperties = persistentProperties;
init();
}
/** (re)initializes the manager by resetting the map and loading it using the PersistentProperties mechanism **/
void init() {
reset();
String ipsAndLevels = loadIpsAndLevels();
if (ipsAndLevels!=null) {
String[] ipsAndLevelArray = ipsAndLevels.split("\r\n");
for (int i = 0; i < ipsAndLevelArray.length; i++) {
String anIpAndLevel = ipsAndLevelArray[i];
if (anIpAndLevel!=null && anIpAndLevel.length()>0 && anIpAndLevel.contains("=")) {
setLogLevelAndAppender(anIpAndLevel);
}
}
}
}
/** Loads the ip to loglevel/appender map using the PersistentProperties mechanism **/
public String loadIpsAndLevels() {
try{
return persistentProperties.getStringPropertyValue(PROP_NAME_REQUESTBASED_IPS, true);
} catch(Exception e) {
Logger.getLogger(RequestBasedLogLevelManager.class).warn("loadIpsAndLevels: Error loading property value "+PROP_NAME_REQUESTBASED_IPS, e);
return null;
}
}
/** Stores the ip to loglevel/appender map using the PersistentProperties mechanism **/
public void storeIpsAndLevels(String ipsAndLevels) {
try{
persistentProperties.setStringProperty(PROP_NAME_REQUESTBASED_IPS, ipsAndLevels, true);
} catch(Exception e) {
Logger.getLogger(RequestBasedLogLevelManager.class).warn("storeIpsAndLevels: Error storing property value "+PROP_NAME_REQUESTBASED_IPS, e);
}
}
/**
* Clears the in-memory ip to loglevel/appender map - not a full reinit method, use init for that
* @see RequestBasedLogLevelManager#init()
**/
public void reset() {
remoteAddrs2LogConfigs.clear();
}
/**
* Sets a particular IP to a particular loglevel/appender using the format
* 192.168.0.1=DEBUG,DebugLog
* @param configStr a one line configuration string in the following format: 192.168.0.1=DEBUG,DebugLog
*/
public void setLogLevelAndAppender(String configStr) {
StringTokenizer st = new StringTokenizer(configStr, "=");
String anIpAddress = st.nextToken();
String logConfig = st.nextToken();
Level level;
Appender appender;
if (logConfig.contains(",")) {
st = new StringTokenizer(logConfig, ",");
level = Level.toLevel(st.nextToken());
String categoryAppenderStr = st.nextToken();
Logger l = Logger.getLogger(categoryAppenderStr);
if (l!=null) {
appender = l.getAppender(categoryAppenderStr);
if (appender==null) {
appender = Logger.getRootLogger().getAppender(categoryAppenderStr);
}
} else {
appender = null;
}
} else {
level = Level.toLevel(logConfig);
appender = null;
}
setLogLevelAndAppenderForRemoteAddr(anIpAddress, level, appender);
}
/** internal helper method which takes care of the actual modifying of the ip to loglevel/appender map **/
private void setLogLevelAndAppenderForRemoteAddr(String remoteAddr, Priority level, Appender appender) {
if (level==null) {
remoteAddrs2LogConfigs.remove(remoteAddr);
} else {
remoteAddrs2LogConfigs.put(remoteAddr, new LogConfig(level, appender));
}
}
/**
* Activates the ThreadLocalAwareLogger for this request if the remote address matches a configured IP.
* <p>
* This method is used very frequently and should hence be performant!
* @param request the request of which the remote address is matched against the map of ips
*/
public void activateRequestBasedLogLevel(HttpServletRequest request) {
LogConfig logConfig = remoteAddrs2LogConfigs.get(request.getRemoteAddr());
if (logConfig!=null) {
threadLocalLogLevelManager.forceThreadLocalLogLevel(logConfig);
} else {
threadLocalLogLevelManager.releaseForcedThreadLocalLogLevel();
}
}
/**
* Deactivate the ThreadLocalAwareLogger if it was previously activated - does nothing otherwise
*/
public void deactivateRequestBasedLogLevel() {
threadLocalLogLevelManager.releaseForcedThreadLocalLogLevel();
}
}