1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\System;
9:
10: /**
11: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
12: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
13: * @copyright Copyright (c) 2019, Afterlogic Corp.
14: *
15: * @category Core
16: */
17: class Logger
18: {
19: /**
20: * @var string
21: */
22: public static $sEventLogPrefix = 'event-';
23:
24: /**
25: * @param string $sDesc
26: * @param string $sModuleName
27: */
28: public static function LogEvent($sDesc, $sModuleName = '')
29: {
30: $oSettings = &Api::GetSettings();
31: if ($oSettings && $oSettings->EnableEventLogging) {
32: $sDate = gmdate('H:i:s');
33: $iIp = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'unknown';
34: $sUserId = Api::getAuthenticatedUserId();
35:
36: self::Log('Event: '.$sUserId.' > '.$sDesc);
37: self::LogOnly('['.$sDate.']['.$iIp.']['.$sUserId.']['.$sModuleName.'] > '.$sDesc, self::GetLogFileDir().self::GetLogFileName(self::$sEventLogPrefix));
38: }
39: }
40:
41: /**
42: * @param mixed $mObject
43: * @param int $iLogLevel = \Aurora\System\Enums\LogLevel::Full
44: * @param string $sFilePrefix = ''
45: */
46: public static function LogObject($mObject, $iLogLevel = Enums\LogLevel::Full, $sFilePrefix = '')
47: {
48: self::Log(print_r($mObject, true), $iLogLevel, $sFilePrefix);
49: }
50:
51: /**
52: * @param Exception $mObject
53: * @param int $iLogLevel = \Aurora\System\Enums\LogLevel::Error
54: * @param string $sFilePrefix = ''
55: */
56: public static function LogException($mObject, $iLogLevel = Enums\LogLevel::Error, $sFilePrefix = '')
57: {
58: $sMessage = '';
59:
60: $oSettings =& Api::GetSettings();
61: // if ($oSettings && $oSettings->GetValue('LogStackTrace', false))
62: // {
63: // $sMessage = (string) $mObject;
64: // }
65: // else
66: // {
67: $sMessage = 'Exception: ' . (string) $mObject . ', Code: ' . $mObject->getCode() . ', Message: ' . $mObject->getMessage();
68: // }
69:
70: if (0 < \count(Api::$aSecretWords)) {
71: $sMessage = \str_replace(Api::$aSecretWords, '*******', $sMessage);
72: }
73:
74: self::Log($sMessage, $iLogLevel, $sFilePrefix);
75: }
76:
77: /**
78: * @param string $sFilePrefix = ''
79: *
80: * @return string
81: */
82: public static function GetLogFileName($sFilePrefix = '', $iTimestamp = 0)
83: {
84: $oSettings =& Api::GetSettings();
85:
86: $sFileName = "log.txt";
87:
88: if ($oSettings && $oSettings->LogFileName) {
89: $fCallback = ($iTimestamp === 0)
90: ? function ($matches) {
91: return date($matches[1]);
92: }
93: : function ($matches) use ($iTimestamp) {
94: return date($matches[1], $iTimestamp);
95: };
96: $sFileName = preg_replace_callback('/\{([\w|-]*)\}/', $fCallback, $oSettings->LogFileName);
97: }
98:
99: return $sFilePrefix.$sFileName;
100: }
101:
102: public static function GetLogFileDir()
103: {
104: static $bDir = null;
105: static $sLogDir = null;
106:
107: if (null === $sLogDir) {
108: $oSettings =& Api::GetSettings();
109: if ($oSettings) {
110: $sS = $oSettings->GetValue('LogCustomFullPath', '');
111: $sLogDir = empty($sS) ? Api::DataPath().'/logs/' : rtrim(trim($sS), '\\/').'/';
112: }
113: }
114:
115: if (null === $bDir) {
116: $bDir = true;
117: if (!@is_dir($sLogDir)) {
118: @mkdir($sLogDir, 0777);
119: }
120: }
121:
122: return $sLogDir;
123: }
124:
125: /**
126: * @return \MailSo\Log\Logger
127: */
128: public static function SystemLogger()
129: {
130: static $oLogger = null;
131: if (null === $oLogger) {
132: $oLogger = \MailSo\Log\Logger::NewInstance()
133: ->Add(
134: \MailSo\Log\Drivers\Callback::NewInstance(function ($sDesc) {
135: self::Log($sDesc);
136: })->DisableTimePrefix()->DisableGuidPrefix()
137: )
138: ->AddForbiddenType(\MailSo\Log\Enumerations\Type::TIME)
139: ;
140:
141: $oSettings =& Api::GetSettings();
142: $oLogger->bLogStackTrace = ($oSettings && $oSettings->GetValue('LogStackTrace', false));
143: }
144:
145: return $oLogger;
146: }
147:
148: /**
149: * @param string $sDesc
150: * @param string $sLogFile
151: */
152: private static function dbDebugBacktrace($sDesc, $sLogFile)
153: {
154: static $iDbBacktraceCount = null;
155:
156: if (null === $iDbBacktraceCount) {
157: $oSettings =& Api::GetSettings();
158: $iDbBacktraceCount = (int) $oSettings->GetValue('DBDebugBacktraceLimit', 0);
159: if (!function_exists('debug_backtrace') || version_compare(PHP_VERSION, '5.4.0') < 0) {
160: $iDbBacktraceCount = 0;
161: }
162: }
163:
164: if (0 < $iDbBacktraceCount && is_string($sDesc) &&
165: (false !== strpos($sDesc, 'DB[') || false !== strpos($sDesc, 'DB ERROR'))) {
166: $bSkip = true;
167: $sLogData = '';
168: $iCount = $iDbBacktraceCount;
169:
170: foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 20) as $aData) {
171: if ($aData && isset($aData['function']) && !in_array(strtolower($aData['function']), array(
172: 'log', 'logonly', 'logend', 'logevent', 'logexception', 'logobject', 'dbdebugbacktrace'))) {
173: $bSkip = false;
174: }
175:
176: if (!$bSkip) {
177: $iCount--;
178: if (isset($aData['class'], $aData['type'], $aData['function'])) {
179: $sLogData .= $aData['class'].$aData['type'].$aData['function'];
180: } elseif (isset($aData['function'])) {
181: $sLogData .= $aData['function'];
182: }
183:
184: if (isset($aData['file'])) {
185: $sLogData .= ' ../'.basename($aData['file']);
186: }
187: if (isset($aData['line'])) {
188: $sLogData .= ' *'.$aData['line'];
189: }
190:
191: $sLogData .= "\n";
192: }
193:
194: if (0 === $iCount) {
195: break;
196: }
197: }
198:
199: if (0 < strlen($sLogData)) {
200: try {
201: @error_log('['.\MailSo\Log\Logger::Guid().'][DB/backtrace]'.AU_API_CRLF.trim($sLogData).AU_API_CRLF, 3, $sLogFile);
202: } catch (\Exception $oE) {
203: }
204: }
205: }
206: }
207:
208: /**
209: * @param string $sDesc
210: * @param int $iLogLevel = \Aurora\System\Enums\LogLevel::Full
211: * @param string $sFilePrefix = ''
212: * @param bool $bIdDb = false
213: */
214: public static function Log($sDesc, $iLogLevel = Enums\LogLevel::Full, $sFilePrefix = '')
215: {
216: static $bIsFirst = true;
217:
218: $oSettings = &Api::GetSettings();
219:
220: if ($oSettings && $oSettings->EnableLogging && $iLogLevel <= $oSettings->LoggingLevel) {
221: try {
222: $oAuthenticatedUser = Api::getAuthenticatedUser();
223: } catch (\Exception $oEx) {
224: $oAuthenticatedUser = false;
225: }
226: $sFirstPrefix = "";
227: if (isset($oAuthenticatedUser)) {
228: $sFirstPrefix = isset($oAuthenticatedUser->WriteSeparateLog) && $oAuthenticatedUser->WriteSeparateLog ? $oAuthenticatedUser->PublicId . '-' : '';
229: }
230: $sLogFile = self::GetLogFileDir() . self::GetLogFileName($sFirstPrefix . $sFilePrefix);
231:
232: $sGuid = \MailSo\Log\Logger::Guid();
233: $aMicro = explode('.', microtime(true));
234: $sDate = gmdate('H:i:s.').str_pad((isset($aMicro[1]) ? substr($aMicro[1], 0, 2) : '0'), 2, '0');
235: if ($bIsFirst) {
236: $sUri = Utils::RequestUri();
237: $bIsFirst = false;
238: $sPost = (isset($_POST) && count($_POST) > 0) ? '[POST('.count($_POST).')]' : '[GET]';
239:
240: self::LogOnly(AU_API_CRLF.'['.$sDate.']['.$sGuid.'] '.$sPost.'[ip:'.(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'unknown').'] '.$sUri, $sLogFile);
241: if (!empty($sPost)) {
242: if ($oSettings->GetValue('LogPostView', false)) {
243: self::LogOnly('['.$sDate.']['.$sGuid.'] POST > '.print_r($_POST, true), $sLogFile);
244: } else {
245: self::LogOnly('['.$sDate.']['.$sGuid.'] POST > ['.implode(', ', array_keys($_POST)).']', $sLogFile);
246: }
247: }
248: self::LogOnly('['.$sDate.']['.$sGuid.']', $sLogFile);
249: }
250:
251: self::LogOnly('['.$sDate.']['.$sGuid.'] '.(is_string($sDesc) ? $sDesc : print_r($sDesc, true)), $sLogFile);
252: }
253: }
254:
255: /**
256: * @param string $sDesc
257: * @param string $sLogFile
258: */
259: public static function LogOnly($sDesc, $sLogFile)
260: {
261: if (0 < \count(Api::$aSecretWords)) {
262: $sDesc = \str_replace(Api::$aSecretWords, '*******', $sDesc);
263: }
264:
265: try {
266: @error_log($sDesc.AU_API_CRLF, 3, $sLogFile);
267: } catch (\Exception $oE) {
268: }
269:
270: self::dbDebugBacktrace($sDesc, $sLogFile);
271: }
272:
273: public static function LogSql($sDesc, $iLogLevel = Enums\LogLevel::Full)
274: {
275: if (Api::$bUseDbLog) {
276: $oSettings = &Api::GetSettings();
277:
278: if ($oSettings && $oSettings->EnableLogging && $iLogLevel <= $oSettings->LoggingLevel) {
279: $sLogFile = self::GetLogFileDir() . self::GetLogFileName('sql-');
280:
281: $sGuid = \MailSo\Log\Logger::Guid();
282: $aMicro = explode('.', microtime(true));
283: $sDate = gmdate('H:i:s.').str_pad((isset($aMicro[1]) ? substr($aMicro[1], 0, 2) : '0'), 2, '0');
284:
285: self::LogOnly('['.$sDate.']['.$sGuid.'] '. $sDesc, $sLogFile);
286: }
287: }
288: }
289:
290: public static function ClearLog($sFileFullPath)
291: {
292: return (@file_exists($sFileFullPath)) ? @unlink($sFileFullPath) : true;
293: }
294:
295: public static function RemoveSeparateLogs()
296: {
297: $sLogDir = self::GetLogFileDir();
298: $sLogFile = self::GetLogFileName();
299: if (is_dir($sLogDir)) {
300: $aLogFiles = array_diff(scandir($sLogDir), array('..', '.'));
301: foreach ($aLogFiles as $sFileName) {
302: if ($sFileName !== $sLogFile && $sFileName !== (self::$sEventLogPrefix . $sLogFile) && strpos($sFileName, $sLogFile) !== false) {
303: unlink($sLogDir.$sFileName);
304: }
305: }
306: }
307: }
308:
309: public static function removeOldLogs()
310: {
311: $sLogDir = self::GetLogFileDir();
312: $sLogFile = self::GetLogFileName();
313: $oSettings = &Api::GetSettings();
314:
315: if ($oSettings) {
316: $bRemoveOldLogs = $oSettings->GetValue('RemoveOldLogs', true);
317:
318: if (is_dir($sLogDir) && $bRemoveOldLogs/* && !file_exists($sLogDir.$sLogFile)*/) {
319: $sYesterdayLogFile = self::GetLogFileName('', time() - 60 * 60 * 24);
320: $aLogFiles = array_diff(scandir($sLogDir), array('..', '.'));
321: foreach ($aLogFiles as $sFileName) {
322: if (strpos($sFileName, $sLogFile) === false && strpos($sFileName, $sYesterdayLogFile) === false && file_exists($sLogDir.$sFileName)) {
323: unlink($sLogDir.$sFileName);
324: }
325: }
326: }
327: }
328: }
329:
330: public static function GetLoggerGuid()
331: {
332: $oSettings = &Api::GetSettings();
333:
334: if ($oSettings && $oSettings->EnableLogging) {
335: return \MailSo\Log\Logger::Guid();
336: }
337:
338: return '';
339: }
340: }
341: