952 days continuous production uptime, 40k+ tp/s single node. Original corpo Bitbucket history not included — clean archive commit.
817 lines
36 KiB
PHP
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
|
|
}
|
|
|
|
} |