2.3. Usage

Logging Table

The goal of logging is to fill the logging table with useful data. Here's a quick summary of what's written into the table. The full details can be found here. Note that the goal was to get data into one flat table without the need to later join the table with any other olat table. The idea was to get a snapshot of the information 'at the time the activity happens' - since course or user information might later change (e.g. a user might change university or a course' title might change etc). That's why the logging table is rather wide.

  • log_id, creationdate, sourceclass: these are technical fields describing a particular logging entry

  • session_id, user_id, username, userproperties[1..12]: these are session and user fields. The user properties is an array of 12 varchar rows which can be configured as to what is stored in them. Suggested usages are to store Shibboleth properties or other useful information about the user.

  • actioncrudtype, actionverb, actionobject, resourceadminaction, simpleduration: these fields describe the action itself. An action is a combination of actionverb + actionobject. The idea behind this is to allow to search for only actionverb or only actionobject. The actionverb is an enum - hence limited.

  • businesspath, targetresid/type/name, parentresid/type/name, grandparent.., greatgrandparent: These fields are resources and define the scope or context in which a particular action happened. Not all fields need to be set. The idea of having four such resource groups (target, parent, grandparent, greatgrandparent) is to limit the database size and also came as a result of the analysis of existing logging usages (where normally 4 is enough). Should the UserActivityLogger have more than 4 ILoggingResourceables available to store in the database, the 3 lowest and the uppermost one are stored, the others not.

Initialization

There are a few things to remember when issuing a user activity log:

  • All logging happens via an IUserActivityLogger. The IUserActivityLogger contains context information such as Course, Node etc. During event handling - i.e. within the event() or dispose() method - the framework sets up a ThreadLocalUserActivityLogger which contains all available context information. This information is taken from the Controller's IUserActivityLogger.

  • Therefore each Controller has an IUserActivityLogger which is initialized at constructor time with context information that is available in the ThreadLocalUserActivityLogger. Plus any additional context information which is available in the Controller's constructor should be passed to the IUserActivityLogger.

  • Logging an event is done by calling IUserActivityLogger.log() method. Should there be additional context information available only at that time, it can be passed to the log() method directly.

Here is an example usage of how the logging is initialized with a course as context information.

// from RunMainController
public RunMainController(final UserRequest ureq, final WindowControl wControl, final ICourse course, final String initialViewIdentifier,
		final boolean offerBookmark, final boolean showCourseConfigLink) {
	super(ureq, wControl);		
			
	... other stuff ...

	// initialize the logging with the course
	addLoggingResourceable(LoggingResourceable.wrap(course));
	
	... other stuff

	// issue a logging action
	ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_ENTERING, getClass());
	
}
					

Once the above is done, then during an event further logging actions can be issued as follows:

// from EditorMainController					
public void event(UserRequest ureq, Controller source, Event event) {
	
	... other stuff ...
	
		ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_PUBLISHED, getClass());
	
	... other stuff ...
}
					

Context information: ILoggingResourceable

Anything that is passed to the logging and stored in the logging table is treated via the interface ILoggingResourceable. The ILoggingResourceable contains the following properties which are stored to the logging table:

  • type: the type of the resource

  • id: identifier of the resource - usually maps 1:1 to the primary key of the resource

  • name: a verbose name of the resource

As mentioned above, you are encouraged to pass ILoggingResourceables to the IUserActivityLogger in a Controller's constructor.

There are factory methods with which to construct ILoggingResourcesables. Here are some examples:

org.olat.util.logging.activity.LoggingResourceable.wrap(ICourse);
org.olat.util.logging.activity.LoggingResourceable.wrap(CourseNode);
org.olat.util.logging.activity.LoggingResourceable.wrap(BGArea);
					

Once created, the ILoggingResourceable can be passed to the IUserActivityLogger as follows:

// within a Controller:

addLoggingResourceable(LoggingResourceable.wrap(someResourceable));


// before creating a Controller with the intension to pass it to the Controller:

ThreadLocalUserActivityLogger.addLoggingResourceable(LoggingResourceable.wrap(anotherResourceable));
					

ILoggingActions

The ILoggingAction describes the actionverb and actionobject as well as what the expected ILoggingResourceables to be stored along with such that logging action are. The idea behind this class is to have a well defined place (subclasses of ILoggingAction) where everything about the existing logging actions is defined. This can be useful when determining what statistics can be done, which parts of OLAT are logged and where more logging might be required. In short, this is a in-code documentation of the logging actions.

Therefore whenever you want to log something via the activity logger, you need to have an ILoggingAction (along with a set of ILoggingResourceables which were collected by the Controller's IUserActivityLogger - or when in a Manager in the ThreadLocalUserActivityLogger). The main log method therefor looks as follows - note that the main info you need to pass to the log() method is the ILoggingAction:

// from IUserActivityLogger
public void log(ILoggingAction loggingAction, Class callingClass, ILoggingResourceable... loggingResourceInfosOrNull);
					

Besides being a container for actionverb and actionobject, the ILoggingAction contains a paranoia safety check for ILoggingResourceables: The ILoggingAction must be given a ResourceableTypeList. The latter is the specification of which ILoggingResourceables are mandatory, optional or not allowed when logging this particular ILoggingAction. This information is then used at runtime to check if the provided set of ILoggingResourceables is correct and issue WARNs and ERRORs if that's not the case. This should allow to - over time - find both errors in this ILoggingAction definition but also prevent you from a situation where you 'thought that the ICourse' was available in the Controller XY - but actually at runtime it is not.