Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Events API: Difference between revisions

From MoodleDocs
Line 28: Line 28:
* moodle core and standard plugins will replace all usages of events_trigger() and add_to_log() with proper events
* moodle core and standard plugins will replace all usages of events_trigger() and add_to_log() with proper events
* For events that already exist in Moodle 2.5 the additional legacy information should be added to the event data (in properties 'legacyeventname' and 'legacyeventdata')
* For events that already exist in Moodle 2.5 the additional legacy information should be added to the event data (in properties 'legacyeventname' and 'legacyeventdata')
* For events that substitute add_to_log() call the additional property 'legacylogaction' will be specified
* For events that substitute add_to_log() calls the additional properties 'legacylogaction' and 'legacylogdata' will be specified.
* Function events_trigger() will create and trigger an instance of event_legacy class with non-empty 'legacyeventname' property and optional 'legacyeventdata'
* Function events_trigger() will create and trigger an instance of event_legacy class with non-empty 'legacyeventname' property and optional 'legacyeventdata'
* Function add_to_log() will create and trigger an instance of event_legacy class with non-empty 'legacylogaction' property
* Function add_to_log() will create and trigger an instance of event_legacy class with non-empty 'legacylogaction' property

Revision as of 03:44, 21 May 2013

Events and Event-based Logging. Specification for 2.6

Summary

  • Starting from Moodle 2.6 logging is implemented in form of event-listening by plugins. The events will be objects with much more details than in 2.5- and much more specific.
  • Logging is performed by plugins listening to events.
  • In 2.6 it will also be possible to subscribe to '*' event (which means everything).
  • Everywhere where in 2.5 we add information to log or trigger an event we must generate the new event. For backward compartibility it will also contain information in the 2.5 format.

Standard Moodle distribution will include simple DB logging plugin storing the data in new format and legacy logging plugin storing the data in old format in {log} table. Old standard reports will use the new event logging. All custom reports will be able to work with {log} table using legacy logging plugin.

Events API

Triggering events

  • All events descriptions are objects extending event_base (which is defined in core)
  • Object class name is unique identifier of the event
  • Class name is pluginfullname_event_xxx. Core events will have prefix 'moodle'
  • Plugins store each event object in plugindir/classes/event/xxx.php
  • event name suffix (xxx in the example above) should be SUBJECT_VERB. There are recommendations for verbs to use in events names [1]

Examples: moodle_event_course_completed, mod_assign_event_submission_commented, mod_forum_event_post_shared, mod_forum_event_post_responded etc.

[TODO] We need to agree on "teacher verbs" to use when entity (course, module, etc.) was created, updated and/or deleted

Backward compartibility:

  • moodle core and standard plugins will replace all usages of events_trigger() and add_to_log() with proper events
  • For events that already exist in Moodle 2.5 the additional legacy information should be added to the event data (in properties 'legacyeventname' and 'legacyeventdata')
  • For events that substitute add_to_log() calls the additional properties 'legacylogaction' and 'legacylogdata' will be specified.
  • Function events_trigger() will create and trigger an instance of event_legacy class with non-empty 'legacyeventname' property and optional 'legacyeventdata'
  • Function add_to_log() will create and trigger an instance of event_legacy class with non-empty 'legacylogaction' property

Handling (observing) events

  • Event handlers can be described as it is done now in plugindir/db/events.php, this file is parsed during install/upgrade of plugin and all handlers are removed on uninstall
  • It is possible to subscribe to all events (*)
  • If event handler refers to the old (2.5) name of event, it will be used only for events that have corresponding 'legacyeventname' property. If it refers to 2.6 event class name it will be used with full data
  • It is also possible to dynamically register/unregister handlers but they are not recommended (same as it is with dynamic caches definitions). Ideally they should only be used in unit and/or behat tests

Handlers (observers) sequence

  • Event handlers can also have an attribute 'sortorder' (positive or negative, default 0).
  • User with appropriate capability can overwrite the sequence of handlers for each event type

Event properties

Those are the fields that an event could define. Depending on the type of event, some can be optional and some can be mandatory. Not all of the mandatory fields have to be set when triggering the event as some logic can help defining them in the constructor. For example, we can use $USER to find out what user is currently logged in.

