From 77e2fa74e5c153e94fb3a7351d102a538012daac Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sun, 25 Oct 2020 03:29:42 +0800 Subject: [PATCH 01/13] Add initial commit for configurable logger --- .../Log/Entities/Api/StackifyError.php | 13 + .../Log/Entities/Api/WebRequestDetail.php | 205 ++++- src/Stackify/Log/Standalone/Logger.php | 6 +- .../Log/Transport/AbstractApiTransport.php | 2 +- .../Log/Transport/AbstractTransport.php | 23 + .../Log/Transport/Config/AbstractConfig.php | 699 ++++++++++++++++++ src/Stackify/Log/Transport/Config/Agent.php | 408 +++++++++- src/Stackify/Log/Transport/CurlTransport.php | 10 +- src/Stackify/Log/Transport/ExecTransport.php | 10 +- 9 files changed, 1349 insertions(+), 27 deletions(-) create mode 100644 src/Stackify/Log/Transport/Config/AbstractConfig.php diff --git a/src/Stackify/Log/Entities/Api/StackifyError.php b/src/Stackify/Log/Entities/Api/StackifyError.php index fcb2b72..4eb4ca6 100644 --- a/src/Stackify/Log/Entities/Api/StackifyError.php +++ b/src/Stackify/Log/Entities/Api/StackifyError.php @@ -2,6 +2,8 @@ namespace Stackify\Log\Entities\Api; +use Stackify\Log\Transport\Config\Agent; + class StackifyError { /** @@ -50,8 +52,19 @@ public function __construct($appName, $environmentName, $logServerVariables = fa } } + /** + * Get server environment variables + * + * @return array + */ private function getEnvironmentVariables() { + $agentConfig = Agent::getInstance(); + if ($agentConfig) { + return isset($_SERVER) && $agentConfig->getCaptureServerVariables() + ? WebRequestDetail::getRequestMap($_SERVER, $agentConfig->getCaptureServerVariablesBlacklist(), $agentConfig->getCaptureServerVariablesBlacklist()) + : null; + } return isset($_SERVER) ? WebRequestDetail::getRequestMap($_SERVER) : null; } diff --git a/src/Stackify/Log/Entities/Api/WebRequestDetail.php b/src/Stackify/Log/Entities/Api/WebRequestDetail.php index 43bf5e1..e529464 100644 --- a/src/Stackify/Log/Entities/Api/WebRequestDetail.php +++ b/src/Stackify/Log/Entities/Api/WebRequestDetail.php @@ -3,88 +3,125 @@ namespace Stackify\Log\Entities\Api; use Stackify\Utils\TypeConverter; +use Stackify\Log\Transport\Config\Agent; class WebRequestDetail { const HIDDEN_VALUE = 'X-MASKED-X'; - private static $HIDDEN_HEADERS = array('cookie', 'authorization'); + private static $_HIDDEN_HEADERS = array( + 'cookie' => 1, + 'authorization' => 1 + ); /** + * User IP address + * * @var string */ public $UserIPAddress; /** + * HTTP Method + * * @var string */ public $HttpMethod; /** + * Request Protocol + * * @var string */ public $RequestProtocol; /** + * Request URL + * * @var string */ public $RequestUrl; /** + * Request URL Root + * * @var string */ public $RequestUrlRoot; /** + * Referral URL + * * @var string */ public $ReferralUrl; /** + * Request Headers + * * @var array Key-value pairs */ public $Headers; /** + * Request Cookies + * * @var array Key-value pairs */ public $Cookies; /** + * $_GET values + * * @var array Key-value pairs */ public $QueryString; /** + * $_POST values + * * @var array Key-value pairs */ public $PostData; /** + * $_SESSION values + * * @var array Key-value pairs */ public $SessionData; /** + * Raw post data + * * @var string */ public $PostDataRaw; /** + * MVC Action + * * @var string */ public $MVCAction; /** + * MVC Controller + * * @var string */ public $MVCController; /** + * MVC Area + * * @var string */ public $MVCArea; + /** + * Constructor + */ private function __construct() { $this->UserIPAddress = $this->getIpAddress(); @@ -93,19 +130,71 @@ private function __construct() $this->RequestUrl = $this->getRequestUrl(); $this->RequestUrlRoot = filter_input(INPUT_SERVER, 'SERVER_NAME'); $this->ReferralUrl = filter_input(INPUT_SERVER, 'HTTP_REFERER'); - $this->Headers = $this->getHeaders(); - $this->Cookies = isset($_COOKIE) ? self::getRequestMap($_COOKIE, true) : null; - $this->QueryString = isset($_GET) ? self::getRequestMap($_GET) : null; - $this->PostData = isset($_POST) ? self::getRequestMap($_POST) : null; - $this->SessionData = isset($_SESSION) ? self::getRequestMap($_SESSION, true) : null; - $this->PostDataRaw = file_get_contents('php://input'); + + /** + * @var \Stackify\Log\Transport\Config\Agent + */ + $agentConfig = Agent::getInstance(); + + if ($agentConfig) { + $this->Headers = $agentConfig->getCaptureErrorHeaders() + ? $this->getHeaders( + $agentConfig->getCaptureErrorHeadersBlacklist(), + $agentConfig->getCaptureErrorHeadersWhitelist() + ) + : null; + $this->Cookies = isset($_COOKIE) && $agentConfig->getCaptureErrorCookies() + ? self::getRequestMap( + $_COOKIE, + $agentConfig->getCaptureErrorCookiesBlacklist(), + $agentConfig->getCaptureErrorCookiesWhitelist() + ) + : null; + + $this->QueryString = isset($_GET) && $agentConfig->getCaptureGetVariables() + ? self::getRequestMap( + $_COOKIE, + $agentConfig->getCaptureGetVariablesBlacklist(), + $agentConfig->getCaptureGetVariablesWhitelist() + ) + : null; + $this->PostData = isset($_POST) && $agentConfig->getCapturePostVariables() + ? self::getRequestMap( + $_COOKIE, + $agentConfig->getCapturePostVariablesBlacklist(), + $agentConfig->getCapturePostVariablesWhitelist() + ) + : null; + $this->SessionData = isset($_SESSION) && $agentConfig->getCaptureSessionVariables() + ? self::getRequestMap( + $_COOKIE, + $agentConfig->getCaptureSessionVariablesBlacklist(), + $agentConfig->getCaptureSessionVariablesWhitelist() + ) + : null; + $this->PostDataRaw = $agentConfig->getCaptureRawPostData() ? file_get_contents('php://input'): null; + } else { + $this->Headers = $this->getHeaders(); + $this->Cookies = isset($_COOKIE) ? self::getRequestMap($_COOKIE, ['*']) : null; + $this->QueryString = isset($_GET) ? self::getRequestMap($_GET) : null; + $this->PostData = isset($_POST) ? self::getRequestMap($_POST) : null; + $this->SessionData = isset($_SESSION) ? self::getRequestMap($_SESSION, ['*']) : null; + $this->PostDataRaw = file_get_contents('php://input'); + } } /** * Singleton attributes */ - private function __clone() {} + protected function __clone() + { + } + /** + * Get singleton instance + * + * @return self + */ public static function getInstance() { static $instance; @@ -117,24 +206,53 @@ public static function getInstance() /** * Converts $data to key-value pairs, where values are strings - * @param mixed $data - * @param boolean $maskValues Hide request values + * + * @param mixed $data Collection + * @param boolean $blacklist Blacklist + * @param boolean $whitelist Whitelist + * * @return array */ - public static function getRequestMap($data, $maskValues = false) + public static function getRequestMap($data, $blacklist = null, $whitelist = null) { + if ($blacklist && is_array($blacklist) == false) { + $blacklist = null; + // TODO: log? + } + + if ($whitelist && is_array($whitelist) == false) { + $whitelist = null; + // TODO: log? + } + $result = array(); if (is_array($data)) { foreach ($data as $key => $value) { - $result[$key] = $maskValues + $maskValue = false; + + if ($blacklist && isset($blacklist[$key])) { + $maskValue = true; + } + + if ($whitelist && isset($whitelist[$key]) === false) { + continue; + } + + $result[$key] = $maskValue ? self::HIDDEN_VALUE : TypeConverter::stringify($value); } } + return empty($result) ? null : $result; } - private function getIpAddress() + /** + * Get User IP Address + * + * @return string + */ + protected function getIpAddress() { $keys = array( 'HTTP_CLIENT_IP', @@ -154,7 +272,12 @@ private function getIpAddress() return null; } - private function getProtocol() + /** + * Get Protocol + * + * @return string + */ + protected function getProtocol() { $protocol = null; if ('cli' === php_sapi_name()) { @@ -165,7 +288,12 @@ private function getProtocol() return $protocol; } - private function getRequestUrl() + /** + * Get Request URL + * + * @return string + */ + protected function getRequestUrl() { $https = filter_input(INPUT_SERVER, 'HTTPS'); $ssl = null !== $https && 'off' !== $https; @@ -178,8 +306,26 @@ private function getRequestUrl() return null; } - private function getHeaders() + /** + * Get headers + * + * @param array $blacklist Blacklist + * @param array $whitelist Whitelist + * + * @return void + */ + protected function getHeaders($blacklist = null, $whitelist = null) { + if ($blacklist && is_array($blacklist) == false) { + $blacklist = null; + // TODO: log? + } + + if ($whitelist && is_array($whitelist) == false) { + $whitelist = null; + // TODO: log? + } + $headers = array(); if (function_exists('getallheaders')) { $headers = getallheaders(); @@ -188,12 +334,31 @@ private function getHeaders() $headers = array(); } } - foreach ($headers as $name => $value) { - if (in_array(strtolower($name), self::$HIDDEN_HEADERS)) { - $headers[$name] = self::HIDDEN_VALUE; + + $result = array(); + + foreach ($headers as $key => $value) { + $maskValue = false; + $lowercaseKey = strtolower($key); + + if ($blacklist && isset($blacklist[$key])) { + $maskValue = true; + } + + if ($whitelist && isset($whitelist[$key]) === false) { + continue; + } + + if (isset(self::$_HIDDEN_HEADERS[$lowercaseKey])) { + $maskValue = true; } + + $result[$key] = $maskValue + ? self::HIDDEN_VALUE + : TypeConverter::stringify($value); } - return empty($headers) ? null : $headers; + + return empty($result) ? null : $result; } } \ No newline at end of file diff --git a/src/Stackify/Log/Standalone/Logger.php b/src/Stackify/Log/Standalone/Logger.php index 463fc02..c1e0ba8 100644 --- a/src/Stackify/Log/Standalone/Logger.php +++ b/src/Stackify/Log/Standalone/Logger.php @@ -5,6 +5,7 @@ use Stackify\Log\Builder\MessageBuilder; use Stackify\Log\Transport\TransportInterface; use Stackify\Log\Transport\AgentSocketTransport; +use Stackify\Log\Transport\Config\Agent; use Psr\Log\AbstractLogger; @@ -16,12 +17,15 @@ class Logger extends AbstractLogger */ private $transport; - public function __construct($appName, $environmentName = null, TransportInterface $transport = null, $logServerVariables = false) + public function __construct($appName, $environmentName = null, TransportInterface $transport = null, $logServerVariables = false, $config = null) { $messageBuilder = new MessageBuilder('Stackify PHP Logger v.1.0', $appName, $environmentName, $logServerVariables); if (null === $transport) { $transport = new AgentSocketTransport(); } + if (null !== $config) { + Agent::getInstance()->extract($config); + } $transport->setMessageBuilder($messageBuilder); $this->transport = $transport; } diff --git a/src/Stackify/Log/Transport/AbstractApiTransport.php b/src/Stackify/Log/Transport/AbstractApiTransport.php index 1208389..b6567e9 100644 --- a/src/Stackify/Log/Transport/AbstractApiTransport.php +++ b/src/Stackify/Log/Transport/AbstractApiTransport.php @@ -54,7 +54,7 @@ protected function getApiHeaders() { return array( 'Content-Type' => 'application/json', - 'X-Stackify-PV' => Api::API_VERSION_HEADER, + 'X-Stackify-PV' => $this->agentConfig ? $this->agentConfig->getApiVersionHeader(): Api::API_VERSION_HEADER, 'X-Stackify-Key' => $this->apiKey, ); } diff --git a/src/Stackify/Log/Transport/AbstractTransport.php b/src/Stackify/Log/Transport/AbstractTransport.php index 934f867..63f9edf 100644 --- a/src/Stackify/Log/Transport/AbstractTransport.php +++ b/src/Stackify/Log/Transport/AbstractTransport.php @@ -23,11 +23,30 @@ abstract class AbstractTransport implements TransportInterface protected $debug = false; private $debugLogPath; + /** + * Agent config + * + * @var \Stackify\Log\Transport\Config\Agent + */ + protected $agentConfig; + /** + * Agent config attribute + * + * @var string + */ + protected $agentConfigAttribute = 'config'; + public function __construct() { $ds = DIRECTORY_SEPARATOR; $this->debugLogPath = realpath(dirname(__FILE__) . "$ds..$ds..") . $ds . 'debug/log.log'; $this->errorGovernor = new ErrorGovernor(); + + $this->agentConfig = Agent::getInstance(); + if ($this->agentConfig) { + $this->debugLogPath = $this->agentConfig->getDebugLogPath(); + } + // add empty implementation to avoid method calls on non-object $this->setMessageBuilder(new NullBuilder()); } @@ -53,6 +72,10 @@ protected function extractOptions($options) } } } + + if (isset($options[$this->agentConfigAttribute]) && $this->agentConfig) { + $this->agentConfig->extract($options[$this->agentConfigAttribute]); + } } protected function logError($message) diff --git a/src/Stackify/Log/Transport/Config/AbstractConfig.php b/src/Stackify/Log/Transport/Config/AbstractConfig.php new file mode 100644 index 0000000..3a9c3eb --- /dev/null +++ b/src/Stackify/Log/Transport/Config/AbstractConfig.php @@ -0,0 +1,699 @@ +CaptureServerVariables = true; + $this->CaptureServerVariablesWhitelist = array('*'); + $this->CaptureServerVariablesBlacklist = null; + + $this->CapturePostVariables = true; + $this->CapturePostVariablesWhitelist = array('*'); + $this->CapturePostVariablesBlacklist = null; + + $this->CaptureGetVariables = true; + $this->CaptureGetVariablesWhitelist = array('*'); + $this->CaptureGetVariablesBlacklist = null; + + $this->CaptureSessionVariables = true; + $this->CaptureSessionVariablesWhitelist = null; + $this->CaptureSessionVariablesBlacklist = array('*'); + + $this->CaptureErrorHeaders = true; + $this->CaptureErrorHeadersWhitelist = array('*'); + $this->CaptureErrorHeadersBlacklist = null; + + $this->CaptureErrorCookies = true; + $this->CaptureErrorCookiesWhitelist = null; + $this->CaptureErrorCookiesBlacklist = array('*'); + + $this->CaptureRawPostData = true; + + $ds = DIRECTORY_SEPARATOR; + $this->DebugLogPath = realpath(dirname(__FILE__) . "$ds..$ds..") . $ds . 'debug/log.log'; + } + + /** + * Set capture raw POST data option + * + * @param boolean $enable Enable + * + * @return void + */ + public function setCaptureRawPostData($enable = null) + { + $this->CaptureRawPostData = $this->getBoolean($enable, 'CaptureRawPostData'); + } + /** + * Set capture $_SERVER variable option + * + * @param boolean $enable Enable + * + * @return void + */ + public function setCaptureServerVariables($enable = null) + { + $this->CaptureServerVariables = $this->getBoolean($enable, 'CaptureServerVariables'); + } + /** + * Set capture $_SERVER whitelist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureServerVariablesWhitelist($rawConfig = null) + { + $this->CaptureServerVariablesWhitelist = $this->parseStringToArray($rawConfig, 'CaptureServerVariablesWhitelist'); + } + /** + * Set capture $_SERVER blacklist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureServerVariablesBlacklist($rawConfig = null) + { + $this->CaptureServerVariablesBlacklist = $this->parseStringToArray($rawConfig, 'CaptureServerVariablesBlacklist'); + } + /** + * Set capture $_POST variable option + * + * @param boolean $enable Enable + * + * @return void + */ + public function setCapturePostVariables($enable = null) + { + $this->CapturePostVariables = $this->getBoolean($enable, 'CapturePostVariables'); + } + /** + * Set capture $_POST whitelist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCapturePostVariablesWhitelist($rawConfig = null) + { + $this->CapturePostVariablesWhitelist = $this->parseStringToArray($rawConfig, 'CapturePostVariablesWhitelist'); + } + /** + * Set capture $_POST blacklist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCapturePostVariablesBlacklist($rawConfig = null) + { + $this->CapturePostVariablesBlacklist = $this->parseStringToArray($rawConfig, 'CapturePostVariablesBlacklist'); + } + /** + * Set capture $_GET variable option + * + * @param boolean $enable Enable + * + * @return void + */ + public function setCaptureGetVariables($enable = null) + { + $this->CaptureGetVariables = $this->getBoolean($enable, 'CaptureGetVariables'); + } + /** + * Set capture $_GET whitelist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureGetVariablesWhitelist($rawConfig = null) + { + $this->CaptureGetVariablesWhitelist = $this->parseStringToArray($rawConfig, 'CaptureGetVariablesWhitelist'); + } + /** + * Set capture $_GET blacklist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureGetVariablesBlacklist($rawConfig = null) + { + $this->CaptureGetVariablesBlacklist = $this->parseStringToArray($rawConfig, 'CaptureGetVariablesBlacklist'); + } + /** + * Set capture $_SESSION variable option + * + * @param boolean $enable Enable + * + * @return void + */ + public function setCaptureSessionVariables($enable = null) + { + $this->CaptureSessionVariables = $this->getBoolean($enable, 'CaptureSessionVariables'); + } + /** + * Set capture $_SESSION whitelist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureSessionVariablesWhitelist($rawConfig = null) + { + $this->CaptureSessionVariablesWhitelist = $this->parseStringToArray($rawConfig, 'CaptureSessionVariablesWhitelist'); + } + /** + * Set capture $_SESSION blacklist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureSessionVariablesBlacklist($rawConfig = null) + { + $this->CaptureSessionVariablesBlacklist = $this->parseStringToArray($rawConfig, 'CaptureSessionVariablesBlacklist'); + } + /** + * Set capture `getallheaders` variable option + * + * @param boolean $enable Enable + * + * @return void + */ + public function setCaptureErrorHeaders($enable = null) + { + $this->CaptureErrorHeaders = $this->getBoolean($enable, 'CaptureErrorHeaders'); + } + /** + * Set capture `getallheaders` whitelist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureErrorHeadersWhitelist($rawConfig = null) + { + $this->CaptureErrorHeadersWhitelist = $this->parseStringToArray($rawConfig, 'CaptureErrorHeadersWhitelist'); + } + /** + * Set capture `getallheaders` blacklist + * + * @param array $rawConfig From config + * + * @return void + */ + public function setCaptureErrorHeadersBlacklist($rawConfig = null) + { + $this->CaptureErrorHeadersBlacklist = $this->parseStringToArray($rawConfig, 'CaptureErrorHeadersBlacklist'); + } + /** + * Set capture $_COOKIE variable option + * + * @param boolean $enable Enable + * + * @return void + */ + public function setCaptureErrorCookies($enable = null) + { + $this->CaptureErrorCookies = $this->getBoolean($enable, 'CaptureErrorCookies'); + } + /** + * Set capture $_COOKIE whitelist + * + * @param array $rawConfig From config + * + * @return mixed + */ + public function setCaptureErrorCookiesWhitelist($rawConfig = null) + { + $this->CaptureErrorCookiesWhitelist = $this->parseStringToArray($rawConfig, 'CaptureErrorCookiesWhitelist'); + } + /** + * Set capture $_COOKIE blacklist + * + * @param array $rawConfig From config + * + * @return mixed + */ + public function setCaptureErrorCookiesBlacklist($rawConfig = null) + { + $this->CaptureErrorCookiesBlacklist = $this->parseStringToArray($rawConfig, 'CaptureErrorCookiesBlacklist'); + } + /** + * Set Debug Log Path + * + * @param string $path Path + * + * @return void + */ + public function setDebugLogPath($path) + { + if ($path == null) { + $this->log('[DebugLogPath] is not valid.'); + return; + } + + $this->DebugLogPath = $path; + } + + + /** + * Get capture raw POST data option + * + * @return boolean + */ + public function getCaptureRawPostData() + { + return $this->CaptureRawPostData; + } + /** + * Get capture $_SERVER variable option + * + * @return boolean + */ + public function getCaptureServerVariables() + { + return $this->CaptureServerVariables; + } + /** + * Get capture $_POST variable option + * + * @return boolean + */ + public function getCapturePostVariables() + { + return $this->CapturePostVariables; + } + /** + * Get capture $_POST whitelist + * + * @return mixed + */ + public function getCapturePostVariablesWhitelist() + { + return $this->CapturePostVariablesWhitelist; + } + /** + * Get capture $_POST blacklist + * + * @return mixed + */ + public function getCapturePostVariablesBlacklist() + { + return $this->CapturePostVariablesBlacklist; + } + /** + * Get capture $_GET variable option + * + * @return boolean + */ + public function getCaptureGetVariables() + { + return $this->CaptureGetVariables; + } + /** + * Get capture $_GET whitelist + * + * @return mixed + */ + public function getCaptureGetVariablesWhitelist() + { + return $this->CaptureGetVariablesWhitelist; + } + /** + * Get capture $_GET blacklist + * + * @return mixed + */ + public function getCaptureGetVariablesBlacklist() + { + return $this->CaptureGetVariablesBlacklist; + } + /** + * Get capture $_SESSION variable option + * + * @return boolean + */ + public function getCaptureSessionVariables() + { + return $this->CaptureSessionVariables; + } + /** + * Get capture $_SESSION whitelist + * + * @return mixed + */ + public function getCaptureSessionVariablesWhitelist() + { + return $this->CaptureSessionVariablesWhitelist; + } + /** + * Get capture $_SESSION blacklist + * + * @return mixed + */ + public function getCaptureSessionVariablesBlacklist() + { + return $this->CaptureSessionVariablesBlacklist; + } + /** + * Get capture `getallheaders` variable option + * + * @return boolean + */ + public function getCaptureErrorHeaders() + { + return $this->CaptureErrorHeaders; + } + /** + * Get capture `getallheaders` whitelist + * + * @return mixed + */ + public function getCaptureErrorHeadersWhitelist() + { + return $this->CaptureErrorHeadersWhitelist; + } + /** + * Get capture `getallheaders` blacklist + * + * @return mixed + */ + public function getCaptureErrorHeadersBlacklist() + { + return $this->CaptureErrorHeadersBlacklist; + } + /** + * Get capture $_COOKIE variable option + * + * @return boolean + */ + public function getCaptureErrorCookies() + { + return $this->CaptureErrorCookies; + } + /** + * Get capture $_COOKIE whitelist + * + * @return mixed + */ + public function getCaptureErrorCookiesWhitelist() + { + return $this->CaptureErrorCookiesWhitelist; + } + /** + * Get capture $_COOKIE blacklist + * + * @return mixed + */ + public function getCaptureErrorCookiesBlacklist() + { + return $this->CaptureErrorCookiesBlacklist; + } + /** + * Get Debug log path + * + * @return string + */ + public function getDebugLogPath() + { + return $this->DebugLogPath; + } + + /** + * Singleton attributes + * + * @return void + */ + private function __clone() + { + } + + /** + * Get singleton instance + * + * @return self + */ + public static function getInstance() + { + static $instance; + if (null === $instance) { + $instance = new self(); + } + return $instance; + } + + /** + * Log error message + * + * @param string $message Message + * + * @return void + */ + protected function logError($message) + { + $this->log($message, func_get_args(), false); + } + + /** + * Log debug message + * + * @param string $message String + * + * @return void + */ + protected function logDebug($message) + { + if (!$this->debug) { + return; + } + $this->log($message, func_get_args(), true); + } + + /** + * Log message + * + * @param string $message Message + * @param mixed $args Context + * @param boolean $success Success + * + * @return void + */ + protected function log($message, $args, $success = true) + { + $replacements = array_slice($args, 1); + $prefix = $success ? 'Stackify Log' : 'Stackify Error'; + $template = "[$prefix][Config] $message"; + $formatted = preg_replace('/\r\n/', '', vsprintf($template, $replacements)); + // first option - write to local file if possible + // this can be not available because of file permissions + @file_put_contents($this->getDebugLogPath(), "$formatted\n", FILE_APPEND); + if (!$success) { + // second option - send to default PHP error log + error_log($formatted); + } + } + + /** + * Extract config from array + * + * @param string|array $config Config + * + * @return void + */ + public function extract($config = null) + { + if (is_array($config) == false) { + $this->log('['. __CLASS__ .']['. __FUNCTION__ .'] $config is not an array.'); + return; + } + + foreach ($config as $key => $value) { + if (property_exists($this, $key)) { + $method = 'set' . $key; + + if (method_exists($this, $method)) { + $method($value); + continue; + } + + $this->log('['. __CLASS__ .']['. __FUNCTION__ .']' . $method .' does not exists.'); + } + } + } + + /** + * Parse comma-delimited string to array + * + * @param string $string String + * @param string $property Property + * + * @return void + */ + protected function parseStringToArray($string = null, $property = null) + { + if (is_string($string) == false && is_array($string) == false) { + $this->log('['.$property.'] is not a comma-delimited string or array.'); + return null;; + } + + $parsedString = $string; + if (is_string($string)) { + $parsedString = array_map('trim', explode(',', $string)); + } + + return array_flip($parsedString); + } + /** + * Get boolean + * + * @param boolean $enable Enable + * @param string $property Property + * + * @return void + */ + protected function getBoolean($enable = null, $property = null) + { + if (is_bool($enable) == false) { + $this->log('['.$property.'] is not a boolean.'); + return null; + } + + return $enable; + } +} \ No newline at end of file diff --git a/src/Stackify/Log/Transport/Config/Agent.php b/src/Stackify/Log/Transport/Config/Agent.php index 25f26f2..c5f0ec5 100644 --- a/src/Stackify/Log/Transport/Config/Agent.php +++ b/src/Stackify/Log/Transport/Config/Agent.php @@ -1,14 +1,416 @@ Protocol = SOCKET_PROTOCOL; + $this->Host = SOCKET_HOST; + $this->Port = SOCKET_PORT; + $this->SocketTimeoutConnect = SOCKET_TIMEOUT_CONNECT; + $this->SocketTimeoutWrite = SOCKET_TIMEOUT_WRITE; + $this->SocketMaxConnectAttempts = SOCKET_MAX_CONNECT_ATTEMPTS; + $this->DomainSocketPath = DOMAIN_SOCKET_PATH; + $this->ApiBaseUrl = Api::API_BASE_URL; + $this->ApiCallLogsEndpoint = Api::API_CALL_LOGS; + $this->ApiMaxTimeout = Api::API_MAX_TIME; + $this->ApiVersionHeader = Api::API_VERSION_HEADER; + + parent::__construct(); + } + + /** + * Set protocol for the agent transport + * + * @param string $protocol Protocol for the transport + * + * @return void + */ + public function setProtocol($protocol = null) + { + if (in_array($protocol, $ValidProtocols) == false) { + $this->log('[Protocol] is not valid.'); + return; + } + + $this->Protocol = $protocol; + } + + /** + * Set hostname for the agent transport + * + * @param string $host Hostname + * + * @return void + */ + public function setHost($host = null) + { + if ($host == null) { + $this->log('[Host] is not valid.'); + } + + // TODO: Hostname checking + $this->Host = $host; + } + + /** + * Set port for the agent transport + * + * @param integer $port Port + * + * @return void + */ + public function setPort($port = null) + { + if (is_int($port) == false) { + $this->log('[Port] is not an integer.'); + return; + } + + if ($port < 0 && $port > 65535) { + $this->log('[Port] is not valid.'); + return; + } + + return $this->Port; + } + + /** + * Set socket connection timeout + * + * @param integer $timeout Timeout + * + * @return void + */ + public function setSocketTimeoutConnect($timeout = null) + { + if (is_int($timeout) == false) { + $this->log('[SocketTimeoutConnect] is not an integer.'); + return; + } + + $this->SocketTimeoutConnect = $timeout; + } + + /** + * Set socket write timeout + * + * @param integer $timeout Timeout + * + * @return void + */ + public function setSocketTimeoutWrite($timeout ) + { + if (is_int($timeout) == false) { + $this->log('[SocketTimeoutWrite] is not an integer.'); + return; + } + + $this->SocketTimeoutWrite = $timeout; + } + + /** + * Set socket max connection attempts + * + * @param integer $attempts Attempts + * + * @return void + */ + public function setSocketMaxConnectAttempts($attempts) + { + if (is_int($attempts) == false) { + $this->log('[SocketMaxConnectAttempts] is not an integer.'); + return; + } + + $this->SocketMaxConnectAttempts = $attempts; + } + + /** + * Set domain socket path + * + * @param string $path Path + * + * @return void + */ + public function setDomainSocketPath($path) + { + if ($path == null) { + $this->log('[DomainSocketPath] is not valid.'); + return; + } + + $this->DomainSocketPath = $path; + } + + /** + * Set Api Base Url + * + * @param string $url URL + * + * @return void + */ + public function setApiBaseUrl($url) + { + if ($url == null) { + $this->log('[ApiBaseUrl] is not valid.'); + return; + } + + $this->ApiBaseUrl = $url; + } + /** + * Set Api Call Logs Endpoint + * + * @param string $path Path + * + * @return void + */ + public function setApiCallLogsEndpoint($path) + { + if ($path == null) { + $this->log('[ApiCallLogsEndpoint] is not valid.'); + return; + } + + $this->ApiCallLogsEndpoint = $path; + } + /** + * Set Api Max Time Out + * + * @param integer $timeout Timeout + * + * @return void + */ + public function setApiMaxTimeout($timeout = null) + { + if (is_int($timeout) == false) { + $this->log('[ApiMaxTimeout] is not an integer.'); + return; + } + + $this->ApiMaxTimeout = $timeout; + } + /** + * Set Api Version Header + * + * @param string $path Path + * + * @return void + */ + public function setApiVersionHeader($path) + { + if ($path == null) { + $this->log('[ApiVersionHeader] is not valid.'); + return; + } + + $this->ApiVersionHeader = $path; + } + + /** + * Get socket protocol + * + * @return integer + */ + public function getProtocol() + { + return $this->Protocol; + } + + /** + * Get socket host + * + * @return integer + */ + public function getHost() + { + return $this->Host; + } + + /** + * Get socket port + * + * @return integer + */ + public function getPort() + { + return $this->Port; + } + + /** + * Get socket connection timeout + * + * @return integer + */ + public function getSocketTimeoutConnect() + { + return $this->SocketTimeoutConnect; + } + + /** + * Get socket write timeout + * + * @return integer + */ + public function getSocketTimeoutWrite() + { + return $this->SocketTimeoutWrite; + } + + /** + * Get socket max connection attempts + * + * @return integer + */ + public function getSocketMaxConnectAttempts() + { + return $this->SocketMaxConnectAttempts; + } + + /** + * Get socket domain path + * + * @return string + */ + public function getDomainSocketPath() + { + return $this->DomainSocketPath; + } + + /** + * Get api base url + * + * @return string + */ + public function getApiBaseUrl() + { + return $this->ApiBaseUrl; + } + /** + * Get api call logs endpoint + * + * @return string + */ + public function getApiCallLogsEndpoint() + { + return $this->ApiCallLogsEndpoint; + } + /** + * Get api max timeout + * + * @return integer + */ + public function getApiMaxTimeout() + { + return $this->ApiMaxTimeout; + } + /** + * Get api version header + * + * @return string + */ + public function getApiVersionHeader() + { + return $this->ApiVersionHeader; + } + + /** + * Log message + * + * @param string $message Message + * @param mixed $args Log context + * @param boolean $success Success + * + * @return void + */ + protected function log($message, $args, $success = true) + { + return $this->log('['. __CLASS__ .']'.$message, $args, $success); + } + } diff --git a/src/Stackify/Log/Transport/CurlTransport.php b/src/Stackify/Log/Transport/CurlTransport.php index fc2de24..07ea656 100644 --- a/src/Stackify/Log/Transport/CurlTransport.php +++ b/src/Stackify/Log/Transport/CurlTransport.php @@ -33,10 +33,18 @@ protected function send($data) foreach ($this->getApiHeaders() as $name => $value) { $headers[] = "$name: $value"; } + $url = Api::API_BASE_URL . Api::API_CALL_LOGS; + $maxTimeout = Api::API_MAX_TIME; + + if ($this->agentConfig) { + $url = $this->agentConfig->getApiBaseUrl() . $this->agentConfig->getApiCallLogsEndpoint(); + $maxTimeout = $this->agentConfig->getApiMaxTimeout(); + } + $handle = curl_init($url); curl_setopt($handle, CURLOPT_POST, 1); - curl_setopt($handle, CURLOPT_TIMEOUT, Api::API_MAX_TIME); + curl_setopt($handle, CURLOPT_TIMEOUT, $maxTimeout); curl_setopt($handle, CURLOPT_HTTPHEADER, $headers); curl_setopt($handle, CURLOPT_POSTFIELDS, $data); curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); diff --git a/src/Stackify/Log/Transport/ExecTransport.php b/src/Stackify/Log/Transport/ExecTransport.php index aa88ecc..ccda23a 100644 --- a/src/Stackify/Log/Transport/ExecTransport.php +++ b/src/Stackify/Log/Transport/ExecTransport.php @@ -79,12 +79,20 @@ protected function sendChunk(array $items) protected function send($data) { $url = Api::API_BASE_URL . Api::API_CALL_LOGS; + $maxTime = Api::API_MAX_TIME; + + if ($this->agentConfig) { + $url = $this->agentConfig->getApiBaseUrl() . $this->agentConfig->getApiCallLogsEndpoint(); + $maxTime = $this->agentConfig->getApiMaxTimeout(); + } + + $cmd = "$this->curlPath -X POST"; foreach ($this->getApiHeaders() as $name => $value) { $cmd .= " --header \"$name: $value\""; } $escapedData = $this->escapeArg($data); - $maxTime = Api::API_MAX_TIME; + $cmd .= " --data '$escapedData' '$url' --max-time $maxTime"; if ($this->proxy) { $cmd .= " --proxy '$this->proxy'"; From 0036f9912656452a76953f6cdb17888e227976d9 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sun, 25 Oct 2020 21:21:42 +0800 Subject: [PATCH 02/13] Add wildcard checking --- .../Log/Entities/Api/WebRequestDetail.php | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/Stackify/Log/Entities/Api/WebRequestDetail.php b/src/Stackify/Log/Entities/Api/WebRequestDetail.php index e529464..0f527a3 100644 --- a/src/Stackify/Log/Entities/Api/WebRequestDetail.php +++ b/src/Stackify/Log/Entities/Api/WebRequestDetail.php @@ -230,12 +230,22 @@ public static function getRequestMap($data, $blacklist = null, $whitelist = null foreach ($data as $key => $value) { $maskValue = false; - if ($blacklist && isset($blacklist[$key])) { - $maskValue = true; + if ($blacklist) { + if (true == isset($blacklist[$key]) || + (true == isset($blacklist[0]) && $blacklist[0] == '*') || + true == isset($blacklist['*']) + ) { + $maskValue = true; + } } - - if ($whitelist && isset($whitelist[$key]) === false) { - continue; + + if ($whitelist) { + if (isset($whitelist[$key]) === false || + (true == isset($whitelist[0]) && $whitelists[0] == '*') || + true == isset($whitelist['*']) + ) { + continue; + } } $result[$key] = $maskValue @@ -341,12 +351,22 @@ protected function getHeaders($blacklist = null, $whitelist = null) $maskValue = false; $lowercaseKey = strtolower($key); - if ($blacklist && isset($blacklist[$key])) { - $maskValue = true; + if ($blacklist) { + if (true == isset($blacklist[$key]) || + (true == isset($blacklist[0]) && $blacklist[0] == '*') || + true == isset($whitelist['*']) + ) { + $maskValue = true; + } } - if ($whitelist && isset($whitelist[$key]) === false) { - continue; + if ($whitelist) { + if (isset($whitelist[$key]) === false || + (true == isset($whitelist[0]) && $whitelists[0] == '*') || + true == isset($whitelist['*']) + ) { + continue; + } } if (isset(self::$_HIDDEN_HEADERS[$lowercaseKey])) { From d4d297d0c0898feb9e2f2f7d8ed20e7b019188ef Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sun, 25 Oct 2020 21:37:01 +0800 Subject: [PATCH 03/13] Add debug setting --- .../Log/Transport/AbstractTransport.php | 1 + .../Log/Transport/Config/AbstractConfig.php | 29 ++++++++++++++++++- src/Stackify/Log/Transport/CurlTransport.php | 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Stackify/Log/Transport/AbstractTransport.php b/src/Stackify/Log/Transport/AbstractTransport.php index 63f9edf..120f9fb 100644 --- a/src/Stackify/Log/Transport/AbstractTransport.php +++ b/src/Stackify/Log/Transport/AbstractTransport.php @@ -45,6 +45,7 @@ public function __construct() $this->agentConfig = Agent::getInstance(); if ($this->agentConfig) { $this->debugLogPath = $this->agentConfig->getDebugLogPath(); + $this->debug = $this->agentConfig->getDebug(); } // add empty implementation to avoid method calls on non-object diff --git a/src/Stackify/Log/Transport/Config/AbstractConfig.php b/src/Stackify/Log/Transport/Config/AbstractConfig.php index 3a9c3eb..3b6d08c 100644 --- a/src/Stackify/Log/Transport/Config/AbstractConfig.php +++ b/src/Stackify/Log/Transport/Config/AbstractConfig.php @@ -128,6 +128,12 @@ abstract class AbstractConfig * @var string */ protected $DebugLogPath; + /** + * Debug Setting + * + * @var boolean + */ + protected $Debug; /** @@ -163,6 +169,7 @@ public function __construct() $ds = DIRECTORY_SEPARATOR; $this->DebugLogPath = realpath(dirname(__FILE__) . "$ds..$ds..") . $ds . 'debug/log.log'; + $this->Debug = false; } /** @@ -390,6 +397,17 @@ public function setDebugLogPath($path) $this->DebugLogPath = $path; } + /** + * Set Debug Settings + * + * @param boolean $enable Enable + * + * @return void + */ + public function setDebug($enable = null) + { + $this->Debug = $this->getBoolean($enable, 'Debug'); + } /** @@ -554,6 +572,15 @@ public function getDebugLogPath() { return $this->DebugLogPath; } + /** + * Get Debug setting + * + * @return boolean + */ + public function getDebug() + { + return $this->Debug; + } /** * Singleton attributes @@ -599,7 +626,7 @@ protected function logError($message) */ protected function logDebug($message) { - if (!$this->debug) { + if (!$this->getDebug()) { return; } $this->log($message, func_get_args(), true); diff --git a/src/Stackify/Log/Transport/CurlTransport.php b/src/Stackify/Log/Transport/CurlTransport.php index 07ea656..bc3e911 100644 --- a/src/Stackify/Log/Transport/CurlTransport.php +++ b/src/Stackify/Log/Transport/CurlTransport.php @@ -55,6 +55,8 @@ protected function send($data) $errorNo = curl_errno($handle); $code = curl_getinfo($handle, CURLINFO_HTTP_CODE); $error = curl_error($handle); + $this->logDebug("Config: %s", json_encode($data)); + $this->logDebug("Body: %s", json_encode($data)); if (0 !== $errorNo || 200 !== $code) { $this->logError(self::ERROR_CURL, $errorNo, $code, $error, $response); } else { From a51055ee342a24b974d503dde58a6746a288e01a Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sun, 25 Oct 2020 21:40:56 +0800 Subject: [PATCH 04/13] Fix namespace --- src/Stackify/Log/Transport/AbstractTransport.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Stackify/Log/Transport/AbstractTransport.php b/src/Stackify/Log/Transport/AbstractTransport.php index 120f9fb..a8bfc5d 100644 --- a/src/Stackify/Log/Transport/AbstractTransport.php +++ b/src/Stackify/Log/Transport/AbstractTransport.php @@ -6,6 +6,7 @@ use Stackify\Log\Builder\NullBuilder; use Stackify\Log\Filters\ErrorGovernor; use Stackify\Exceptions\InitializationException; +use Stackify\Log\Transport\Config\Agent; abstract class AbstractTransport implements TransportInterface { From 035afaabcdb3f53ff38bdb5e7e9eb44da2ade0cb Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 26 Oct 2020 00:25:46 +0800 Subject: [PATCH 05/13] Transport fixes --- .../Log/Transport/AbstractTransport.php | 35 +++++-- .../Log/Transport/AgentSocketTransport.php | 20 +++- src/Stackify/Log/Transport/AgentTransport.php | 91 ++++++++++++++++++- .../Log/Transport/Config/AbstractConfig.php | 31 ++----- src/Stackify/Log/Transport/Config/Agent.php | 69 +++++++++----- src/Stackify/Log/Transport/CurlTransport.php | 2 - src/Stackify/Log/Transport/ExecTransport.php | 5 +- 7 files changed, 188 insertions(+), 65 deletions(-) diff --git a/src/Stackify/Log/Transport/AbstractTransport.php b/src/Stackify/Log/Transport/AbstractTransport.php index a8bfc5d..224ac89 100644 --- a/src/Stackify/Log/Transport/AbstractTransport.php +++ b/src/Stackify/Log/Transport/AbstractTransport.php @@ -44,10 +44,6 @@ public function __construct() $this->errorGovernor = new ErrorGovernor(); $this->agentConfig = Agent::getInstance(); - if ($this->agentConfig) { - $this->debugLogPath = $this->agentConfig->getDebugLogPath(); - $this->debug = $this->agentConfig->getDebug(); - } // add empty implementation to avoid method calls on non-object $this->setMessageBuilder(new NullBuilder()); @@ -87,7 +83,7 @@ protected function logError($message) protected function logDebug($message) { - if (!$this->debug) { + if (!$this->getDebug()) { return; } $this->log($message, func_get_args(), true); @@ -101,11 +97,38 @@ private function log($message, $args, $success = true) $formatted = preg_replace('/\r\n/', '', vsprintf($template, $replacements)); // first option - write to local file if possible // this can be not available because of file permissions - @file_put_contents($this->debugLogPath, "$formatted\n", FILE_APPEND); + @file_put_contents($this->getDebugLogPath(), "$formatted\n", FILE_APPEND); if (!$success) { // second option - send to default PHP error log error_log($formatted); } } + /** + * Get debug setting + * + * @return boolean + */ + public function getDebug() + { + if ($this->debug == false && $this->agentConfig) { + return $this->agentConfig->getDebug(); + } + + return $this->debug; + } + + /** + * Get debug log path + * + * @return string + */ + public function getDebugLogPath() + { + if ($this->agentConfig) { + return $this->agentConfig->getDebugLogPath(); + } + + return $this->debugLogPath; + } } \ No newline at end of file diff --git a/src/Stackify/Log/Transport/AgentSocketTransport.php b/src/Stackify/Log/Transport/AgentSocketTransport.php index ea02d4b..c97ddcc 100644 --- a/src/Stackify/Log/Transport/AgentSocketTransport.php +++ b/src/Stackify/Log/Transport/AgentSocketTransport.php @@ -32,7 +32,7 @@ protected function send($data) try { if (!empty($data)) { $messageFactory = new GuzzleMessageFactory(); - $client = new Client($messageFactory, array('remote_socket' => 'unix://' . Config::DOMAIN_SOCKET_PATH)); + $client = new Client($messageFactory, array('remote_socket' => 'unix://' . $this->getDomainSocketPath())); $request = $messageFactory->createRequest('POST', 'http://log', array('Content-Type' => 'application/json', 'Content-Length' => strlen($data)), @@ -41,12 +41,26 @@ protected function send($data) $response = $client->sendRequest($request); if ($response->getStatusCode() != 200) { - $this->logError(self::ERROR_WRITE, Config::DOMAIN_SOCKET_PATH); + $this->logError(self::ERROR_WRITE, $this->getDomainSocketPath()); } } } catch (HttpClientException $e) { - $this->logError(self::ERROR_CONNECT, Config::DOMAIN_SOCKET_PATH, $e->getCode(), $e->getMessage()); + $this->logError(self::ERROR_CONNECT, $this->getDomainSocketPath(), $e->getCode(), $e->getMessage()); } } + + /** + * Get domain socket path + * + * @return string + */ + public function getDomainSocketPath() + { + if ($this->agentConfig) { + return $this->agentConfig->getDomainSocketPath(); + } + + return Config::DOMAIN_SOCKET_PATH; + } } diff --git a/src/Stackify/Log/Transport/AgentTransport.php b/src/Stackify/Log/Transport/AgentTransport.php index 34aa057..380db30 100644 --- a/src/Stackify/Log/Transport/AgentTransport.php +++ b/src/Stackify/Log/Transport/AgentTransport.php @@ -74,17 +74,100 @@ private function send($data) private function connect() { - while (!$this->connected && $this->connectAttempts < Config::SOCKET_MAX_CONNECT_ATTEMPTS) { + while (!$this->connected && $this->connectAttempts < $this->getSocketMaxConnectAttempts()) { $this->connectAttempts++; - $remote = sprintf('%s://%s:%d', Config::SOCKET_PROTOCOL, Config::SOCKET_HOST, $this->port); - $this->socket = @stream_socket_client($remote, $errno, $errstr, Config::SOCKET_TIMEOUT_CONNECT); + $remote = sprintf('%s://%s:%d', $this->getSocketProtocol(), $this->getSocketHost(), $this->getSocketPort()); + $this->socket = @stream_socket_client($remote, $errno, $errstr, $this->getSocketTimeoutConnect()); $this->connected = false !== $this->socket; if ($this->connected) { - stream_set_timeout($this->socket, Config::SOCKET_TIMEOUT_WRITE); + stream_set_timeout($this->socket, $this->getSocketTimeoutWrite()); } else { $this->logError(self::ERROR_CONNECT, $remote, $errno, $errstr); } } } + /** + * Get socket port + * + * @return mixed + */ + public function getSocketPort() + { + if ($this->port == Config::SOCKET_PORT && $this->agentConfig) { + return $this->agentConfig->getPort(); + } + + return $this->port; + } + + /** + * Get socket max connect attempts + * + * @return integer + */ + public function getSocketMaxConnectAttempts() + { + if ($this->agentConfig) { + return $this->agentConfig->getSocketMaxConnectAttempts(); + } + + return Config::SOCKET_MAX_CONNECT_ATTEMPTS; + } + + /** + * Get socket max connect attempts + * + * @return string + */ + public function getSocketProtocol() + { + if ($this->agentConfig) { + return $this->agentConfig->getProtocol(); + } + + return Config::SOCKET_PROTOCOL; + } + + /** + * Get socket host + * + * @return integer + */ + public function getSocketHost() + { + if ($this->agentConfig) { + return $this->agentConfig->getHost(); + } + + return Config::SOCKET_HOST; + } + + /** + * Get socket timeout connect + * + * @return integer + */ + public function getSocketTimeoutConnect() + { + if ($this->agentConfig) { + return $this->agentConfig->getSocketTimeoutConnect(); + } + + return Config::SOCKET_TIMEOUT_CONNECT; + } + + /** + * Get socket timeout write + * + * @return integer + */ + public function getSocketTimeoutWrite() + { + if ($this->agentConfig) { + return $this->agentConfig->getSocketTimeoutWrite(); + } + + return Config::SOCKET_TIMEOUT_WRITE; + } } \ No newline at end of file diff --git a/src/Stackify/Log/Transport/Config/AbstractConfig.php b/src/Stackify/Log/Transport/Config/AbstractConfig.php index 3b6d08c..be32637 100644 --- a/src/Stackify/Log/Transport/Config/AbstractConfig.php +++ b/src/Stackify/Log/Transport/Config/AbstractConfig.php @@ -582,29 +582,6 @@ public function getDebug() return $this->Debug; } - /** - * Singleton attributes - * - * @return void - */ - private function __clone() - { - } - - /** - * Get singleton instance - * - * @return self - */ - public static function getInstance() - { - static $instance; - if (null === $instance) { - $instance = new self(); - } - return $instance; - } - /** * Log error message * @@ -675,7 +652,7 @@ public function extract($config = null) $method = 'set' . $key; if (method_exists($this, $method)) { - $method($value); + $this->$method($value); continue; } @@ -694,8 +671,12 @@ public function extract($config = null) */ protected function parseStringToArray($string = null, $property = null) { + if ($string == null) { + return null; + } + if (is_string($string) == false && is_array($string) == false) { - $this->log('['.$property.'] is not a comma-delimited string or array.'); + $this->logError('['.$property.'] is not a comma-delimited string or array.'); return null;; } diff --git a/src/Stackify/Log/Transport/Config/Agent.php b/src/Stackify/Log/Transport/Config/Agent.php index c5f0ec5..b406ce2 100644 --- a/src/Stackify/Log/Transport/Config/Agent.php +++ b/src/Stackify/Log/Transport/Config/Agent.php @@ -67,7 +67,7 @@ class Agent extends AbstractConfig * * @var string */ - protected $ApiCallLogs; + protected $ApiCallLogsEndpoint; /** * Api Max Time out * @@ -88,13 +88,13 @@ class Agent extends AbstractConfig */ public function __construct() { - $this->Protocol = SOCKET_PROTOCOL; - $this->Host = SOCKET_HOST; - $this->Port = SOCKET_PORT; - $this->SocketTimeoutConnect = SOCKET_TIMEOUT_CONNECT; - $this->SocketTimeoutWrite = SOCKET_TIMEOUT_WRITE; - $this->SocketMaxConnectAttempts = SOCKET_MAX_CONNECT_ATTEMPTS; - $this->DomainSocketPath = DOMAIN_SOCKET_PATH; + $this->Protocol = static::SOCKET_PROTOCOL; + $this->Host = static::SOCKET_HOST; + $this->Port = static::SOCKET_PORT; + $this->SocketTimeoutConnect = static::SOCKET_TIMEOUT_CONNECT; + $this->SocketTimeoutWrite = static::SOCKET_TIMEOUT_WRITE; + $this->SocketMaxConnectAttempts = static::SOCKET_MAX_CONNECT_ATTEMPTS; + $this->DomainSocketPath = static::DOMAIN_SOCKET_PATH; $this->ApiBaseUrl = Api::API_BASE_URL; $this->ApiCallLogsEndpoint = Api::API_CALL_LOGS; $this->ApiMaxTimeout = Api::API_MAX_TIME; @@ -112,8 +112,8 @@ public function __construct() */ public function setProtocol($protocol = null) { - if (in_array($protocol, $ValidProtocols) == false) { - $this->log('[Protocol] is not valid.'); + if (in_array($protocol, $this->ValidProtocols) == false) { + $this->logError('[Protocol] is not valid.'); return; } @@ -130,7 +130,7 @@ public function setProtocol($protocol = null) public function setHost($host = null) { if ($host == null) { - $this->log('[Host] is not valid.'); + $this->logError('[Host] is not valid.'); } // TODO: Hostname checking @@ -147,16 +147,16 @@ public function setHost($host = null) public function setPort($port = null) { if (is_int($port) == false) { - $this->log('[Port] is not an integer.'); + $this->logError('[Port] is not an integer.'); return; } if ($port < 0 && $port > 65535) { - $this->log('[Port] is not valid.'); + $this->logError('[Port] is not valid.'); return; } - return $this->Port; + $this->Port = $port; } /** @@ -169,7 +169,7 @@ public function setPort($port = null) public function setSocketTimeoutConnect($timeout = null) { if (is_int($timeout) == false) { - $this->log('[SocketTimeoutConnect] is not an integer.'); + $this->logError('[SocketTimeoutConnect] is not an integer.'); return; } @@ -186,7 +186,7 @@ public function setSocketTimeoutConnect($timeout = null) public function setSocketTimeoutWrite($timeout ) { if (is_int($timeout) == false) { - $this->log('[SocketTimeoutWrite] is not an integer.'); + $this->logError('[SocketTimeoutWrite] is not an integer.'); return; } @@ -203,7 +203,7 @@ public function setSocketTimeoutWrite($timeout ) public function setSocketMaxConnectAttempts($attempts) { if (is_int($attempts) == false) { - $this->log('[SocketMaxConnectAttempts] is not an integer.'); + $this->logError('[SocketMaxConnectAttempts] is not an integer.'); return; } @@ -220,7 +220,7 @@ public function setSocketMaxConnectAttempts($attempts) public function setDomainSocketPath($path) { if ($path == null) { - $this->log('[DomainSocketPath] is not valid.'); + $this->logError('[DomainSocketPath] is not valid.'); return; } @@ -237,7 +237,7 @@ public function setDomainSocketPath($path) public function setApiBaseUrl($url) { if ($url == null) { - $this->log('[ApiBaseUrl] is not valid.'); + $this->logError('[ApiBaseUrl] is not valid.'); return; } @@ -253,7 +253,7 @@ public function setApiBaseUrl($url) public function setApiCallLogsEndpoint($path) { if ($path == null) { - $this->log('[ApiCallLogsEndpoint] is not valid.'); + $this->logError('[ApiCallLogsEndpoint] is not valid.'); return; } @@ -269,7 +269,7 @@ public function setApiCallLogsEndpoint($path) public function setApiMaxTimeout($timeout = null) { if (is_int($timeout) == false) { - $this->log('[ApiMaxTimeout] is not an integer.'); + $this->logError('[ApiMaxTimeout] is not an integer.'); return; } @@ -285,7 +285,7 @@ public function setApiMaxTimeout($timeout = null) public function setApiVersionHeader($path) { if ($path == null) { - $this->log('[ApiVersionHeader] is not valid.'); + $this->logError('[ApiVersionHeader] is not valid.'); return; } @@ -410,7 +410,30 @@ public function getApiVersionHeader() */ protected function log($message, $args, $success = true) { - return $this->log('['. __CLASS__ .']'.$message, $args, $success); + return parent::log('['.get_class().']'.$message, $args, $success); + } + + + /** + * Singleton attributes + * + * @return void + */ + private function __clone() + { } + /** + * Get singleton instance + * + * @return self + */ + public static function getInstance() + { + static $instance; + if (null === $instance) { + $instance = new self(); + } + return $instance; + } } diff --git a/src/Stackify/Log/Transport/CurlTransport.php b/src/Stackify/Log/Transport/CurlTransport.php index bc3e911..07ea656 100644 --- a/src/Stackify/Log/Transport/CurlTransport.php +++ b/src/Stackify/Log/Transport/CurlTransport.php @@ -55,8 +55,6 @@ protected function send($data) $errorNo = curl_errno($handle); $code = curl_getinfo($handle, CURLINFO_HTTP_CODE); $error = curl_error($handle); - $this->logDebug("Config: %s", json_encode($data)); - $this->logDebug("Body: %s", json_encode($data)); if (0 !== $errorNo || 200 !== $code) { $this->logError(self::ERROR_CURL, $errorNo, $code, $error, $response); } else { diff --git a/src/Stackify/Log/Transport/ExecTransport.php b/src/Stackify/Log/Transport/ExecTransport.php index ccda23a..e874b7e 100644 --- a/src/Stackify/Log/Transport/ExecTransport.php +++ b/src/Stackify/Log/Transport/ExecTransport.php @@ -91,13 +91,14 @@ protected function send($data) foreach ($this->getApiHeaders() as $name => $value) { $cmd .= " --header \"$name: $value\""; } + $escapedData = $this->escapeArg($data); $cmd .= " --data '$escapedData' '$url' --max-time $maxTime"; if ($this->proxy) { $cmd .= " --proxy '$this->proxy'"; } - if ($this->debug) { + if ($this->getDebug()) { $cmd .= ' --verbose'; } else { // return immediately while curl will run in the background @@ -107,7 +108,7 @@ protected function send($data) $r = exec($cmd, $output, $result); // if debug mode is off, it makes no sense to check result, // because command is send to background - if ($this->debug) { + if ($this->getDebug()) { if ($result !== 0) { // curl returned some error $this->logError(self::ERROR_CURL, $cmd, $result, implode(' ', $output)); From e16933d028d0167fde2b2568c3a048f9f8ff6c8c Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 26 Oct 2020 02:51:46 +0800 Subject: [PATCH 06/13] Add fix config and request details --- .../Log/Entities/Api/StackifyError.php | 2 +- .../Log/Entities/Api/WebRequestDetail.php | 31 ++++++++++--------- .../Log/Transport/Config/AbstractConfig.php | 20 +++++++++++- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/Stackify/Log/Entities/Api/StackifyError.php b/src/Stackify/Log/Entities/Api/StackifyError.php index 4eb4ca6..4c5cf2f 100644 --- a/src/Stackify/Log/Entities/Api/StackifyError.php +++ b/src/Stackify/Log/Entities/Api/StackifyError.php @@ -62,7 +62,7 @@ private function getEnvironmentVariables() $agentConfig = Agent::getInstance(); if ($agentConfig) { return isset($_SERVER) && $agentConfig->getCaptureServerVariables() - ? WebRequestDetail::getRequestMap($_SERVER, $agentConfig->getCaptureServerVariablesBlacklist(), $agentConfig->getCaptureServerVariablesBlacklist()) + ? WebRequestDetail::getRequestMap($_SERVER, $agentConfig->getCaptureServerVariablesBlacklist(), $agentConfig->getCaptureServerVariablesWhitelist()) : null; } return isset($_SERVER) ? WebRequestDetail::getRequestMap($_SERVER) : null; diff --git a/src/Stackify/Log/Entities/Api/WebRequestDetail.php b/src/Stackify/Log/Entities/Api/WebRequestDetail.php index 0f527a3..f1d558a 100644 --- a/src/Stackify/Log/Entities/Api/WebRequestDetail.php +++ b/src/Stackify/Log/Entities/Api/WebRequestDetail.php @@ -141,7 +141,7 @@ private function __construct() ? $this->getHeaders( $agentConfig->getCaptureErrorHeadersBlacklist(), $agentConfig->getCaptureErrorHeadersWhitelist() - ) + ) : null; $this->Cookies = isset($_COOKIE) && $agentConfig->getCaptureErrorCookies() ? self::getRequestMap( @@ -153,21 +153,21 @@ private function __construct() $this->QueryString = isset($_GET) && $agentConfig->getCaptureGetVariables() ? self::getRequestMap( - $_COOKIE, + $_GET, $agentConfig->getCaptureGetVariablesBlacklist(), $agentConfig->getCaptureGetVariablesWhitelist() ) : null; $this->PostData = isset($_POST) && $agentConfig->getCapturePostVariables() ? self::getRequestMap( - $_COOKIE, + $_POST, $agentConfig->getCapturePostVariablesBlacklist(), $agentConfig->getCapturePostVariablesWhitelist() ) : null; $this->SessionData = isset($_SESSION) && $agentConfig->getCaptureSessionVariables() ? self::getRequestMap( - $_COOKIE, + $_SESSION, $agentConfig->getCaptureSessionVariablesBlacklist(), $agentConfig->getCaptureSessionVariablesWhitelist() ) @@ -240,11 +240,13 @@ public static function getRequestMap($data, $blacklist = null, $whitelist = null } if ($whitelist) { - if (isset($whitelist[$key]) === false || - (true == isset($whitelist[0]) && $whitelists[0] == '*') || - true == isset($whitelist['*']) + if ( + !(true == isset($whitelist[0]) && $whitelist[0] == '*') && + false == isset($whitelist['*']) ) { - continue; + if (isset($whitelist[$key]) === false) { + continue; + } } } @@ -346,7 +348,6 @@ protected function getHeaders($blacklist = null, $whitelist = null) } $result = array(); - foreach ($headers as $key => $value) { $maskValue = false; $lowercaseKey = strtolower($key); @@ -354,18 +355,20 @@ protected function getHeaders($blacklist = null, $whitelist = null) if ($blacklist) { if (true == isset($blacklist[$key]) || (true == isset($blacklist[0]) && $blacklist[0] == '*') || - true == isset($whitelist['*']) + true == isset($blacklist['*']) ) { $maskValue = true; } } if ($whitelist) { - if (isset($whitelist[$key]) === false || - (true == isset($whitelist[0]) && $whitelists[0] == '*') || - true == isset($whitelist['*']) + if ( + !(true == isset($whitelist[0]) && $whitelist[0] == '*') && + false == isset($whitelist['*']) ) { - continue; + if (isset($whitelist[$key]) === false) { + continue; + } } } diff --git a/src/Stackify/Log/Transport/Config/AbstractConfig.php b/src/Stackify/Log/Transport/Config/AbstractConfig.php index be32637..ea6b56a 100644 --- a/src/Stackify/Log/Transport/Config/AbstractConfig.php +++ b/src/Stackify/Log/Transport/Config/AbstractConfig.php @@ -428,6 +428,24 @@ public function getCaptureServerVariables() { return $this->CaptureServerVariables; } + /** + * Get capture $_SERVER whitelist + * + * @return mixed + */ + public function getCaptureServerVariablesWhitelist() + { + return $this->CaptureServerVariablesWhitelist; + } + /** + * Get capture $_SERVER blacklist + * + * @return mixed + */ + public function getCaptureServerVariablesBlacklist() + { + return $this->CaptureServerVariablesBlacklist; + } /** * Get capture $_POST variable option * @@ -671,7 +689,7 @@ public function extract($config = null) */ protected function parseStringToArray($string = null, $property = null) { - if ($string == null) { + if (empty($string)) { return null; } From ddfa77fc99c1ab20cee79e591ba5c485b8ca0f19 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 26 Oct 2020 03:18:15 +0800 Subject: [PATCH 07/13] Update is int to numeric --- src/Stackify/Log/Transport/Config/Agent.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Stackify/Log/Transport/Config/Agent.php b/src/Stackify/Log/Transport/Config/Agent.php index b406ce2..d667a1f 100644 --- a/src/Stackify/Log/Transport/Config/Agent.php +++ b/src/Stackify/Log/Transport/Config/Agent.php @@ -146,7 +146,7 @@ public function setHost($host = null) */ public function setPort($port = null) { - if (is_int($port) == false) { + if (is_numeric($port) == false) { $this->logError('[Port] is not an integer.'); return; } @@ -168,7 +168,7 @@ public function setPort($port = null) */ public function setSocketTimeoutConnect($timeout = null) { - if (is_int($timeout) == false) { + if (is_numeric($timeout) == false) { $this->logError('[SocketTimeoutConnect] is not an integer.'); return; } @@ -185,7 +185,7 @@ public function setSocketTimeoutConnect($timeout = null) */ public function setSocketTimeoutWrite($timeout ) { - if (is_int($timeout) == false) { + if (is_numeric($timeout) == false) { $this->logError('[SocketTimeoutWrite] is not an integer.'); return; } @@ -202,7 +202,7 @@ public function setSocketTimeoutWrite($timeout ) */ public function setSocketMaxConnectAttempts($attempts) { - if (is_int($attempts) == false) { + if (is_numeric($attempts) == false) { $this->logError('[SocketMaxConnectAttempts] is not an integer.'); return; } @@ -268,7 +268,7 @@ public function setApiCallLogsEndpoint($path) */ public function setApiMaxTimeout($timeout = null) { - if (is_int($timeout) == false) { + if (is_numeric($timeout) == false) { $this->logError('[ApiMaxTimeout] is not an integer.'); return; } From 8efd0d915fe18373ec709eb12cd5da7831a30be2 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 26 Oct 2020 15:35:07 +0800 Subject: [PATCH 08/13] Add request body debug --- src/Stackify/Log/Transport/AbstractApiTransport.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Stackify/Log/Transport/AbstractApiTransport.php b/src/Stackify/Log/Transport/AbstractApiTransport.php index b6567e9..2c49665 100644 --- a/src/Stackify/Log/Transport/AbstractApiTransport.php +++ b/src/Stackify/Log/Transport/AbstractApiTransport.php @@ -36,6 +36,9 @@ public function finish() $json = $this->messageBuilder->getApiMessage($this->queue); // empty queue to avoid duplicates $this->queue = array(); + if ($this->getDebug()) { + $this->logDebug('['.get_class().'] Request Body: %s', $json); + } $this->send($json); } } From f8247834583bc1d1b12bdd63571e33e4e3ea2a3a Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Wed, 4 Nov 2020 17:16:39 +0800 Subject: [PATCH 09/13] Update README file --- README.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/README.md b/README.md index ea3de8b..b7f8812 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,107 @@ Note that ExecTransport does not produce any errors at all, but you can switch i $transport = new ExecTransport($apiKey, ['debug' => true]); ``` +### Logger Settings +These are the available settings for the logger. +#### Server Variables +- `CaptureServerVariables` - `Boolean` - Capture `$_SERVER` variables +- `CaptureServerVariablesWhitelist` - `Array` or `Comma-delimited string` - Whitelist `$_SERVER` attributes +- `CaptureServerVariablesBlacklist` - `Array` or `Comma-delimited string` - Mask `$_SERVER` attributes (e.g. `attribute => 'X-MASKED-X'`) +#### Get Variables +- `CaptureGetVariables` - `Boolean` - Capture `$_GET` variables +- `CaptureGetVariablesWhitelist` - `Array` or `Comma-delimited string` - Whitelist `$_GET` attributes +- `CaptureGetVariablesBlacklist` - `Array` or `Comma-delimited string` - Mask `$_GET` attributes (e.g. `attribute => 'X-MASKED-X'`) +#### Post Variables +- `CapturePostVariables` - `Boolean` - Capture `$_POST` variables +- `CapturePostVariablesWhitelist` - `Array` or `Comma-delimited string` - Whitelist `$_POST` attributes +- `CapturePostVariablesBlacklist` - `Array` or `Comma-delimited string` - Mask `$_POST` attributes (e.g. `attribute => 'X-MASKED-X'`) +#### Session Variables +- `CaptureSessionVariables` - `Boolean` - Capture `$_SESSION` variables +- `CaptureSessionVariablesWhitelist` - `Array` or `Comma-delimited string` - Whitelist `$_SESSION` attributes +- `CaptureSessionVariablesBlacklist` - `Array` or `Comma-delimited string` - Mask `$_SESSION` attributes (e.g. `attribute => 'X-MASKED-X'`) +#### Error Headers +- `CaptureErrorHeaders` - `Boolean` - Capture `HEADER` attributes available in `$_SERVER` variable +- `CaptureErrorHeadersWhitelist` - `Array` or `Comma-delimited string` - Whitelist `HEADER` attributes in `$_SERVER` variable +- `CaptureErrorHeadersBlacklist` - `Array` or `Comma-delimited string` - Mask `HEADER` attributes in `$_SERVER` variable (e.g. `attribute => 'X-MASKED-X'`) +#### Error Cookies +- `CaptureErrorCookies` - `Boolean` - Capture `$_COOKIE` variables +- `CaptureErrorCookiesWhitelist` - `Array` or `Comma-delimited string` - Whitelist `$_COOKIE` attributes +- `CaptureErrorCookiesBlacklist` - `Array` or `Comma-delimited string` - Mask `$_COOKIE` attributes +#### Capture Raw Post Data +- `CaptureRawPostData` - `Boolean` - Capture `php://input` stream data `(e.g. file_get_contents("php://input"))` +#### Debug Settings +- `Debug` - `Boolean` - Enable DEBUG in the logger +- `DebugLogPath` - `String` - A qualified path for the log file produced during debug or error +#### Agent Transport Settings +- `Protocol` - `String` - Protocol can be `tcp` or `udp` +- `Host` - `String` - Server Hostname +- `Port` - `Numeric` - Port +- `SocketTimeoutConnect` - `Numeric` - Connection Request Timeout +- `SocketTimeoutWrite` - `Numeric` - Connection Write Timeout +- `SocketMaxConnectAttempts` - `Numeric` - Connection Attempts +#### Agent Socket Transport Settings +- `DomainSocketPath` - `String` - Stackify Agent unix socket path +#### API or Curl Exec Socket Transport Settings +- `ApiBaseUrl` - `String` - Stackify API base url +- `ApiCallLogsEndpoint` - `String` - Stackify API Call Logs endpoint +- `ApiMaxTimeout` - `Numeric` - Stackify API Call Max Timeout +- `ApiVersionHeader` - `String` - Stackify API Version Header + +#### Example Configuration Setup +```php +$logger = new Logger( + 'PHP-HelloWorld', + 'Stackify-Sample-Environment', + $transport, + false, + [ + // Server + 'CaptureServerVariables' => false, + 'CaptureServerVariablesWhitelist' => '*', + 'CaptureServerVariablesBlacklist' => 'REMOTE_ADDR,SERVER_ADDR', + // Get + 'CaptureGetVariables' => false, + 'CaptureGetVariablesWhitelist' => 'test', + 'CaptureGetVariablesBlacklist' => [ + 'test1' + ], + // Post + 'CapturePostVariables' => true, + 'CapturePostVariablesWhitelist' => 'password', + 'CapturePostVariablesBlacklist' => 'username', + // Session + 'CaptureSessionVariables' => true, + 'CaptureSessionVariablesWhitelist' => 'testa', + 'CaptureSessionVariablesBlacklist' => 'testa', + // Headers + 'CaptureErrorHeaders' => true, + 'CaptureErrorHeadersWhitelist' => 'test4', + 'CaptureErrorHeadersBlacklist' => 'test-4', + // Cookies + 'CaptureErrorCookies' => true, + 'CaptureErrorCookiesWhitelist' => 'test5', + 'CaptureErrorCookiesBlacklist' => 'test-5', + // Raw post data + 'CaptureRawPostData' => true, + // Debug log path + 'DebugLogPath' => getcwd() . DIRECTORY_SEPARATOR . 'log.log', + 'Debug' => true, + // Agent Config + 'Protocol' => 'udp', + 'Host' => '127.0.0.2', + 'Port' => 10602, // didnt detect + 'SocketTimeoutConnect' => 5, + 'SocketTimeoutWrite' => 4, + 'SocketMaxConnectAttempts' => 3, + 'DomainSocketPath' => '/usr/local/stackify/stackify.sock', + 'ApiBaseUrl' => 'https://api.stackify.com', + 'ApiCallLogsEndpoint' => '/Log/Save', + 'ApiMaxTimeout' => 6, + 'ApiVersionHeader' => 'V2' + ] +); +``` + ## License Copyright 2019 Stackify, LLC. From 4ac1cca2ad194cf9fddf66210f0d1f9f6e497979 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Thu, 5 Nov 2020 08:06:53 +0800 Subject: [PATCH 10/13] Add logger changes --- README.md | 103 ++++++++---------- .../Log/Entities/Api/WebRequestDetail.php | 99 +++++++---------- .../Log/Transport/Config/AbstractConfig.php | 4 +- 3 files changed, 82 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index b7f8812..7297d54 100644 --- a/README.md +++ b/README.md @@ -62,17 +62,36 @@ system environment variables; do not enable if sensitive information such as pas $logger = new Logger('application_name', 'environment_name', $transport, true); ``` - -#### Troubleshooting +### **Configuration Settings** +- This allow users to override default settings of the logger (Masking Request Variables, Session, Cookie or Updating connection properties to different Transports etc.) -If transport does not work, try looking into ```vendor\stackify\logger\src\Stackify\debug\log.log``` file (if it is available for writing). Errors are also written to global PHP [error_log](http://php.net/manual/en/errorfunc.configuration.php#ini.error-log). -Note that ExecTransport does not produce any errors at all, but you can switch it to debug mode: -```php -$transport = new ExecTransport($apiKey, ['debug' => true]); +#### Logger Level + ```php +$config = array( + 'CaptureServerVariables' => false, + 'CaptureServerVariablesWhitelist' => '*', + 'CaptureServerVariablesBlacklist' => 'REMOTE_ADDR,SERVER_ADDR', + ... + ); + +$logger = new Logger('application_name', 'environment_name', $transport, true, $config); ``` -### Logger Settings -These are the available settings for the logger. +#### Transport Level +- This applies to all the transports `(ExecTransport, CurlTransport, AgentTransport, AgentSocketTransport)` + ```php +$config = array( + 'CaptureServerVariables' => false, + 'CaptureServerVariablesWhitelist' => '*', + 'CaptureServerVariablesBlacklist' => 'REMOTE_ADDR,SERVER_ADDR', + ... + ); + +$transport = new ExecTransport($apiKey, [ + 'config' => $config +]); +``` +### Available Options: #### Server Variables - `CaptureServerVariables` - `Boolean` - Capture `$_SERVER` variables - `CaptureServerVariablesWhitelist` - `Array` or `Comma-delimited string` - Whitelist `$_SERVER` attributes @@ -117,59 +136,23 @@ These are the available settings for the logger. - `ApiMaxTimeout` - `Numeric` - Stackify API Call Max Timeout - `ApiVersionHeader` - `String` - Stackify API Version Header -#### Example Configuration Setup +#### Troubleshooting + +If transport does not work, try looking into ```vendor\stackify\logger\src\Stackify\debug\log.log``` file (if it is available for writing). Errors are also written to global PHP [error_log](http://php.net/manual/en/errorfunc.configuration.php#ini.error-log). +Note that ExecTransport does not produce any errors at all, but you can switch it to debug mode: ```php -$logger = new Logger( - 'PHP-HelloWorld', - 'Stackify-Sample-Environment', - $transport, - false, - [ - // Server - 'CaptureServerVariables' => false, - 'CaptureServerVariablesWhitelist' => '*', - 'CaptureServerVariablesBlacklist' => 'REMOTE_ADDR,SERVER_ADDR', - // Get - 'CaptureGetVariables' => false, - 'CaptureGetVariablesWhitelist' => 'test', - 'CaptureGetVariablesBlacklist' => [ - 'test1' - ], - // Post - 'CapturePostVariables' => true, - 'CapturePostVariablesWhitelist' => 'password', - 'CapturePostVariablesBlacklist' => 'username', - // Session - 'CaptureSessionVariables' => true, - 'CaptureSessionVariablesWhitelist' => 'testa', - 'CaptureSessionVariablesBlacklist' => 'testa', - // Headers - 'CaptureErrorHeaders' => true, - 'CaptureErrorHeadersWhitelist' => 'test4', - 'CaptureErrorHeadersBlacklist' => 'test-4', - // Cookies - 'CaptureErrorCookies' => true, - 'CaptureErrorCookiesWhitelist' => 'test5', - 'CaptureErrorCookiesBlacklist' => 'test-5', - // Raw post data - 'CaptureRawPostData' => true, - // Debug log path - 'DebugLogPath' => getcwd() . DIRECTORY_SEPARATOR . 'log.log', - 'Debug' => true, - // Agent Config - 'Protocol' => 'udp', - 'Host' => '127.0.0.2', - 'Port' => 10602, // didnt detect - 'SocketTimeoutConnect' => 5, - 'SocketTimeoutWrite' => 4, - 'SocketMaxConnectAttempts' => 3, - 'DomainSocketPath' => '/usr/local/stackify/stackify.sock', - 'ApiBaseUrl' => 'https://api.stackify.com', - 'ApiCallLogsEndpoint' => '/Log/Save', - 'ApiMaxTimeout' => 6, - 'ApiVersionHeader' => 'V2' - ] -); +$transport = new ExecTransport($apiKey, ['debug' => true]); +``` + +You can set it also on the `Logger` level. Setting the `Debug` and `DebugLogPath` + +```php +$config = array( + 'DebugLogPath' => '/path/to/log.log', + 'Debug' => true + ); + +$logger = new Logger('application_name', 'environment_name', $transport, true, $config); ``` ## License diff --git a/src/Stackify/Log/Entities/Api/WebRequestDetail.php b/src/Stackify/Log/Entities/Api/WebRequestDetail.php index f1d558a..d4ab8f4 100644 --- a/src/Stackify/Log/Entities/Api/WebRequestDetail.php +++ b/src/Stackify/Log/Entities/Api/WebRequestDetail.php @@ -215,14 +215,39 @@ public static function getInstance() */ public static function getRequestMap($data, $blacklist = null, $whitelist = null) { - if ($blacklist && is_array($blacklist) == false) { + if (!is_array($whitelist)) { + $whitelist = null; + } + + if (!is_array($blacklist)) { $blacklist = null; - // TODO: log? } - if ($whitelist && is_array($whitelist) == false) { - $whitelist = null; - // TODO: log? + if (empty($whitelist)) { + return null; + } + + if (empty($blacklist)) { + $blacklist = null; + } + + $whitelistAll = false; + $blacklistAll = false; + + if ($blacklist) { + if ((true == isset($blacklist[0]) && $blacklist[0] == '*') + || true == isset($blacklist['*']) + ) { + $blacklistAll = true; + } + } + + if ($whitelist) { + if ((true == isset($whitelist[0]) && $whitelist[0] == '*') + || true == isset($whitelist['*']) + ) { + $whitelistAll = true; + } } $result = array(); @@ -231,23 +256,17 @@ public static function getRequestMap($data, $blacklist = null, $whitelist = null $maskValue = false; if ($blacklist) { - if (true == isset($blacklist[$key]) || - (true == isset($blacklist[0]) && $blacklist[0] == '*') || - true == isset($blacklist['*']) + if ($blacklistAll + || true == isset($blacklist[$key]) ) { $maskValue = true; } } - if ($whitelist) { - if ( - !(true == isset($whitelist[0]) && $whitelist[0] == '*') && - false == isset($whitelist['*']) - ) { - if (isset($whitelist[$key]) === false) { - continue; - } - } + if (!$whitelistAll + && false == isset($whitelist[$key]) + ) { + continue; } $result[$key] = $maskValue @@ -328,16 +347,6 @@ protected function getRequestUrl() */ protected function getHeaders($blacklist = null, $whitelist = null) { - if ($blacklist && is_array($blacklist) == false) { - $blacklist = null; - // TODO: log? - } - - if ($whitelist && is_array($whitelist) == false) { - $whitelist = null; - // TODO: log? - } - $headers = array(); if (function_exists('getallheaders')) { $headers = getallheaders(); @@ -347,41 +356,7 @@ protected function getHeaders($blacklist = null, $whitelist = null) } } - $result = array(); - foreach ($headers as $key => $value) { - $maskValue = false; - $lowercaseKey = strtolower($key); - - if ($blacklist) { - if (true == isset($blacklist[$key]) || - (true == isset($blacklist[0]) && $blacklist[0] == '*') || - true == isset($blacklist['*']) - ) { - $maskValue = true; - } - } - - if ($whitelist) { - if ( - !(true == isset($whitelist[0]) && $whitelist[0] == '*') && - false == isset($whitelist['*']) - ) { - if (isset($whitelist[$key]) === false) { - continue; - } - } - } - - if (isset(self::$_HIDDEN_HEADERS[$lowercaseKey])) { - $maskValue = true; - } - - $result[$key] = $maskValue - ? self::HIDDEN_VALUE - : TypeConverter::stringify($value); - } - - return empty($result) ? null : $result; + return self::getRequestMap($headers, $blacklist, $whitelist); } } \ No newline at end of file diff --git a/src/Stackify/Log/Transport/Config/AbstractConfig.php b/src/Stackify/Log/Transport/Config/AbstractConfig.php index ea6b56a..0d7cda7 100644 --- a/src/Stackify/Log/Transport/Config/AbstractConfig.php +++ b/src/Stackify/Log/Transport/Config/AbstractConfig.php @@ -154,7 +154,7 @@ public function __construct() $this->CaptureGetVariablesBlacklist = null; $this->CaptureSessionVariables = true; - $this->CaptureSessionVariablesWhitelist = null; + $this->CaptureSessionVariablesWhitelist = array('*'); $this->CaptureSessionVariablesBlacklist = array('*'); $this->CaptureErrorHeaders = true; @@ -162,7 +162,7 @@ public function __construct() $this->CaptureErrorHeadersBlacklist = null; $this->CaptureErrorCookies = true; - $this->CaptureErrorCookiesWhitelist = null; + $this->CaptureErrorCookiesWhitelist = array('*'); $this->CaptureErrorCookiesBlacklist = array('*'); $this->CaptureRawPostData = true; From 94dea14ad2f8d908afebad71977c8a3bae4fa7a2 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Thu, 5 Nov 2020 08:19:17 +0800 Subject: [PATCH 11/13] Add note for get debug transport level --- src/Stackify/Log/Transport/AbstractTransport.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Stackify/Log/Transport/AbstractTransport.php b/src/Stackify/Log/Transport/AbstractTransport.php index 224ac89..81fd3c1 100644 --- a/src/Stackify/Log/Transport/AbstractTransport.php +++ b/src/Stackify/Log/Transport/AbstractTransport.php @@ -111,6 +111,8 @@ private function log($message, $args, $success = true) */ public function getDebug() { + // If debug is not set on the transport level + // then Logger level Debug takes precedence if ($this->debug == false && $this->agentConfig) { return $this->agentConfig->getDebug(); } From afc79af76fe2a305f53742c4ebf4d73c19853941 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Thu, 5 Nov 2020 08:57:41 +0800 Subject: [PATCH 12/13] Update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7297d54..334b5d6 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ $logger = new Logger('application_name', 'environment_name', $transport, true); ### **Configuration Settings** - This allow users to override default settings of the logger (Masking Request Variables, Session, Cookie or Updating connection properties to different Transports etc.) +- **Note** - For the `Whitelist/Blackist` setting. Anything `falsy` (`null`, `false`, `array()` etc. - Refer to php [empty](https://www.php.net/manual/en/function.empty.php) function checking) will be considered as `Do Not Track` - No variable data will be processed. #### Logger Level ```php From be1dfdcaf68d4d034c0822b3bd65fb7b8b810a16 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Fri, 6 Nov 2020 01:25:04 +0800 Subject: [PATCH 13/13] Update fallback request map parameters based on default settings --- src/Stackify/Log/Entities/Api/StackifyError.php | 2 +- src/Stackify/Log/Entities/Api/WebRequestDetail.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Stackify/Log/Entities/Api/StackifyError.php b/src/Stackify/Log/Entities/Api/StackifyError.php index 4c5cf2f..ece2f8c 100644 --- a/src/Stackify/Log/Entities/Api/StackifyError.php +++ b/src/Stackify/Log/Entities/Api/StackifyError.php @@ -65,7 +65,7 @@ private function getEnvironmentVariables() ? WebRequestDetail::getRequestMap($_SERVER, $agentConfig->getCaptureServerVariablesBlacklist(), $agentConfig->getCaptureServerVariablesWhitelist()) : null; } - return isset($_SERVER) ? WebRequestDetail::getRequestMap($_SERVER) : null; + return isset($_SERVER) ? WebRequestDetail::getRequestMap($_SERVER, null, array('*')) : null; } } diff --git a/src/Stackify/Log/Entities/Api/WebRequestDetail.php b/src/Stackify/Log/Entities/Api/WebRequestDetail.php index d4ab8f4..d47ae93 100644 --- a/src/Stackify/Log/Entities/Api/WebRequestDetail.php +++ b/src/Stackify/Log/Entities/Api/WebRequestDetail.php @@ -174,11 +174,11 @@ private function __construct() : null; $this->PostDataRaw = $agentConfig->getCaptureRawPostData() ? file_get_contents('php://input'): null; } else { - $this->Headers = $this->getHeaders(); - $this->Cookies = isset($_COOKIE) ? self::getRequestMap($_COOKIE, ['*']) : null; - $this->QueryString = isset($_GET) ? self::getRequestMap($_GET) : null; - $this->PostData = isset($_POST) ? self::getRequestMap($_POST) : null; - $this->SessionData = isset($_SESSION) ? self::getRequestMap($_SESSION, ['*']) : null; + $this->Headers = $this->getHeaders(null, array('*')); + $this->Cookies = isset($_COOKIE) ? self::getRequestMap($_COOKIE, array('*'), array('*')) : null; + $this->QueryString = isset($_GET) ? self::getRequestMap($_GET, null, array('*')) : null; + $this->PostData = isset($_POST) ? self::getRequestMap($_POST, null, array('*')) : null; + $this->SessionData = isset($_SESSION) ? self::getRequestMap($_SESSION, array('*'), array('*')) : null; $this->PostDataRaw = file_get_contents('php://input'); } }