Skip to content

Commit

Permalink
System - Log Rotation plugin (#61)
Browse files Browse the repository at this point in the history
* System - Log Rotation plugin

* Fix merge conflict

* Use strict Comparison

* Correct language items. Thanks @brianteeman
  • Loading branch information
joomdonation authored and mbabker committed Jun 12, 2018
1 parent df9c85b commit ab65c60
Show file tree
Hide file tree
Showing 10 changed files with 352 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
INSERT INTO `#__extensions` (`extension_id`, `package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `manifest_cache`, `params`, `custom_data`, `system_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES
(486, 0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, '', '{"cachetimeout":"30","logstokeep":"1","lastrun":0}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(487, 0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0);
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "manifest_cache", "params", "custom_data", "system_data", "checked_out", "checked_out_time", "ordering", "state") VALUES
(486, 0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 0, 1, 0, '', '{"cachetimeout":"30","logstokeep":"1","lastrun":0}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(487, 0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0);
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "manifest_cache", "params", "custom_data", "system_data", "checked_out", "checked_out_time", "ordering", "state") VALUES
(486, 0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, '', '{"cachetimeout":"30","logstokeep":"1","lastrun":0}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(487, 0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0);
11 changes: 11 additions & 0 deletions administrator/language/en-GB/en-GB.plg_system_logrotation.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
; Joomla! Project
; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
; Note : All ini files need to be saved as UTF-8

PLG_SYSTEM_LOGROTATION="System - Log Rotation"
PLG_SYSTEM_LOGROTATION_XML_DESCRIPTION="This plugin periodically rotates system log files."
PLG_SYSTEM_LOGROTATION_CACHETIMEOUT_LABEL="Log Rotation (in days)"
PLG_SYSTEM_LOGROTATION_CACHETIMEOUT_DESC="How often should the logs be rotated."
PLG_SYSTEM_LOGROTATION_LOGSTOKEEP_DESC="The maximum number of old logs to keep."
PLG_SYSTEM_LOGROTATION_LOGSTOKEEP_LABEL="Maximum Logs"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
; Joomla! Project
; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
; Note : All ini files need to be saved as UTF-8

PLG_SYSTEM_LOGROTATION="System - Log Rotation"
PLG_SYSTEM_LOGROTATION_XML_DESCRIPTION="This plugin periodically rotates system log files."
1 change: 1 addition & 0 deletions installation/sql/mysql/joomla.sql
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ INSERT INTO `#__extensions` (`extension_id`, `package_id`, `name`, `type`, `elem
(483, 0, 'plg_system_actionlogs', 'plugin', 'actionlogs', 'system', 0, 1, 1, 0, '', '{"logDeletePeriod":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(484, 0, 'plg_actionlog_joomla', 'plugin', 'joomla', 'actionlog', 0, 1, 1, 0, '', '{}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(485, 0, 'plg_system_privacyconsent', 'plugin', 'privacyconsent', 'system', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(486, 0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, '', '{"cachetimeout":"30","logstokeep":"1","lastrun":0}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(487, 0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(503, 0, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(504, 0, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
Expand Down
1 change: 1 addition & 0 deletions installation/sql/postgresql/joomla.sql
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "elem
(483, 0, 'plg_system_actionlogs', 'plugin', 'actionlogs', 'system', 0, 1, 1, 0, '', '{"logDeletePeriod":"0"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(484, 0, 'plg_actionlog_joomla', 'plugin', 'joomla', 'actionlog', 0, 1, 1, 0, '', '{}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(485, 0, 'plg_system_privacyconsent', 'plugin', 'privacyconsent', 'system', 0, 0, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(486, 0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 0, 1, 0, '', '{"cachetimeout":"30","logstokeep":"1","lastrun":0}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(487, 0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(503, 0, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(504, 0, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
Expand Down
1 change: 1 addition & 0 deletions installation/sql/sqlazure/joomla.sql
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,7 @@ INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "elem
(483, 0, 'plg_system_actionlogs', 'plugin', 'actionlogs', 'system', 0, 1, 1, 0, '', '{"logDeletePeriod":"0","ip_logging":"1","loggable_extensions":["com_banners","com_cache","com_categories","com_config","com_contact","com_content","com_installer","com_media","com_menus","com_messages","com_modules","com_newsfeeds","com_plugins","com_redirect","com_tags","com_templates","com_users"]}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(484, 0, 'plg_actionlogs_joomla', 'plugin', 'joomla', 'actionlogs', 0, 1, 1, 0, '', '{}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(485, 0, 'plg_system_privacyconsent', 'plugin', 'privacyconsent', 'system', 0, 0, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(486, 0, 'plg_system_logrotation', 'plugin', 'logrotation', 'system', 0, 1, 1, 0, '', '{"cachetimeout":"30","logstokeep":"1","lastrun":0}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(487, 0, 'plg_privacy_user', 'plugin', 'user', 'privacy', 0, 1, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(503, 0, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(504, 0, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
Expand Down
275 changes: 275 additions & 0 deletions plugins/system/logrotation/logrotation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage System.logrotation
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

defined('_JEXEC') or die;

use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Joomla\Filesystem\Path;

/**
* Joomla! Log Rotation plugin
*
* Rotate the log files created by Joomla core
*
* @since __DEPLOY_VERSION__
*/
class PlgSystemLogrotation extends JPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
* @since __DEPLOY_VERSION__
*/
protected $autoloadLanguage = true;

/**
* Application object.
*
* @var JApplicationCms
* @since __DEPLOY_VERSION__
*/
protected $app;

/**
* Database object.
*
* @var JDatabaseDriver
* @since __DEPLOY_VERSION__
*/
protected $db;

/**
* The log check and rotation code is triggered after the page has fully rendered.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function onAfterRender()
{
// Get the timeout as configured in plugin parameters

/** @var \Joomla\Registry\Registry $params */
$cache_timeout = (int) $this->params->get('cachetimeout', 30);
$cache_timeout = 24 * 3600 * $cache_timeout;
$logsToKeep = (int) $this->params->get('logstokeep', 1);

// Do we need to run? Compare the last run timestamp stored in the plugin's options with the current
// timestamp. If the difference is greater than the cache timeout we shall not execute again.
$now = time();
$last = (int) $this->params->get('lastrun', 0);

if ((abs($now - $last) < $cache_timeout))
{
return;
}

// Update last run status
$this->params->set('lastrun', $now);

$db = $this->db;
$query = $db->getQuery(true)
->update($db->qn('#__extensions'))
->set($db->qn('params') . ' = ' . $db->q($this->params->toString('JSON')))
->where($db->qn('type') . ' = ' . $db->q('plugin'))
->where($db->qn('folder') . ' = ' . $db->q('system'))
->where($db->qn('element') . ' = ' . $db->q('logrotation'));

try
{
// Lock the tables to prevent multiple plugin executions causing a race condition
$db->lockTable('#__extensions');
}
catch (Exception $e)
{
// If we can't lock the tables it's too risky to continue execution
return;
}

try
{
// Update the plugin parameters
$result = $db->setQuery($query)->execute();

$this->clearCacheGroups(array('com_plugins'), array(0, 1));
}
catch (Exception $exc)
{
// If we failed to execite
$db->unlockTables();
$result = false;
}

try
{
// Unlock the tables after writing
$db->unlockTables();
}
catch (Exception $e)
{
// If we can't lock the tables assume we have somehow failed
$result = false;
}

// Abort on failure
if (!$result)
{
return;
}

// Get the log path
$logPath = Path::clean($this->app->get('log_path'));

// Invalid path, stop processing further
if (!is_dir($logPath))
{
return;
}

$logFiles = $this->getLogFiles($logPath);


// Sort log files by version number in reserve order
krsort($logFiles, SORT_NUMERIC);

foreach ($logFiles as $version => $files)
{
if ($version >= $logsToKeep)
{
// Delete files which has version greater than or equals $logsToKeep
foreach ($files as $file)
{
File::delete($logPath . '/' . $file);
}
}
else
{
// For files which has version smaller than $logsToKeep, rotate (increase version number)
foreach ($files as $file)
{
$this->rotate($logPath, $file, $version);
}
}
}
}

/**
* Get log files from log folder
*
* @param string $path The folder to get log files
*
* @return array The log files in the given path grouped by version number (not rotated files has number 0)
*
* @since __DEPLOY_VERSION__
*/
private function getLogFiles($path)
{
$logFiles = array();
$files = Folder::files($path, '\.php$');

foreach ($files as $file)
{
$parts = explode('.', $file);

/*
* Rotated log file has this filename format [VERSION].[FILENAME].php. So if $parts has at least 3 elements
* and the first element is a number, we know that it's a rotated file and can get it's current version
*/
if (count($parts) >= 3 && is_numeric($parts[0]))
{
$version = (int) $parts[0];
}
else
{
$version = 0;
}

if (!isset($logFiles[$version]))
{
$logFiles[$version] = array();
}

$logFiles[$version][] = $file;
}

return $logFiles;
}

/**
* Method to rotate (increase version) of a log file
*
* @param string $path Path to file to rotate
* @param string $filename Name of file to rotate
* @param int $currentVersion The current version number
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
private function rotate($path, $filename, $currentVersion)
{
if ($currentVersion === 0)
{
$rotatedFile = $path . '/1.' . $filename;
}
else
{
/*
* Rotated log file has this filename format [VERSION].[FILENAME].php. To rotate it, we just need to explode
* the filename into an array, increase value of first element (keep version) and implode it back to get the
* rotated file name
*/
$parts = explode('.', $filename);
$parts[0] = $currentVersion + 1;

$rotatedFile = $path . '/' . implode('.', $parts);
}

File::move($path . '/' . $filename, $rotatedFile);
}

/**
* Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp.
*
* @param array $clearGroups The cache groups to clean
* @param array $cacheClients The cache clients (site, admin) to clean
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
private function clearCacheGroups(array $clearGroups, array $cacheClients = array(0, 1))
{
$conf = JFactory::getConfig();

foreach ($clearGroups as $group)
{
foreach ($cacheClients as $client_id)
{
try
{
$options = array(
'defaultgroup' => $group,
'cachebase' => $client_id ? JPATH_ADMINISTRATOR . '/cache' :
$conf->get('cache_path', JPATH_SITE . '/cache')
);

$cache = JCache::getInstance('callback', $options);
$cache->clean();
}
catch (Exception $e)
{
// Ignore it
}
}
}
}
}
53 changes: 53 additions & 0 deletions plugins/system/logrotation/logrotation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<extension version="3.9" type="plugin" group="system" method="upgrade">
<name>plg_system_logrotation</name>
<author>Joomla! Project</author>
<creationDate>May 2018</creationDate>
<copyright>Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.9.0</version>
<description>PLG_SYSTEM_LOGROTATION_XML_DESCRIPTION</description>
<files>
<filename plugin="logrotation">logrotation.php</filename>
</files>
<languages folder="language">
<language tag="en-GB">en-GB.plg_system_logrotation.ini</language>
<language tag="en-GB">en-GB.plg_system_logrotation.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="cachetimeout"
type="integer"
label="PLG_SYSTEM_LOGROTATION_CACHETIMEOUT_LABEL"
description="PLG_SYSTEM_LOGROTATION_CACHETIMEOUT_DESC"
first="0"
last="120"
step="1"
default="30"
/>

<field
name="logstokeep"
type="integer"
label="PLG_SYSTEM_LOGROTATION_LOGSTOKEEP_LABEL"
description="PLG_SYSTEM_LOGROTATION_LOGSTOKEEP_DESC"
first="1"
last="10"
step="1"
default="1"
/>

<field
name="lastrun"
type="hidden"
default="0"
size="15"
/>
</fieldset>
</fields>
</config>
</extension>

0 comments on commit ab65c60

Please sign in to comment.