There are suggested list of properties, with some mandatory fields (*) defined for the base class.

  • Event specification version number*
  • Event name*
  • Type*
    • Error
    • User action
      • Procedural action
      • Manual action
    • System log
  • Datetime* (milliseconds? DateTime object? Let's keep in mind that timestamps are unreliable as they don't include timezone!)
  • Context ID*
  • Category ID
  • Category name
  • Course ID*
  • Course name
  • Course module ID*
  • Course module name
  • Component*
    • core
    • course
    • mod_assign
    • mod_workshop
  • Subject* (Defines the object on which the action is performed)
    • user
    • section
    • assignment
    • submission_phase
  • Subject ID*
  • Subject name (Human readable identifier of the object: Mark Johnson, Course ABC, ...)
  • Subject URL (URL to notice the changes)
  • Action* (The exact action that is performed on the object)
    • created
    • moved (for a course, a section, a module, a user between groups)
    • submitted (for an assignment, or a message)
    • ended (for a submission phase for instance)
  • Actor* (user, cli, cron, ...)
  • Actor ID/User ID* (ID assiacted to the actor, typically the user id)
  • Real actor/user ID* (When logged in as, store the real user ID)
  • Actor IP address*
  • Associated object (Object associated to the subject. Ie: category of origin when a course is moved. User to whom a message is sent.)
    • section
    • category
    • user (to whom you sent a message)
  • Associated object ID
  • Transaction type* (CRUD)
    • create
    • read
    • update
    • delete
  • Level* (Reserved for internal use, probabl no more than 3 types, default to normal)
    • major
    • normal
    • minor
  • Severity (In the case of error logging, we would need to set the severity according to http://tools.ietf.org/html/rfc5424#section-6.2.1, defaults to info)
  • Message (human readable log)
  • Data (serialized/JSON'd data for more any extra field specific to this event)
  • Current URL

Data

The field data should be a placeholder for any extra information which is specific to the event. This array/object of values could describe in more details an entry which has been deleted by storing the data, or an entry updated by storing the differences, etc.... This data could ideally be converted to JSON (by a logger) to easily be stored, which means that PHP Objects cannot be included in there. We would not rely on serialized data as they have been proven not reliable.

Real actor/User ID

This should be the real user when we are logged in as someone. Or the user ID of the person on behalf of whom we are performing an action, when running CLI or Cron.

event_base class

abstract class event_base implements cacheable_object, IteratorAggregate {

 // ... constants
 // ... all properties as protected variables, magic function __get() for all properties
 protected function __construct() {
   // Here plugins can initialise all properties that are static for this event (CRUD, object, subject). No dynamic values here.
 }
 public static function create($args = array()) {
   $event = new self();
   // ... Fill the properties from $args
   // Plugins can overwrite with filling the default dynamic values such as $USER->id, $PAGE->get_course(), etc. No DB queries or expensive operations here.
   return $event;    
 }
 public static final function restore($object) {
   $event = new self();
   // .. restore each property from $object to $event
   return $event;
 }
 public static final function create_and_trigger($args = array()) {
   $event = self::create($args);
   $event->trigger();
 }
 public final function trigger() {
   // ... 
 }
 abstract public static function event_name();
 abstract public static function event_description();
 abstract public function can_view($user = null);
 abstract public function event_message();
 public function getIterator() {
   // ...
 }
 public function prepare_to_cache() {
   // ... basic implementation caching all properties
   // plugins are recommended to overwrite with caching only non-static properties
 }
 public static function wake_from_cache($a) {
   $event = new self();
   // ... restore properties from $a
   return $event;
 }

}

Logging

This section is deliberately not called "Logging API" because Moodle does not provide any logging API. All plugins that perform logging and analyse logs to display reports can do it as they wish. Events API defines how to subscribe to events and use them.

From event to report

There are four steps of how event is converted into report:

  1. Handling of events and filtering what needs to be logged ("What to log")
  2. Storing the events data in the log storage (DB, filesystem, etc.) ("How to log")
  3. Retriving the data from log storage ("How to retrieve")
  4. Displaying the data in form of the report ("How to display")

One plugin can cover one or several steps. It is also possible to create a report that has built-in logging and listens to events (all 4 steps). Also external logging systems do not need to care about steps 3 and 4 at all.

Moodle standard distribution provides a suggestion on logging-report chain that can be followed by 3rd party plugins and may be not.

Logging plugins relation

Standard logging plugins

Standard Moodle 2.6 distribution will include 4 plugins:

Legacy logging plugin (tool_loglegacy)

Covers steps 1-3 from above. Listens to all events, for events that have non-empty 'legacylogaction' property adds the data to {log} table.

Core functions get_logs(), get_logs_usercourse(), get_logs_userday() will be checking if this plugin is installed and redirect to its appropriate functions.

All report plugins that use table {log} should use this plugin.

Event logging handling plugin (tool_eventobserver)

Responsible for step 1 from above. Has functions pluginname_register_log_instance() and pluginname_unregister_log_instance() that allow to add/remove log storage instance and also allows admin to configure what kind of events to store for each log instance.

Event logging DB storage plugin (tool_logdbstorage)

Responsible for step 2 from above. Has function pluginname_get_log_instances(). Allows admin to create storage instances and assign event handling plugin to fill each of them

Event logging driver plugin (tool_logdbdriver)

Responsible for step 3 from above. This plugin can only work with tool_logdbstorage. It provides functions to access data in log.