system_log.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. /**
  3. * Elgg system log.
  4. * Listens to events and writes crud events into the system log database.
  5. *
  6. * @package Elgg.Core
  7. * @subpackage Logging
  8. */
  9. /**
  10. * Retrieve the system log based on a number of parameters.
  11. *
  12. * @todo too many args, and the first arg is too confusing
  13. *
  14. * @param int|array $by_user The guid(s) of the user(s) who initiated the event.
  15. * Use 0 for unowned entries. Anything else falsey means anyone.
  16. * @param string $event The event you are searching on.
  17. * @param string $class The class of object it effects.
  18. * @param string $type The type
  19. * @param string $subtype The subtype.
  20. * @param int $limit Maximum number of responses to return. (default from settings)
  21. * @param int $offset Offset of where to start.
  22. * @param bool $count Return count or not
  23. * @param int $timebefore Lower time limit
  24. * @param int $timeafter Upper time limit
  25. * @param int $object_id GUID of an object
  26. * @param string $ip_address The IP address.
  27. * @return mixed
  28. */
  29. function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", $limit = null,
  30. $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0,
  31. $ip_address = "") {
  32. global $CONFIG;
  33. $by_user_orig = $by_user;
  34. if (is_array($by_user) && sizeof($by_user) > 0) {
  35. foreach ($by_user as $key => $val) {
  36. $by_user[$key] = (int) $val;
  37. }
  38. } else {
  39. $by_user = (int)$by_user;
  40. }
  41. $event = sanitise_string($event);
  42. $class = sanitise_string($class);
  43. $type = sanitise_string($type);
  44. $subtype = sanitise_string($subtype);
  45. $ip_address = sanitise_string($ip_address);
  46. if ($limit === null) {
  47. $limit = elgg_get_config('default_limit');
  48. }
  49. $limit = (int)$limit;
  50. $offset = (int)$offset;
  51. $where = array();
  52. if ($by_user_orig !== "" && $by_user_orig !== false && $by_user_orig !== null) {
  53. if (is_int($by_user)) {
  54. $where[] = "performed_by_guid=$by_user";
  55. } else if (is_array($by_user)) {
  56. $where [] = "performed_by_guid in (" . implode(",", $by_user) . ")";
  57. }
  58. }
  59. if ($event != "") {
  60. $where[] = "event='$event'";
  61. }
  62. if ($class !== "") {
  63. $where[] = "object_class='$class'";
  64. }
  65. if ($type != "") {
  66. $where[] = "object_type='$type'";
  67. }
  68. if ($subtype !== "") {
  69. $where[] = "object_subtype='$subtype'";
  70. }
  71. if ($timebefore) {
  72. $where[] = "time_created < " . ((int) $timebefore);
  73. }
  74. if ($timeafter) {
  75. $where[] = "time_created > " . ((int) $timeafter);
  76. }
  77. if ($object_id) {
  78. $where[] = "object_id = " . ((int) $object_id);
  79. }
  80. if ($ip_address) {
  81. $where[] = "ip_address = '$ip_address'";
  82. }
  83. $select = "*";
  84. if ($count) {
  85. $select = "count(*) as count";
  86. }
  87. $query = "SELECT $select from {$CONFIG->dbprefix}system_log where 1 ";
  88. foreach ($where as $w) {
  89. $query .= " and $w";
  90. }
  91. if (!$count) {
  92. $query .= " order by time_created desc";
  93. $query .= " limit $offset, $limit"; // Add order and limit
  94. }
  95. if ($count) {
  96. $numrows = get_data_row($query);
  97. if ($numrows) {
  98. return $numrows->count;
  99. }
  100. } else {
  101. return get_data($query);
  102. }
  103. return false;
  104. }
  105. /**
  106. * Return a specific log entry.
  107. *
  108. * @param int $entry_id The log entry
  109. *
  110. * @return mixed
  111. */
  112. function get_log_entry($entry_id) {
  113. global $CONFIG;
  114. $entry_id = (int)$entry_id;
  115. return get_data_row("SELECT * from {$CONFIG->dbprefix}system_log where id=$entry_id");
  116. }
  117. /**
  118. * Return the object referred to by a given log entry
  119. *
  120. * @param \stdClass|int $entry The log entry row or its ID
  121. *
  122. * @return mixed
  123. */
  124. function get_object_from_log_entry($entry) {
  125. if (is_numeric($entry)) {
  126. $entry = get_log_entry($entry);
  127. if (!$entry) {
  128. return false;
  129. }
  130. }
  131. $class = $entry->object_class;
  132. $id = $entry->object_id;
  133. if (!class_exists($class)) {
  134. // failed autoload
  135. return false;
  136. }
  137. $getters = array(
  138. 'ElggAnnotation' => 'elgg_get_annotation_from_id',
  139. 'ElggMetadata' => 'elgg_get_metadata_from_id',
  140. 'ElggRelationship' => 'get_relationship',
  141. );
  142. if (isset($getters[$class]) && is_callable($getters[$class])) {
  143. $object = call_user_func($getters[$class], $id);
  144. } elseif (preg_match('~^Elgg[A-Z]~', $class)) {
  145. $object = get_entity($id);
  146. } else {
  147. // surround with try/catch because object could be disabled
  148. try {
  149. $object = new $class($entry->object_id);
  150. return $object;
  151. } catch (Exception $e) {
  152. }
  153. }
  154. if (!is_object($object) || get_class($object) !== $class) {
  155. return false;
  156. }
  157. return $object;
  158. }
  159. /**
  160. * Log a system event related to a specific object.
  161. *
  162. * This is called by the event system and should not be called directly.
  163. *
  164. * @param object $object The object you're talking about.
  165. * @param string $event The event being logged
  166. * @return void
  167. */
  168. function system_log($object, $event) {
  169. global $CONFIG;
  170. static $log_cache;
  171. static $cache_size = 0;
  172. if ($object instanceof Loggable) {
  173. /* @var \ElggEntity|\ElggExtender $object */
  174. if (datalist_get('version') < 2012012000) {
  175. // this is a site that doesn't have the ip_address column yet
  176. return;
  177. }
  178. // reset cache if it has grown too large
  179. if (!is_array($log_cache) || $cache_size > 500) {
  180. $log_cache = array();
  181. $cache_size = 0;
  182. }
  183. // Has loggable interface, extract the necessary information and store
  184. $object_id = (int)$object->getSystemLogID();
  185. $object_class = get_class($object);
  186. $object_type = $object->getType();
  187. $object_subtype = $object->getSubtype();
  188. $event = sanitise_string($event);
  189. $time = time();
  190. $ip_address = sanitize_string(_elgg_services()->request->getClientIp());
  191. if (!$ip_address) {
  192. $ip_address = '0.0.0.0';
  193. }
  194. $performed_by = elgg_get_logged_in_user_guid();
  195. if (isset($object->access_id)) {
  196. $access_id = $object->access_id;
  197. } else {
  198. $access_id = ACCESS_PUBLIC;
  199. }
  200. if (isset($object->enabled)) {
  201. $enabled = $object->enabled;
  202. } else {
  203. $enabled = 'yes';
  204. }
  205. if (isset($object->owner_guid)) {
  206. $owner_guid = $object->owner_guid;
  207. } else {
  208. $owner_guid = 0;
  209. }
  210. // Create log if we haven't already created it
  211. if (!isset($log_cache[$time][$object_id][$event])) {
  212. $query = "INSERT DELAYED into {$CONFIG->dbprefix}system_log
  213. (object_id, object_class, object_type, object_subtype, event,
  214. performed_by_guid, owner_guid, access_id, enabled, time_created, ip_address)
  215. VALUES
  216. ('$object_id','$object_class','$object_type', '$object_subtype', '$event',
  217. $performed_by, $owner_guid, $access_id, '$enabled', '$time', '$ip_address')";
  218. insert_data($query);
  219. $log_cache[$time][$object_id][$event] = true;
  220. $cache_size += 1;
  221. }
  222. }
  223. }
  224. /**
  225. * This function creates an archive copy of the system log.
  226. *
  227. * @param int $offset An offset in seconds from now to archive (useful for log rotation)
  228. *
  229. * @return bool
  230. */
  231. function archive_log($offset = 0) {
  232. global $CONFIG;
  233. $offset = (int)$offset;
  234. $now = time(); // Take a snapshot of now
  235. $ts = $now - $offset;
  236. // create table
  237. $query = "CREATE TABLE {$CONFIG->dbprefix}system_log_$now as
  238. SELECT * from {$CONFIG->dbprefix}system_log WHERE time_created<$ts";
  239. if (!update_data($query)) {
  240. return false;
  241. }
  242. // delete
  243. // Don't delete on time since we are running in a concurrent environment
  244. if (delete_data("DELETE from {$CONFIG->dbprefix}system_log WHERE time_created<$ts") === false) {
  245. return false;
  246. }
  247. // alter table to engine
  248. if (!update_data("ALTER TABLE {$CONFIG->dbprefix}system_log_$now engine=archive")) {
  249. return false;
  250. }
  251. return true;
  252. }
  253. /**
  254. * Default system log handler, allows plugins to override, extend or disable logging.
  255. *
  256. * @param string $event Event name
  257. * @param string $object_type Object type
  258. * @param Loggable $object Object to log
  259. *
  260. * @return true
  261. */
  262. function system_log_default_logger($event, $object_type, $object) {
  263. system_log($object['object'], $object['event']);
  264. return true;
  265. }
  266. /**
  267. * System log listener.
  268. * This function listens to all events in the system and logs anything appropriate.
  269. *
  270. * @param String $event Event name
  271. * @param String $object_type Type of object
  272. * @param Loggable $object Object to log
  273. *
  274. * @return true
  275. * @access private
  276. */
  277. function system_log_listener($event, $object_type, $object) {
  278. if (($object_type != 'systemlog') && ($event != 'log')) {
  279. elgg_trigger_event('log', 'systemlog', array('object' => $object, 'event' => $event));
  280. }
  281. return true;
  282. }
  283. return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
  284. /** Register event to listen to all events **/
  285. $events->registerHandler('all', 'all', 'system_log_listener', 400);
  286. /** Register a default system log handler */
  287. $events->registerHandler('log', 'systemlog', 'system_log_default_logger', 999);
  288. };