/** * 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. * <p> */ package org.olat.core.logging.activity; import java.util.List; import org.olat.core.id.Identity; import org.olat.core.id.context.ContextEntry; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; /** * Wrapper around a ThreadLocal holding a IUserActivityLogger. * <p> * The ThreadLocalUserActivityLogger can be called any time during * event handling and in dispose in order to do * User Activity Logging - i.e. to call the log() method. * <p> * The idea of the ThreadLocalUserActivityLogger is to avoid * having to pass all sorts of resource objects between Controller * and Manager methods for the sole purpose of having them available * at log time. * <p> * In the current design each Controller has a IUserActivityLogger * which is set up with the ThreadLocalUserActivityLogger's content * at Controller construction time (done in DefaultController<init>). * <p> * Each Controller is then suggested to add LoggingResourcables * in the constructor - i.e. to add those resourceables which it knows * at construction time and which will be used later in its event() * methods to do logging. * <p> * With this simplification of having data (i.e. LoggingResourceables) * collected by a ThreadLocal one might easily loose oversight over * what exactly is set at what time where. <br> * To help work around this the ILoggingAction/ResourceableTypeList * concept was introduced: it is a runtime safety check comparing * all the LoggingResourceables available in an IUserActivityLogger * versus what is in the businesPath and what the programmer knows * at implementation time as to which LoggingResourceables are mandatory * or optional. * <p> * Note that there is a Helper class called ThreadLocalUserActivityLoggerInstaller * which is a peer class working together with ThreadLocalUserActivityLogger * but was separated for logical reasons: * <ul> * <li>The ThreadLocalUserActivityLogger mainly has two public functions: * the addLoggingResourceInfo and the log method: the OLAT developer * should mainly get in contact with these two method</li> * <li>The ThreadLocalUserActivityLoggerInstaller's job though is to * set up/tear down IUserActivityLogger's at event handling time.</li> * </ul> * Initial Date: 21.10.2009 <br> * @author Stefan * @see ILoggingAction * @see org.olat.util.logging.activity.LoggingResourceable * @see IUserActivityLogger * @see ResourceableTypeList */ public class ThreadLocalUserActivityLogger { private static final OLog log_ = Tracing.createLoggerFor(ThreadLocalUserActivityLogger.class); /** THE ThreadLocal IUserActivityLogger - initialized by ThreadLocalUserActivityLoggerInstaller **/ final static ThreadLocal<IUserActivityLogger> userActivityLogger_ = new ThreadLocal<IUserActivityLogger>(); /** package protected getter for the ThreadLocal userActivityLogger - assumes initialized and complains if not ! **/ static IUserActivityLogger getUserActivityLogger() { IUserActivityLogger logger = userActivityLogger_.get(); if (logger==null) { if(log_.isDebug()) {//only generate this exception in debug log_.warn("No UserActivityLogger set! Reinitializing now.", new Exception("stacktrace")); } return new UserActivityLoggerImpl(); } return logger; } public static Identity getLoggedIdentity() { IUserActivityLogger logger = userActivityLogger_.get(); if (logger!=null) { return logger.getLoggedIdentity(); } return null; } /** * Adds the given LoggingResourceInfo to the ThreadLocalUserActivityLogger. * <p> * Only use this method before Controller constructors - if you are * inside a Controller constructor and want to add a LoggingResourceable * to the Controller's IUserActivityLogger call DefaultController.addLoggingResourceInfo instead! * <p> * For temporarily adding/removing LoggingResourceInfos use one of the log() methods directly instead * <p> * @param resourceInfo */ public static void addLoggingResourceInfo(ILoggingResourceable resourceInfo) { IUserActivityLogger logger = userActivityLogger_.get(); if (logger!=null) { logger.addLoggingResourceInfo(resourceInfo); } } /** * Issues a log entry to the logging database based on the given loggingAction and * the loggingResourceables. * <p> * The loggingAction defines - besides the actual logMessage - which loggingResourceables * are expected and allowed to be logged alongside the logMessage. * If this check fails, a log.warn() is issued. This should then be fixed by either * adding the required loggingResourceable to the UserActivityLogger in the Controller's * constructor (preferred way) - or by passing it in this log call (less preferred way). * <p> * @param loggingAction the logging action which should be logged * @param callingClass the class which calls this log method - stored to the database * @param loggingResourceables optional and the less preferred way of passing * loggingResourceables to be stored alongside this log message to the database. * the preferred way though is to have it all added to the UserActivityLogger earlier - * that is, usually and typically in the Controller' constructor. * Preferred way of using loggingResourceables is via comma separated 'list' */ public static void log(ILoggingAction loggingAction, Class<?> callingClass, ILoggingResourceable... loggingResourceables) { IUserActivityLogger logger = userActivityLogger_.get(); if (logger==null) { log_.error("No ThreadLocal IUserActivityLogger set - cannot log to database: "+loggingAction.getActionVerb()); } else { logger.log(loggingAction, callingClass, loggingResourceables); } } /** * Sets the given ActionType 'sticky' to this Thread's ThreadLocal IUserActivityLogger - * i.e. when you set the sticky ActionType any ActionType passed along to * the log() method in the ILoggingAction is overwritten. * @param actionType the sticky ActionType which should overwrite * whatever comes in the ILoggingAction in log() */ public static void setStickyActionType(ActionType actionType) { IUserActivityLogger logger = userActivityLogger_.get(); if (logger!=null) { logger.setStickyActionType(actionType); } } /** * Gets 'sticky' ActionType of this Thread's ThreadLocal IUserActivityLogger - or null * if none is set */ public static ActionType getStickyActionType() { IUserActivityLogger logger = userActivityLogger_.get(); if (logger!=null) { return logger.getStickyActionType(); } else { return null; } } /** * Sets the businessPath on the ThreadLocal' UserActivityLogger. * <p> * Internal framework use only */ static void setBusinessPath(String businessPath, UserActivityLoggerImpl current) { IUserActivityLogger logger = userActivityLogger_.get(); if (logger==current) { // stop return; } if (logger instanceof UserActivityLoggerImpl) { UserActivityLoggerImpl genLogger= (UserActivityLoggerImpl)logger; genLogger.frameworkSetBusinessPath(businessPath); } } /** * Sets the context entries on the ThreadLocal' UserActivityLogger. * <p> * Internal framework use only */ static void setBCContextEntries(List<ContextEntry> bcContextEntries, UserActivityLoggerImpl current) { IUserActivityLogger logger = userActivityLogger_.get(); if (logger==current) { // stop return; } if (logger instanceof UserActivityLoggerImpl) { UserActivityLoggerImpl genLogger= (UserActivityLoggerImpl)logger; genLogger.frameworkSetBCContextEntries(bcContextEntries); } } }