Files
namaste/classes/gacErrorLogger.class.inc
gramps 373ebc8c93 Archive: Namaste PHP AMQP framework v1.0 (2017-2020)
952 days continuous production uptime, 40k+ tp/s single node.
Original corpo Bitbucket history not included — clean archive commit.
2026-04-05 09:49:30 -07:00

817 lines
36 KiB
PHP

<?php
use MongoDB\Driver\WriteResult;
class gacErrorLogger {
public bool $haveErrors; // true if we have ERROR errors
public bool $haveWarnings; // true if we have WARN errors
public bool $haveFatals; // true if we have FATAL errors
public array $errStack; // the error stack array
public bool $available = false; // boolean indicating service availability
public bool $mirror = false; // mirror db log messages to console?
public string $status; // dynamic progress indicator
private string $service = ''; // defines which service the current instantiation is current running on
private ?string $eventGUID = ''; // recording the broker event identifier
private bool $isMetric = false; // indicates if this is a metrics (as opposed to a log) message
private string $res = 'LOGR: '; // console-log label
private bool $publishMessage; // do we publish the message remotely or write locally?
private string $ext = ''; // class extension (_log or _met)
private string $class = ''; // class name of the current collection
private array $config; // holds copy of the pgsConfig for mongo
private ?object $connection; // mongo DB Driver connection resource
private string $dbName; // name of the mongodb
private string $collectionName = ''; // mongo table name (log end-point)
private array $validTemplates; // names of the collections
private string $env = ''; // defines the environment for ddb table names
private gacLogClient $abc; // Admin Broker Client -- pointer to the admin broker client class
public function __construct(string $_eg = null, bool $_pm = true)
{
$this->status = false;
try {
register_shutdown_function(array($this, '__destruct'));
$this->errStack = [];
$this->haveErrors = false;
$this->haveWarnings = false;
$this->haveFatals = false;
$this->publishMessage = $_pm;
$this->eventGUID = (!is_null($_eg) and validateGUID($_eg)) ? $_eg : null;
$this->validTemplates = [TEMPLATE_CLASS_LOGS, TEMPLATE_CLASS_METRICS];
// toss a fatal if the config hasn't been instantiated...
if (!isset(gasConfig::$settings) or empty(gasConfig::$settings)) {
$this->throwFatal(__FILE__ . '(' . __LINE__ . '): ' . ERROR_CONFIG_404);
} elseif (empty(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_MONGODB])) {
$this->throwFatal(__FILE__ . '(' . __LINE__ . '): ' . ERROR_CONFIG_RESOURCE_404 . RESOURCE_DDB);
}
// $this->config = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_DDB];
// $this->connection = $this->getNoSQLResource(); // ddb connection
$this->config = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_MONGODB];
$this->connection = gasResourceManager::fetchResource(RESOURCE_MONGO_MASTER, ENV_ADMIN);
$this->available = (!is_null($this->connection)) ? true : false;
$this->env = gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV];
$this->dbName = $this->env . UDASH . $this->config[CONFIG_DATABASE_MONGODB_ADMIN][CONFIG_DATABASE_MONGODB_DB_NAME];
// $this->abc = null;
$this->status = true;
} catch (TypeError | Throwable $e) {
$hdr = basename(__METHOD__) . AT . __LINE__ . COLON;
echo getDateTime() . CON_ERROR . $this->res . $hdr . $e->getMessage();
}
}
/**
* setService() -- public method
*
* When any class that extends core creates an embedded logger class, which they all should, then the instantiation
* class should call this function s.t. the logger class inherits (sloppily) the service defined for the current
* class. This will validated later in the function: isServiceLocal() when we're instantiating a class to ensure
* that a class can only be instantiated in it's service when we're dealing with appServer and tercero classes.
*
* There is a single input parameter to the function that is the defined name of the current service. We're not
* validating this string value b/c that's happened already within the "parent" class.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_service
* @return bool
*
* HISTORY:
* ========
* 11-04-20 mks DB-171: original coding
*
*/
public function setService(string $_service):bool
{
if (isset(gasConfig::$settings[$_service]))
if (gasConfig::$settings[$_service][CONFIG_IS_LOCAL])
$this->service = $_service;
else {
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__);
$msg = sprintf(ERROR_SERVICE_NOT_LOCAL, $_service);
consoleLog('SSER: ', CON_ERROR, $hdr . $msg);
return false;
}
else {
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __FILE__);
$msg = ERROR_CONFIG_RESOURCE_404 . STRING_SERVICE;
consoleLog('SSER: ', CON_ERROR, $hdr . $msg);
return false;
}
return true;
}
/**
* throwFatal() -- private method
*
* if the logger cannot be properly instantiated, (thereby being unable to log errors in the database), the the
* last remaining option is to throw a direMessage (tm) to the console.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_msg -- the text dumped to the php-errors log
* @throws Exception
*
* HISTORY:
* ========
* 06-07-17 mks original coding
* 12-07-17 mks CORE-591: removed errStack dump b/c cached and can be recovered
* 01-22-18 mks _INF-139: updated code to php 7.2
* 03-02-18 mks CORE-680: deprecated trace logging
*
*/
private function throwFatal(string $_msg): void
{
$msg = PHP_EOL . '----------------------------------------------------------------------------------------------------' . PHP_EOL;
$msg .= ' FATAL ERRORS OCCURRED IN THE ERROR CLASS. EVALUATE, FIX, AND RE-RUN.' . PHP_EOL;
$msg .= PHP_EOL . $_msg . PHP_EOL;
$msg .= '----------------------------------------------------------------------------------------------------' . PHP_EOL;
throw new Exception($msg);
}
public function debug(string $_message): void
{
try {
self::set(ERROR_DEBUG, $_message);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
}
}
public function info(string $_message): void
{
try {
self::set(ERROR_INFO, $_message);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
}
}
public function data(string $_message): void
{
try {
self::set(ERROR_DATA, $_message);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
}
}
public function error(string $_message): void
{
try {
self::set(ERROR_ERROR, $_message);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
}
}
public function warn(string $_message): void
{
try {
self::set(ERROR_WARN, $_message);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
}
}
public function fatal(string $_message): void
{
try {
self::set(ERROR_FATAL, $_message);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
}
}
public function metrics(string $_message, float $_time, &$_es = null): void
{
try {
self::set(ERROR_METRICS, $_message, true, $_time, $_es);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
}
}
/**
* set() -- public method
*
* this method is where most of the work gets done for the class.
*
* this method accepts a log level and a log message. if AMQP and nosql services are not available (either),
* then the message will be output to the console.
*
* otherwise, we're going to parse the log level against the configuration settings to ensure that we're
* publishing messages according to the settings and the current environment. (iow, don't publish debug level
* messages while in a production environment)
*
* RULES:
* ------
* DEBUG errors are only logged if debug mode is on
*
* we use debug_backtrace to generate the originating message data points and assemble the message data
* (and the meta data for the broker request) into a single array which is then processed and published
* to the delayed-write queue (itself a fire-and-forget queue).
*
* this design differs from previous iterations in that we're no longer trying to preserve an ongoing error
* stack - instead we're just considering the incoming message to be the only message currently in existence.
*
* The input parameters to the method are:
*
* $_level - which is the message level and a defined constant
* $_message - the text of the actual message
* $_metrics - (optional, default = false) if true, use the metrics template instead of the logs template
* $_t - (optional, default = 0), for metrics, the total query time
* $_es - (optional, default = 0), call-by-reference param to store/return the metrics error stack
*
* There are no return values, either implicitly or explicitly.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_level error level
* @param string $_message error message string
* @param bool $_metrics defines the event as a metric (instead of an error) event
* @param float $_t time, in ms
* @param array $_es error stack: call by reference array stack/container
* @throws Exception
*
*
* HISTORY:
* ========
* 06-07-17 mks original coding
* 07-12-17 mks added log-level values to collection/processing
* 12-08-17 mks CORE-591: new throw-fatal processing
* 03-02-18 mks CORE-680: deprecated trace logging
* 06-08-18 mks CORE-1034: deprecating XML prodBox tag
* 07-30-18 mks CORE-774: PHP7.2 exception handling update
* 09-24-19 mks DB-136: fixed error in levelValues element assignment to correct constant
* 11-04-20 mks DB-171: added service (origin) definition to logging output
*
*/
private function set(string $_level, string $_message, bool $_metrics = false, float $_t = 0, array &$_es = null): void
{
$this->errStack = [];
$levelValues = [
ERROR_EVENT => ERROR_EVENT_VAL,
ERROR_METRICS => ERROR_METRICS_VAL,
ERROR_DEBUG => ERROR_DEBUG_VAL,
ERROR_DATA => ERROR_DATA_VAL,
ERROR_INFO => ERROR_INFO_VAL,
ERROR_ERROR => ERROR_ERROR_VAL,
ERROR_WARN => ERROR_WARN_VAL,
ERROR_FATAL => ERROR_FATAL_VAL
];
if (!array_key_exists($_level, $levelValues)) {
$this->throwFatal(__FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UNKNOWN_KEY, $_level, LOG_VALUE));
$_level = ERROR_ERROR;
}
// the system is up - so create the error message request for publication to the DW broker
$_message = empty($_message) ? ERROR_DATA_INPUT_EMPTY : trim($_message);
switch ($_level) {
case ERROR_DEBUG :
$saveError = gasConfig::$settings[CONFIG_DEBUG] and !(gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] == ENV_PRODUCTION);
break;
case ERROR_DATA :
case ERROR_INFO :
case ERROR_METRICS :
$saveError = true;
break;
case ERROR_ERROR :
$this->haveErrors = true;
$saveError = true;
break;
case ERROR_WARN :
$this->haveWarnings = true;
$saveError = true;
break;
case ERROR_FATAL :
$this->haveFatals = true;
$saveError = true;
break;
default :
$saveError = true;
$_level = ERROR_INFO;
break;
}
if (!$saveError) return;
try {
if ($_level == ERROR_METRICS) {
$this->isMetric = true;
$this->setCollection(TEMPLATE_CLASS_METRICS);
} else {
$this->setCollection();
}
} catch (Throwable $t) {
consoleLog($this->res, CON_ERROR, ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage());
}
if (is_array($_message)) {
$_message = implode('<br />', $_message);
}
$backTrace = debug_backtrace();
for ($index = 0; $index < 3; $index++) {
if (isset($backTrace[$index][STRING_FUNCTION]) and $backTrace[$index][STRING_FUNCTION] != STRING_SET)
break;
}
// if (isset($backTrace[2][ERROR_FILE])) {
// $index = 2;
// } elseif (isset($backTrace[1][ERROR_FILE])) {
// $index = 1;
// } else {
// $index = 0;
// }
$errStack = null;
if (empty($this->errStack)) $this->errStack = array();
if ($_level == ERROR_METRICS) @$errStack[(LOG_EVENT . $this->ext)] = EVENT_METRICS;
@$errStack[(LOG_FILE . $this->ext)] = $this->service . ARROW . $backTrace[$index][ERROR_FILE];
@$errStack[(LOG_LINE . $this->ext)] = $backTrace[$index][ERROR_LINE];
@$errStack[(LOG_METHOD . $this->ext)] = $backTrace[$index+1][STRING_FUNCTION];
@$errStack[(LOG_CLASS . $this->ext)] = $backTrace[$index+1][ERROR_CLASS];
@$errStack[(LOG_LEVEL . $this->ext)] = $_level;
@$errStack[(LOG_VALUE . $this->ext)] = $levelValues[$_level];
@$errStack[(LOG_MESSAGE . $this->ext)] = utf8_encode($_message);
if (isset($this->eventGUID))
@$errStack[(DB_EVENT_GUID . $this->ext)] = $this->eventGUID;
if ($_metrics) {
@$errStack[(LOG_TIMER . $this->ext)] = $_t;
$_es = $backTrace;
}
$errStack[(LOG_CREATED . $this->ext)] = time();
array_push($this->errStack, $errStack);
try {
$this->writeLogMessage();
if ($this->mirror) {
consoleLog($this->res, CON_ERROR, $_message);
}
} catch (Throwable $t) {
consoleLog($this->res, CON_ERROR, ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage());
}
}
/**
* writeLogMessage() -- public static method
*
* this is the record-create method for saving a log event.
*
* There are three input parameters to the method, all of which are required:
*
* $_data -- the data payload as received by the broker in associative array format
* $_meta -- the meta payload as received by the broker in associative array format
* $_where -- string (use the system constant, please) that defines the collection destination
*
* first thing we do is set the collection destination by passing the $_where input parameter to the
* private setCollection() method.
*
* if we (self) are not available, then return immediately where the calling client should assume that the
* logging services are not available and post the necessary console message.
*
* Validate the $_data payload by assigning the contents, filtering the payload through a validation array,
* into a temp variable...
*
* next, insert the generated sequence key into the temp array structure and add the created-date timestamp...
*
* finally, insert the record into the designated collection, trapping any nosql exception raised in our fatal
* handler so that the (error) results are saved to the console log.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param bool $_im -- is Metric -- should only be set to true from adminIn broker event call
* @throws Exception
*
* HISTORY:
* ========
* 06-07-17 mks original coding
* 06-14-17 mks refactored for dynamodb
* 07-05-17 mks CORE-463: refactored for RMQ and mongoDB
* 07-07-17 mks CORE-463: switch collections on-the-fly for adminIn Metrics event call
* 12-06-17 mks CORE-591: caching log messages based on XML config params, w/console logging on errors
* 01-22-18 mks _INF-139: fixed bug where when caching is disabled, the current error wasn't being
* added to the error stack.
* 03-01-18 mks CORE-689: removed env tag from collection name
* 07-30-18 mks CORE-774: converted to PHP7.2 typeError trapping/processing
*
*/
public function writeLogMessage(bool $_im = false)
{
// if the logger service is unavailable or we don't have a message to publish, then return
if (!$this->available or empty($this->errStack)) return;
$this->status = false;
$oe = $oc = $os = '';
$maxMsgCount = gasConfig::$settings[CONFIG_CACHE][CONFIG_CACHE_LOG_BUFFER_COUNT];
$msgBufferOn = boolval(gasConfig::$settings[CONFIG_CACHE][CONFIG_CACHE_LOG_BUFFER]);
$whichBuffer = ($this->isMetric) ? CONFIG_CACHE_METRICS_BUFFER : CONFIG_CACHE_LOG_BUFFER;
$whichBufferCounter = ($this->isMetric) ? CONFIG_CACHE_METRICS_BUFFER_COUNT : CONFIG_CACHE_LOG_BUFFER_COUNT;
// if (empty($aryData[DB_PKEY])) {
// $this->errStack[(DB_PKEY . $this->ext)] = guid();
// }
// $this->errStack[(LOG_CREATED . $this->ext)] = time();
// admin service is remote - either cache or publish the message
if ($this->publishMessage) {
try {
$currMsgCount = gasCache::sysGet($whichBufferCounter);
$msgBuffer = gasCache::sysGet($whichBuffer);
// remove cache data
gasCache::sysDel($whichBuffer);
gasCache::sysDel($whichBufferCounter);
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
return;
}
// validate the message buffer fetched from cache
if (!empty($msgBuffer) and !is_array($msgBuffer)) {
$msg = basename(__FILE__) . COLON . __LINE__ . COLON;
$msg .= sprintf(ERROR_CACHE_DATA_MALFORMED, 'array', CONFIG_CACHE_LOG_BUFFER, gettype($msgBuffer));
$msg .= PHP_EOL . 'Dump: ' . PHP_EOL;
$msg .= var_export($msgBuffer, true);
$msg .= PHP_EOL;
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . $msg);
return;
}
if ($currMsgCount === false) {
/** @noinspection PhpUnusedLocalVariableInspection */
$currMsgCount = 0;
}
if (false === $msgBuffer) $msgBuffer = [];
$msgBuffer = [...$msgBuffer, ...$this->errStack];
$currMsgCount = count($msgBuffer);
// determine if we're going to publish or cache the message buffer
$publish = (!$msgBufferOn or $currMsgCount >= $maxMsgCount) ? true : false;
if ($publish) {
try {
// $this->abc = new gacAdminClientIn(__METHOD__ . COLON . __LINE__);
$this->abc = new gacLogClient();
if (!$this->abc->status) {
$hdr = basename(__METHOD__) . AT . __LINE__ . COLON;
$msg = ERROR_FAILED_TO_INSTANTIATE . RESOURCE_ADMIN_CLIENT;
consoleLog($this->res, CON_ERROR, $hdr . $msg);
return;
}
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
return;
}
$request = [
BROKER_REQUEST => ($this->isMetric) ? BROKER_REQUEST_MET : BROKER_REQUEST_LOG,
BROKER_DATA => $msgBuffer,
BROKER_META_DATA => [
META_TEMPLATE => ($this->isMetric) ? TEMPLATE_CLASS_METRICS : TEMPLATE_CLASS_LOGS,
META_CLIENT => CLIENT_SYSTEM
]
];
if (!empty($this->eventGUID)) $request[BROKER_META_DATA][META_EVENT_GUID] = $this->eventGUID;
try {
$route = (!$this->isMetric) ? EXCHANGE_SOURCE_LOGS : EXCHANGE_SOURCE_METRICS;
$this->abc->call(gzcompress(json_encode($request)), $route);
if (is_object($this->abc)) $this->abc->__destruct();
unset($this->abc);
} catch (TypeError | Throwable $t) {
$hdr = basename(__METHOD__) . AT . __LINE__ . COLON;
consoleLog($this->res, CON_ERROR, $hdr . $t->getMessage());
return;
}
unset($this->abc);
// $this->abc = null;
$this->isMetric = false;
// if (gasConfig::$settings[CONFIG_DEBUG]) {
// $msg = sprintf(SUCCESS_CACHE_LOG_DUMP, $currMsgCount);
// consoleLog($this->res, CON_SUCCESS, $msg);
// }
} else {
// update the current message buffer and message buffer counter and re-cache
$msgBuffer = [...$msgBuffer, ...$this->errStack];
$currMsgCount = count($msgBuffer);
try {
if (!gasCache::sysAdd($whichBuffer, $msgBuffer)) {
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_CACHE_ADD_FAIL . $whichBuffer);
return;
}
if (!gasCache::sysAdd($whichBufferCounter, $currMsgCount)) {
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_CACHE_ADD_FAIL . $whichBufferCounter . COLON . $currMsgCount);
return;
}
} catch (Throwable $t) {
$msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
consoleLog($this->res, CON_ERROR, $msg);
return;
}
}
} else {
// write the message directly to mongodb - start by checking the mongo resource
if (is_null($this->connection)) {
// if we "lost" the resource, attempt to reconnect
$this->connection = gasResourceManager::fetchResource(RESOURCE_MONGO_MASTER, ENV_ADMIN);
// if we can't reconnect, throw a fatal and exit
if (is_null($this->connection)) {
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_MONGO_CONNECT);
}
}
try {
$record = new MongoDB\Driver\BulkWrite();
foreach ($this->errStack as $errorMessage)
$record->insert($errorMessage);
if ($_im) {
$oc = $this->collectionName;
$oe = $this->ext;
$os = $this->class;
$this->ext = COLLECTION_MONGO_METRICS_EXT;
$this->collectionName = COLLECTION_MONGO_METRICS . $this->ext;
$this->class = COLLECTION_MONGO_METRICS;
} else {
$this->ext = COLLECTION_MONGO_LOGS_EXT;
$this->collectionName = COLLECTION_MONGO_LOGS . $this->ext;
$this->class = COLLECTION_MONGO_LOGS;
}
/** @var WriteResult $result */
$result = $this->connection->executeBulkWrite($this->dbName . DOT . $this->collectionName, $record);
if ($_im) {
$this->ext = $oe;
$this->collectionName = $oc;
$this->class = $os;
}
unset($record);
if ($result->getInsertedCount() != 1) {
$msg = __CLASS__ . COLON . __LINE__ . COLON;
$msg .= sprintf(ERROR_MONGO_INSERT_COUNT, 1, $result->getInsertedCount());
echo getDateTime() . CON_ERROR . $this->res . $msg . PHP_EOL;
// todo -- eventManger should be invoked here
} else {
$this->status = true;
}
} catch (MongoDB\Driver\Exception\BulkWriteException $e) {
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_MONGO_EXCEPTION_BULK_WRITE . PHP_EOL . $e->getMessage());
} catch (MongoDB\Driver\Exception\InvalidArgumentException $e) {
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_MONGO_EXCEPTION_INVALID_ARGS . PHP_EOL . $e->getMessage());
} catch (MongoDB\Driver\Exception\ConnectionException $e) {
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_MONGO_EXCEPTION_CONNECTION . PHP_EOL .$e->getMessage());
} catch (MongoDB\Driver\Exception\RuntimeException $e) {
$this->throwFatal(basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_MONGO_EXCEPTION_RUNTIME . PHP_EOL . $e->getMessage());
}
}
}
/**
* setCollection() -- private method
*
* this is a private method, always called by the writeLogMessage() method, that sets the collection destination
* for the current request. As of this writing, the logger handles all writes to both the log (pgsLogs_log) and
* metrics (pgsMetrics_met) collections. Based on the data passed in the error-message, we set the collection
* destination in this method, along with other member variables that are collection dependent.
*
* the method has a single input parameter, which defaults to a known constant, that is the destination collection.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_which
* @throws Exception
*
* HISTORY:
* ========
* 06-07-17 mks original coding
* 06-14-17 mks updated for ddb, dynamic environments
* 07-05-17 mks CORE-463: converted to mongodb
* 03-01-18 mks CORE-689: removed env tags from db name
*
*/
private function setCollection(string $_which = TEMPLATE_CLASS_LOGS): void
{
if (!in_array($_which, $this->validTemplates)) {
$this->throwFatal(ERROR_INVALID_TEMPLATE . $_which);
} else {
if ($_which == TEMPLATE_CLASS_LOGS) {
$this->ext = COLLECTION_MONGO_LOGS_EXT;
$this->collectionName = COLLECTION_MONGO_LOGS . $this->ext;
$this->class = COLLECTION_MONGO_LOGS;
} else {
$this->ext = COLLECTION_MONGO_METRICS_EXT;
$this->collectionName = COLLECTION_MONGO_METRICS . $this->ext;
$this->class = COLLECTION_MONGO_METRICS;
}
}
}
/**
* getLog() - public method
*
* getLog is the method that is used to fetch log (or Metrics) records from the mongo collection.
*
* The method has two required parameters:
*
* $_lines - integer value that indicates the number of log entries to retrieve - defaults to the system constant
* $_where - defines which collection to fetch from
*
* Method opens a channel/connection to mongodb and fetches N lines from the collection specified by the
* input parameter.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param int $_lines
* @param string $_where
* @return null|string
* @throws Exception
*
* HISTORY:
* ========
* 07-05-17 mks CORE-463: original coding
* 09-08-17 mks CORE-529: added event guids to the generated output for dumper
* 07-30-18 mks CORE-774: PHP7.2 Exception Compliance
*
*/
public function getLog($_lines, $_where): ?string
{
if (!in_array($_where, $this->validTemplates)) {
$msg = ERROR_MONGO_TEMPLATE_INVALID . $_where;
return($msg);
}
try {
$this->setCollection($_where);
} catch (Throwable $t) {
consoleLog($this->res, CON_ERROR, ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage());
}
if (!$this->available) {
return(null); // returning on false implicitly generates a console error message by the calling client
}
if (!is_numeric($_lines) or $_lines < 0) {
$_lines = intval(MONGO_LOG_MAX_LINES);
} else {
$_lines = intval($_lines);
}
$mongoData = null;
$returnData = null;
$cursor = null;
try {
$options = [
STRING_SORT => [(LOG_CREATED . $this->ext) => -1],
STRING_LIMIT => $_lines
];
$filter = [];
$nameSpace = $this->dbName . DOT . $this->collectionName;
$readPreference = new MongoDB\Driver\ReadPreference($this->config[CONFIG_DATABASE_MONGODB_ADMIN][CONFIG_DATABASE_MONGODB_SECONDARY_RP]);
$query = new MongoDB\Driver\Query($filter, $options);
$cursor = $this->connection->executeQuery($nameSpace, $query, $readPreference);
} catch (Throwable | TypeError $e) {
$this->throwFatal(__FILE__ . COLON . __LINE__ . COLON . ERROR_MONGO_EXCEPTION_INVALID_ARGS . PHP_EOL . $e->getMessage());
}
if (!is_null($cursor)) {
foreach ($cursor as $property) {
$property = (array) $property;
$returnData .= '<div class="rowMeta">'; // note: css is defined in the utilities directory
$returnData .= getDateTime($property[(LOG_CREATED . $this->ext)]) . ' - ';
// $returnData .= date(TIME_DATE_FORMAT, $row[(META_SESSION_DATE . self::$ext)]->sec) . ' - ';
// add error label as a span: warn/error/fatal...
try {
$returnData .= self::getErrorLabel($property[(LOG_LEVEL . $this->ext)]);
} catch (TypeError $t) {
consoleLog($this->res, CON_ERROR, ERROR_TYPE_EXCEPTION . COLON . $t->getMessage());
}
$returnData .= ' ' . $property[(ERROR_FILE . $this->ext)] . '(' . $property[(ERROR_LINE . $this->ext)] . ')';
$cd = '';
if (!empty($property[(ERROR_CLASS . $this->ext)])) $cd = ' class[' . $property[(ERROR_CLASS . $this->ext)] . ']';
if (!empty($property[(ERROR_METHOD . $this->ext)])) $cd .= '.method(' . $property[(ERROR_METHOD . $this->ext)] . ')</div>';
$returnData .= $cd;
$returnData .= '<div class="rowData">' . htmlentities($property[(ERROR_MESSAGE . $this->ext)]);
if ($_where == TEMPLATE_CLASS_METRICS) {
$returnData .= ' - ' . $property[(DB_TIMER . $this->ext)] . ' or ';
$returnData .= ($property[(DB_TIMER . $this->ext)] * NUMBER_MS_PER_SEC) . 'ms';
}
$returnData .= '</div>';
$returnData .= '<div class="rowHist">';
if (!empty($property[(DB_EVENT_GUID . $this->ext)])) {
$returnData .= 'Event ID: ' . $property[(DB_EVENT_GUID . $this->ext)];
}
// foreach($row[(TEMPLATE_HISTORY . $this->ext)] as $histRec) {
// $returnData .= date('Y-M-d h:i:s', $histRec[META_SESSION_DATE]->sec);// . ' (';
// if (!is_null($row[(MONGO_LOG_EVENT_GUID . $this->ext)]))
// $returnData .= ', Event ID: ' . $row[(MONGO_LOG_EVENT_GUID . $this->ext)];
// $returnData .= $histRec[META_SESSION_EVENT] . ') from (';
// $returnData .= $histRec[META_SESSION_IP] . '): ';
// $returnData .= ((isset($histRec[META_SESSION_ID])) ? $histRec[META_SESSION_ID] : $histRec[META_CLIENT_ID]) . '<br />';
// }
$returnData .= '</div><br />';
}
}
return ($returnData);
}
/**
* getErrorLabel() - public method
*
* build an association between known error types and a color-key for output.
*
* if the error does not exist, return black.
*
* in all cases, return an HTML SPAN tag coded to the selected color.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param $errorType
* @return string
*
* HISTORY:
* ========
* 06-07-17 mks original coding
*
*/
private function getErrorLabel(string $errorType):string
{
// set the error level text-color
$errorColorMap = [
ERROR_DEBUG => '#008000', // green
ERROR_METRICS => '#00FF00', // black
ERROR_DATA => '#0000FF', // black
ERROR_INFO => '#000080', // navy
ERROR_ERROR => '#800080', // purple
ERROR_FATAL => '#FF0000', // red
ERROR_WARN => '#F47E1C', // orange,
ERROR_EVENT => '#FF00CC', // pink
];
$cssColor = (!empty($errorColorMap[$errorType])) ? $errorColorMap[$errorType] : '#0';
return '<span style="color:' . $cssColor . ';">' . strtoupper($errorType) . '</span>';
}
/**
* __clone() -- public method
*
* Silently disallows cloning of the object
*
* @author mike@givingassistant.org
* @version 1.0
*
* @return null
*
* HISTORY:
* ========
* 03-18-15 mks original coding
*
*/
private function __clone()
{
return null;
}
/**
* __destruct() -- public method
*
* As of PHP 5.3.10, destructors are not run on shutdowns caused by fatal errors - since the destructor is
* now registered in the constructor method, recovery and/or clean-up efforts should go into this method.
*
* @author mike@givingassistant.org
* @version 1.0
*
* HISTORY:
* ========
* 03-18-15 mks original coding
* 05-11-16 mks ome-287: support for dynamic resource management
*
*/
public function __destruct()
{
//do nothing
}
}