$error = is_array($result['output']) ? implode('', $result['output']) : $result['output'];
$emsg = "ERROR [$component_type] $error";
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
return $ret;
} else {
// It shouldn't happen.
$this->getModule('logging')->log(
- __FUNCTION__,
- "Attemp to update resource with different resource types.",
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ 'Attemp to update resource with different resource types.'
);
$resource = $resource_orig;
}
} else {
$emsg = sprintf("Attemp to format a directive value with not supported value type '%s'.", gettype($value));
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
return $directive_value;
break;
}
}
- } catch(TException $e) {
- $this->getModule('logging')->log(
- __FUNCTION__,
- "Method: {$_SERVER['REQUEST_METHOD']} $e",
- Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
- );
+ } catch (TException $e) {
if ($e instanceof BAPIException) {
$this->output = $e->getErrorMessage();
$this->error = $e->getErrorCode();
} else {
$json = json_encode($output);
}
+ $out = json_encode($output, JSON_PRETTY_PRINT);
+ $this->audit($out);
return $json;
}
echo $this->getOutput();
}
+ /**
+ * Write each request output to audit log.
+ * This method is dedicated for logging requests to API.
+ *
+ * @param string $output output string
+ */
+ private function audit($output) {
+ $username = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '-';
+ $msg = sprintf(
+ "[%s] %s %s\n%s",
+ $_SERVER['REQUEST_METHOD'],
+ $username,
+ $this->Request->getRequestUri(),
+ $output
+ );
+ $this->getModule('logging')->log(
+ Logging::CATEGORY_AUDIT,
+ $msg
+ );
+ }
+
/**
* Shortcut method for getting application modules instances by
* module name.
$is_valid = false;
$emsg = 'Invalid Basic user config. Missing ' . $this->required_options[$i] . ' option.';
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
break;
}
private function execCommand($director, array $command, $ptype = null) {
$cmd = '';
$result = null;
+ $output = [];
if(!is_null($director) && $this->isValidDirector($director) === false) {
throw new BConsoleException(
BconsoleError::MSG_ERROR_INVALID_DIRECTOR,
$cmd = $this->getCommand($pattern, $sudo, $dir, $bconsole_command);
exec($cmd['cmd'], $output, $exitcode);
if($exitcode != 0) {
- $emsg = ' Output=>' . implode("\n", $output) . ', Exitcode=>' . $exitcode;
+ $emsg = ' Output=>' . implode(PHP_EOL, $output) . ', Exitcode=>' . $exitcode;
throw new BConsoleException(
BconsoleError::MSG_ERROR_BCONSOLE_CONNECTION_PROBLEM . $emsg,
BconsoleError::ERROR_BCONSOLE_CONNECTION_PROBLEM
}
}
$this->Application->getModule('logging')->log(
- $cmd['cmd'],
- $output,
Logging::CATEGORY_EXECUTE,
- __FILE__,
- __LINE__
+ Logging::prepareOutput($cmd['cmd'], $output)
);
return $result;
public function execCommand($cmd, $ptype = null) {
exec($cmd['cmd'], $output, $exitcode);
$this->getModule('logging')->log(
- $cmd['cmd'],
- $output,
Logging::CATEGORY_EXECUTE,
- __FILE__,
- __LINE__
+ Logging::prepareOutput($cmd['cmd'], $output)
);
if ($ptype === self::PTYPE_BG_CMD) {
$output = [
$cmd_pattern = $this->getCmdPattern();
$cmd = sprintf($cmd_pattern, $sudo, $bin);
exec($cmd, $output, $exitcode);
- $this->getModule('logging')->log($cmd, $output, Logging::CATEGORY_EXECUTE, __FILE__, __LINE__);
+ $this->getModule('logging')->log(
+ Logging::CATEGORY_EXECUTE,
+ Logging::prepareOutput($cmd, $output)
+ );
$result = $this->prepareResult($output, $exitcode);
return $result;
}
$logmsg = 'DBParams=%s, Connection=%s, TablesFormat=%s';
$msg = sprintf($logmsg, print_r($db_params, true), var_export($is_connection, true), var_export($tables_format, true));
$this->getModule('logging')->log(
- __FUNCTION__,
- $msg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $msg
);
return $is_connection;
}
$output_txt = implode('', $output);
$out = json_decode($output_txt, true);
if (!is_array($out)) {
- $this->getModule('logging')->log('Parse output', $output_txt, Logging::CATEGORY_EXTERNAL, __FILE__, __LINE__);
+ $this->getModule('logging')->log(
+ Logging::CATEGORY_EXTERNAL,
+ "Parse output: $output_txt"
+ );
$out = null;
}
return $out;
}
$cmd = sprintf($cmd_pattern, $sudo, $bin, $cfg, $options);
exec($cmd, $output, $exitcode);
- $this->getModule('logging')->log($cmd, $output, Logging::CATEGORY_EXECUTE, __FILE__, __LINE__);
+ $this->getModule('logging')->log(
+ Logging::CATEGORY_EXECUTE,
+ Logging::prepareOutput($cmd, $output)
+ );
if (!empty($config)) {
unlink($cfg);
if ($exitcode === 0) {
$is_valid = false;
$emsg = 'Invalid OAuth2 config. Missing ' . $this->required_options[$i] . ' option.';
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
break;
}
<module id="device_config" class="Baculum\API\Modules\DeviceConfig" />
<!-- logging modules -->
<module id="log" class="System\Util\TLogRouter">
- <route class="TFileLogRoute" Categories="Execute, External, Application, General, Security" LogPath="Baculum\API\Logs" LogFile="baculum-api.log" MaxFileSize="1000" MaxLogFiles="5" />
+ <route class="Baculum\Common\Modules\BFileLogRoute" Categories="Execute, External, Application, General, Security" LogPath="Baculum\API\Logs" LogFile="baculum-api-debug.log" MaxFileSize="10000" MaxLogFiles="5" />
+ <route class="Baculum\Common\Modules\BFileLogRoute" Categories="Audit" LogPath="Baculum\API\Logs" LogFile="baculum-api.log" MaxFileSize="10000" MaxLogFiles="5" />
</module>
<!-- component status modules -->
<module id="status_dir" class="Baculum\API\Modules\StatusDirector" />
<module id="oauth2_token" class="Baculum\API\Modules\OAuth2\TokenManager" />
<!-- config modules -->
<module id="api_config" class="Baculum\API\Modules\APIConfig" />
+ <!-- logging modules -->
+ <module id="log" class="System\Util\TLogRouter">
+ <route class="Baculum\Common\Modules\BFileLogRoute" Categories="Execute, External, Application, General, Security" LogPath="Baculum\API\Logs" LogFile="baculum-api-debug.log" MaxFileSize="10000" MaxLogFiles="5" />
+ <route class="Baculum\Common\Modules\BFileLogRoute" Categories="Audit" LogPath="Baculum\API\Logs" LogFile="baculum-api.log" MaxFileSize="10000" MaxLogFiles="5" />
+ </module>
</modules>
</configuration>
</module>
<!-- logging modules -->
<module id="log" class="System\Util\TLogRouter">
- <route class="TFileLogRoute" Categories="Execute, External, Application, General, Security" LogPath="Baculum\API\Logs" LogFile="baculum-api.log" MaxFileSize="1000" MaxLogFiles="5" />
+ <route class="Baculum\Common\Modules\BFileLogRoute" Categories="Execute, External, Application, General, Security" LogPath="Baculum\API\Logs" LogFile="baculum-api-debug.log" MaxFileSize="10000" MaxLogFiles="5" />
+ <route class="Baculum\Common\Modules\BFileLogRoute" Categories="Audit" LogPath="Baculum\API\Logs" LogFile="baculum-api.log" MaxFileSize="10000" MaxLogFiles="5" />
</module>
<!-- auth modules -->
<module id="basic_apiuser" class="Baculum\API\Modules\BasicAPIUserConfig" />
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2022 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+namespace Baculum\Common\Modules;
+
+use Prado\Util\TFileLogRoute;
+
+/**
+ * File log route class.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class BFileLogRoute extends TFileLogRoute {
+
+ protected function formatLogMessage($message, $level, $category, $time) {
+ $t = date('Y-m-d H:i:s');
+ $c = sprintf('[%s]', $category);
+ return join(
+ ' ',
+ [$t, $c, $message]
+ );
+ }
+}
}
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
return $valid;
);
$this->error = $emsg;
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_EXTERNAL,
- __FILE__,
- __LINE__
+ $emsg
);
}
namespace Baculum\Common\Modules;
use Prado\Prado;
+use Prado\Util\TLogger;
/**
- * Logger class.
+ * Main logger class.
*
* @author Marcin Haba <marcin.haba@bacula.pl>
* @category Module
*/
class Logging extends CommonModule {
+ /*
+ * Stores debug enable state.
+ *
+ * @var bool
+ */
public static $debug_enabled = false;
+ /**
+ * Log categories.
+ */
const CATEGORY_EXECUTE = 'Execute';
const CATEGORY_EXTERNAL = 'External';
const CATEGORY_APPLICATION = 'Application';
const CATEGORY_GENERAL = 'General';
const CATEGORY_SECURITY = 'Security';
+ const CATEGORY_AUDIT = 'Audit';
- private function getLogCategories() {
- $categories = array(
- self::CATEGORY_EXECUTE,
- self::CATEGORY_EXTERNAL,
- self::CATEGORY_APPLICATION,
- self::CATEGORY_GENERAL,
- self::CATEGORY_SECURITY
- );
- return $categories;
- }
-
- public function log($cmd, $output, $category, $file, $line) {
- if(self::$debug_enabled !== true) {
+ /**
+ * Main log method used to log message.
+ *
+ * @param string $category log category
+ */
+ public function log($category, $message) {
+ if (!$this->isEnabled($category)) {
return;
}
- $current_mode = $this->Application->getMode();
+ $this->prepareMessage($category, $message);
+ Prado::log($message, TLogger::INFO, $category);
+ }
- // switch application to debug mode
- $this->Application->setMode('Debug');
+ /**
+ * Check if log is enabled.
+ *
+ * @param string $category log category
+ * @return bool true if log is enabled, otherwise false
+ */
+ private function isEnabled($category) {
+ $is_enabled = false;
+ if (self::$debug_enabled === true || $category === self::CATEGORY_AUDIT) {
+ // NOTE: Audit log is written always, it is not possible to disable it
+ $is_enabled = true;
+ }
+ return $is_enabled;
+ }
- if(!in_array($category, $this->getLogCategories())) {
- $category = self::CATEGORY_SECURITY;
+ /**
+ * Prepare log to send to log manager.
+ *
+ * @param string $category log category
+ * @param array|string|object &$message log message reference
+ */
+ private function prepareMessage($category, &$message) {
+ if (is_object($message) || is_array($message)) {
+ // make a message as string if needed
+ $message = print_r($message, true);
}
+ if (self::$debug_enabled === true && $category !== self::CATEGORY_AUDIT) {
+ // If debug enabled, add file and line to log message
+ $f = '';
+ $trace = debug_backtrace();
+ if (isset($trace[1]['file']) && isset($trace[1]['line'])) {
+ $f = sprintf(
+ '%s:%s:',
+ basename($trace[1]['file']),
+ $trace[1]['line']
+ );
+ }
+ $message = $f . $message;
+ }
+ $message .= PHP_EOL . PHP_EOL;
+ }
- $log = sprintf(
- 'Command=%s, Output=%s, File=%s, Line=%d',
+ /**
+ * Helper method for preparing logs that come from executing programs or scripts.
+ * This log consists of command and output.
+ * Useful for bconsole, b*json... and others.
+ *
+ * @param string $cmd command
+ * @param array|object|string $output command output
+ * @return string formatted command log
+ */
+ public static function prepareOutput($cmd, $output) {
+ if (is_array($output)) {
+ $output = implode(PHP_EOL, $output);
+ } elseif(is_object($output)) {
+ $output = print_r($output, true);
+ }
+ return sprintf(
+ "\n\n===> Command:\n\n%s\n\n===> Output:\n\n%s",
$cmd,
- print_r($output, true),
- $file,
- intval($line)
+ $output
);
-
- Prado::trace($log, $category);
-
- // switch back application to original mode
- $this->Application->setMode($current_mode);
}
}
-
-?>
} else {
$emsg = 'Unable to exclusive lock ' . $sessfile;
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
fclose($fp);
} else {
$emsg = 'Unable to shared lock ' . $sessfile;
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
fclose($fp);
$this->addSpecialParams($uri);
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- PHP_EOL . PHP_EOL . 'EXECUTE URI ==> ' . $uri . ' <==' . PHP_EOL . PHP_EOL,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ 'REQUEST URI => ' . $uri
);
}
return $uri;
private function preParseOutput($result, $error, $errno, $show_error = true) {
// first write log with that what comes
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $result,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $result
);
// decode JSON to object
}
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $resource,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $resource
);
return $resource;
$data_deps = json_decode($deps_file);
} else {
$emsg = "Data dependencies file '$deps_file' does not exist or is not readable.";
- $this->Application->getModule('logging')->log(__FUNCTION__, $emsg, Logging::CATEGORY_APPLICATION, __FILE__, __LINE__);
+ $this->Application->getModule('logging')->log(
+ Logging::CATEGORY_APPLICATION,
+ $emsg
+ );
}
return $data_deps;
}
$emsg = "ERROR [$path] Internal error";
}
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
return $valid;
} else {
$emsg = 'Could not get the exclusive lock: ' . $f;
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
fclose($fp);
} else {
$emsg = 'Could not get the exclusive lock: ' . $f;
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
fclose($fp);
} else {
$emsg = 'Could not get the exclusive lock: ' . $f;
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
fclose($fp);
} else {
$emsg = 'Could not get the shared lock: ' . $f;
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
fclose($fp);
if ($ret !== true) {
$emsg = 'Error while saving auth basic config.';
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
return $ret;
for ($i = 0; $i < $required_len; $i++) {
$emsg = "ERROR [$path] Required {$invalid['required'][$i]['type']} '{$invalid['required'][$i]['value']}' not found for role '{$invalid['required'][$i]['role']}.";
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
}
$ret = false;
$emsg = 'Error while importing basic users.';
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
return $ret;
} else {
$emsg = 'Basic auth is enabled, but PHP_AUTH_USER is not set or is empty.';
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_SECURITY,
- __FILE__,
- __LINE__
+ $emsg
);
}
}
if (!key_exists($params['user_attr'], $users[$i])) {
$emsg = "User attribute '{$params['user_attr']}' doesn't exist in LDAP response.";
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_EXTERNAL,
- __FILE__,
- __LINE__
+ $emsg
);
continue;
}
if ($params['user_attr'] !== Ldap::DN_ATTR && $users[$i][$params['user_attr']]['count'] != 1) {
$emsg = "Invalid user attribute count for '{$params['user_attr']}'. Is {$users[$i][$params['user_attr']]['count']}, should be 1.";
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_EXTERNAL,
- __FILE__,
- __LINE__
+ $emsg
);
continue;
} else {
$emsg = 'Error while saving basic user config.';
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
if (!$ret) {
$emsg = 'Error while saving user config.';
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
} else {
$emsg = 'Error while saving auth host config.';
$this->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
}
<translation type="gettext" source="Baculum\Web\Lang" marker="@@" autosave="false" cache="false" DefaultCulture="en" />
</module>
<module id="log" class="System\Util\TLogRouter">
- <route class="TFileLogRoute" Categories="Execute, External, Application, General, Security" LogPath="Baculum\Web\Logs" LogFile="baculum-web.log" MaxFileSize="1000" MaxLogFiles="5" />
+ <route class="Baculum\Common\Modules\BFileLogRoute" Categories="Execute, External, Application, General, Security" LogPath="Baculum\Web\Logs" LogFile="baculum-web-debug.log" MaxFileSize="10000" MaxLogFiles="5" />
</module>
<module id="log_parser" class="Baculum\Web\Modules\LogParser" />
<!-- auth modules -->
$success = false;
$emsg = 'Error while renaming resource: ' . $result->output;
$this->Application->getModule('logging')->log(
- __FUNCTION__,
- $emsg,
Logging::CATEGORY_APPLICATION,
- __FILE__,
- __LINE__
+ $emsg
);
}
return $success;