MDL-49399 task: Add admin log viewer
[moodle.git] / lib / classes / task / database_logger.php
CommitLineData
4b71596f
AN
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Database logger for task logging.
19 *
20 * @package core
21 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24namespace core\task;
25
26defined('MOODLE_INTERNAL') || die();
27
28/**
29 * Database logger for task logging.
30 *
31 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33 */
34class database_logger implements task_logger {
35
36 /** @var int Type constant for a scheduled task */
37 const TYPE_SCHEDULED = 0;
38
39 /** @var int Type constant for an adhoc task */
40 const TYPE_ADHOC = 1;
41
42 /**
43 * Whether the task is configured and ready to log.
44 *
45 * @return bool
46 */
47 public static function is_configured() : bool {
48 return true;
49 }
50
51 /**
52 * Store the log for the specified task.
53 *
54 * @param task_base $task The task that the log belongs to.
55 * @param string $logpath The path to the log on disk
56 * @param bool $failed Whether the task failed
57 * @param int $dbreads The number of DB reads
58 * @param int $dbwrites The number of DB writes
59 * @param float $timestart The start time of the task
60 * @param float $timeend The end time of the task
61 */
62 public static function store_log_for_task(task_base $task, string $logpath, bool $failed,
63 int $dbreads, int $dbwrites, float $timestart, float $timeend) {
64 global $DB;
65
66 // Write this log to the database.
67 $logdata = (object) [
68 'type' => is_a($task, scheduled_task::class) ? self::TYPE_SCHEDULED : self::TYPE_ADHOC,
69 'component' => $task->get_component(),
70 'classname' => get_class($task),
71 'userid' => 0,
72 'timestart' => $timestart,
73 'timeend' => $timeend,
74 'dbreads' => $dbreads,
75 'dbwrites' => $dbwrites,
76 'result' => (int) $failed,
77 'output' => file_get_contents($logpath),
78 ];
79
80 if (is_a($task, adhoc_task::class) && $userid = $task->get_userid()) {
81 $logdata->userid = $userid;
82 }
83
84 $logdata->id = $DB->insert_record('task_log', $logdata);
85 }
86
8c69e86c
AN
87 /**
88 * Whether this task logger has a report available.
89 *
90 * @return bool
91 */
92 public static function has_log_report() : bool {
93 return true;
94 }
95
96 /**
97 * Get any URL available for viewing relevant task log reports.
98 *
99 * @param string $classname The task class to fetch for
100 * @return \moodle_url
101 */
102 public static function get_url_for_task_class(string $classname) : \moodle_url {
103 global $CFG;
104
105 return new \moodle_url("/{$CFG->admin}/tasklogs.php", [
106 'filter' => $classname,
107 ]);
108 }
109
4b71596f
AN
110 /**
111 * Cleanup old task logs.
112 */
113 public static function cleanup() {
114 global $CFG, $DB;
115
116 // Delete logs older than the retention period.
117 $params = [
118 'retentionperiod' => time() - $CFG->task_logretention,
119 ];
120 $logids = $DB->get_fieldset_select('task_log', 'id', 'timestart < :retentionperiod', $params);
121 self::delete_task_logs($logids);
122
123 // Delete logs to retain a minimum number of logs.
124 $sql = "SELECT classname FROM {task_log} GROUP BY classname HAVING COUNT(classname) > :retaincount";
125 $params = [
126 'retaincount' => $CFG->task_logretainruns,
127 ];
128 $classes = $DB->get_fieldset_sql($sql, $params);
129
130 foreach ($classes as $classname) {
131 $params = [
132 'classname' => $classname,
133 ];
134
135 $retaincount = (int) $CFG->task_logretainruns;
136 $keeplogs = $DB->get_fieldset_sql(
137 "SELECT id FROM {task_log} WHERE classname = :classname ORDER BY timestart DESC LIMIT {$retaincount}",
138 $params
139 );
140
141 $notinsql = "";
142 if ($keeplogs) {
143 list($notinsql, $params) = $DB->get_in_or_equal($keeplogs, SQL_PARAMS_NAMED, 'p', false);
144 $params['classname'] = $classname;
145 $notinsql = " AND id {$notinsql}";
146 }
147
148 $logids = $DB->get_fieldset_select('task_log', 'id', "classname = :classname {$notinsql}", $params);
149 self::delete_task_logs($logids);
150 }
151 }
152
153 /**
154 * Delete task logs for the specified logs.
155 *
156 * @param array $logids
157 */
158 public static function delete_task_logs(array $logids) {
159 global $DB;
160
161 if (empty($logids)) {
162 return;
163 }
164
165 $DB->delete_records_list('task_log', 'id', $logids);
166 }
167}