137748cd8SNickeau<?php 237748cd8SNickeau/** 337748cd8SNickeau * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 437748cd8SNickeau * 537748cd8SNickeau * This source code is licensed under the GPL license found in the 637748cd8SNickeau * COPYING file in the root directory of this source tree. 737748cd8SNickeau * 837748cd8SNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 937748cd8SNickeau * @author ComboStrap <support@combostrap.com> 1037748cd8SNickeau * 1137748cd8SNickeau */ 1237748cd8SNickeau 1337748cd8SNickeaunamespace ComboStrap; 1437748cd8SNickeau 1537748cd8SNickeaurequire_once(__DIR__ . '/PluginUtility.php'); 1637748cd8SNickeau 1737748cd8SNickeauclass LogUtility 1837748cd8SNickeau{ 1937748cd8SNickeau 2037748cd8SNickeau /** 2137748cd8SNickeau * Constant for the function {@link msg()} 2237748cd8SNickeau * -1 = error, 0 = info, 1 = success, 2 = notify 2337748cd8SNickeau * (Not even in order of importance) 2437748cd8SNickeau */ 2537748cd8SNickeau const LVL_MSG_ERROR = 4; //-1; 2637748cd8SNickeau const LVL_MSG_WARNING = 3; //2; 2737748cd8SNickeau const LVL_MSG_SUCCESS = 2; //1; 2837748cd8SNickeau const LVL_MSG_INFO = 1; //0; 2937748cd8SNickeau const LVL_MSG_DEBUG = 0; //3; 3037748cd8SNickeau 3137748cd8SNickeau 3237748cd8SNickeau /** 3337748cd8SNickeau * Id level to name 3437748cd8SNickeau */ 3537748cd8SNickeau const LVL_NAME = array( 3637748cd8SNickeau 0 => "debug", 3737748cd8SNickeau 1 => "info", 3837748cd8SNickeau 3 => "warning", 3937748cd8SNickeau 2 => "success", 4037748cd8SNickeau 4 => "error" 4137748cd8SNickeau ); 4237748cd8SNickeau 4337748cd8SNickeau /** 4437748cd8SNickeau * Id level to name 4537748cd8SNickeau * {@link msg()} constant 4637748cd8SNickeau */ 4737748cd8SNickeau const LVL_TO_MSG_LEVEL = array( 4837748cd8SNickeau 0 => 3, 4937748cd8SNickeau 1 => 0, 5037748cd8SNickeau 2 => 1, 5137748cd8SNickeau 3 => 2, 5237748cd8SNickeau 4 => -1 5337748cd8SNickeau ); 5437748cd8SNickeau 5537748cd8SNickeau 5637748cd8SNickeau const LOGLEVEL_URI_QUERY_PROPERTY = "loglevel"; 57*0e43c1dbSgerardnico /** 58*0e43c1dbSgerardnico * 59*0e43c1dbSgerardnico * @var bool 60*0e43c1dbSgerardnico */ 61*0e43c1dbSgerardnico private static $throwExceptionOnDevTest = true; 6237748cd8SNickeau 6337748cd8SNickeau /** 6437748cd8SNickeau * Send a message to a manager and log it 6537748cd8SNickeau * Fail if in test 6637748cd8SNickeau * @param string $message 6737748cd8SNickeau * @param int $level - the level see LVL constant 6837748cd8SNickeau * @param string $canonical - the canonical 6937748cd8SNickeau */ 70c3437056SNickeau public static function msg(string $message, int $level = self::LVL_MSG_ERROR, string $canonical = "support") 7137748cd8SNickeau { 7237748cd8SNickeau 73d61dea15Sgerardnico try { 74d61dea15Sgerardnico self::messageNotEmpty($message); 75d61dea15Sgerardnico } catch (ExceptionCombo $e) { 76d61dea15Sgerardnico self::log2file($e->getMessage(), LogUtility::LVL_MSG_ERROR, $canonical); 77e7bbc4b8Sgerardnico } 78e7bbc4b8Sgerardnico 7937748cd8SNickeau /** 8037748cd8SNickeau * Log to frontend 8137748cd8SNickeau */ 82c3437056SNickeau self::log2FrontEnd($message, $level, $canonical); 8337748cd8SNickeau 8437748cd8SNickeau /** 8537748cd8SNickeau * Log level passed for a page (only for file used) 8637748cd8SNickeau * to not allow an attacker to see all errors in frontend 8737748cd8SNickeau */ 8837748cd8SNickeau global $INPUT; 8937748cd8SNickeau $loglevelProp = $INPUT->str(self::LOGLEVEL_URI_QUERY_PROPERTY, null); 9037748cd8SNickeau if (!empty($loglevelProp)) { 9137748cd8SNickeau $level = $loglevelProp; 9237748cd8SNickeau } 932f4da794Sgerardnico /** 942f4da794Sgerardnico * TODO: Make it a configuration ? 952f4da794Sgerardnico */ 962f4da794Sgerardnico if ($level >= self::LVL_MSG_WARNING) { 9737748cd8SNickeau self::log2file($message, $level, $canonical); 982f4da794Sgerardnico } 9937748cd8SNickeau 10037748cd8SNickeau /** 10137748cd8SNickeau * If test, we throw an error 10237748cd8SNickeau */ 103c3437056SNickeau self::throwErrorIfTest($level, $message); 10437748cd8SNickeau } 10537748cd8SNickeau 10637748cd8SNickeau /** 10737748cd8SNickeau * Print log to a file 10837748cd8SNickeau * 10937748cd8SNickeau * Adapted from {@link dbglog} 11037748cd8SNickeau * Note: {@link dbg()} dbg print to the web page 11137748cd8SNickeau * 11237748cd8SNickeau * @param string $msg 11337748cd8SNickeau * @param int $logLevel 11437748cd8SNickeau * @param null $canonical 11537748cd8SNickeau */ 116d61dea15Sgerardnico static function log2file(string $msg, int $logLevel = self::LVL_MSG_INFO, $canonical = null) 11737748cd8SNickeau { 11837748cd8SNickeau 119d61dea15Sgerardnico try { 120d61dea15Sgerardnico self::messageNotEmpty($msg); 121d61dea15Sgerardnico } catch (ExceptionCombo $e) { 122d61dea15Sgerardnico $msg = $e->getMessage(); 123d61dea15Sgerardnico $logLevel = self::LVL_MSG_ERROR; 124d61dea15Sgerardnico } 125d61dea15Sgerardnico 126c3437056SNickeau if (PluginUtility::isTest() || $logLevel >= self::LVL_MSG_WARNING) { 12737748cd8SNickeau 12837748cd8SNickeau $prefix = PluginUtility::$PLUGIN_NAME; 12937748cd8SNickeau if (!empty($canonical)) { 13037748cd8SNickeau $prefix .= ' - ' . $canonical; 13137748cd8SNickeau } 13237748cd8SNickeau $msg = $prefix . ' - ' . $msg; 13337748cd8SNickeau 13437748cd8SNickeau global $INPUT; 13537748cd8SNickeau global $conf; 13637748cd8SNickeau 137c3437056SNickeau /** 138c3437056SNickeau * Adding page - context information 139c3437056SNickeau * We are not using {@link Page::createPageFromRequestedPage()} 140c3437056SNickeau * because it throws an error message when the environment 141c3437056SNickeau * is not good, creating a recursive call. 142c3437056SNickeau */ 143c3437056SNickeau $id = PluginUtility::getMainPageDokuwikiId(); 14437748cd8SNickeau 14537748cd8SNickeau $file = $conf['cachedir'] . '/debug.log'; 14637748cd8SNickeau $fh = fopen($file, 'a'); 14737748cd8SNickeau if ($fh) { 14837748cd8SNickeau $sep = " - "; 14937748cd8SNickeau fwrite($fh, date('c') . $sep . self::LVL_NAME[$logLevel] . $sep . $msg . $sep . $INPUT->server->str('REMOTE_ADDR') . $sep . $id . "\n"); 15037748cd8SNickeau fclose($fh); 15137748cd8SNickeau } 152c3437056SNickeau 153c3437056SNickeau 154c3437056SNickeau self::throwErrorIfTest($logLevel, $msg); 155c3437056SNickeau 156c3437056SNickeau 15737748cd8SNickeau } 15837748cd8SNickeau 15937748cd8SNickeau } 16037748cd8SNickeau 16137748cd8SNickeau /** 16237748cd8SNickeau * @param $message 16337748cd8SNickeau * @param $level 164d61dea15Sgerardnico * @param string $canonical 16537748cd8SNickeau * @param bool $withIconURL 16637748cd8SNickeau */ 16737748cd8SNickeau public static function log2FrontEnd($message, $level, $canonical = "support", $withIconURL = true) 16837748cd8SNickeau { 169d61dea15Sgerardnico 170d61dea15Sgerardnico try { 171d61dea15Sgerardnico self::messageNotEmpty($message); 172d61dea15Sgerardnico } catch (ExceptionCombo $e) { 173d61dea15Sgerardnico $message = $e->getMessage(); 174d61dea15Sgerardnico $level = self::LVL_MSG_ERROR; 175d61dea15Sgerardnico } 176d61dea15Sgerardnico 17737748cd8SNickeau /** 17837748cd8SNickeau * If we are not in the console 17937748cd8SNickeau * and not in test 18037748cd8SNickeau * we test that the message comes in the front end 18137748cd8SNickeau * (example {@link \plugin_combo_frontmatter_test} 18237748cd8SNickeau */ 183c3437056SNickeau $isTerminal = Console::isConsoleRun(); 184c3437056SNickeau if ($isTerminal) { 18537748cd8SNickeau if (!defined('DOKU_UNITTEST')) { 186c3437056SNickeau /** 187c3437056SNickeau * such as {@link cli_plugin_combo} 188c3437056SNickeau */ 189c3437056SNickeau $userAgent = "cli"; 190c3437056SNickeau } else { 191c3437056SNickeau $userAgent = "phpunit"; 19237748cd8SNickeau } 193c3437056SNickeau } else { 194c3437056SNickeau $userAgent = "browser"; 19537748cd8SNickeau } 196c3437056SNickeau 197c3437056SNickeau switch ($userAgent) { 198c3437056SNickeau case "cli": 199c3437056SNickeau echo "$message\n"; 200c3437056SNickeau break; 201c3437056SNickeau case "phpunit": 202c3437056SNickeau case "browser": 203c3437056SNickeau default: 204c3437056SNickeau $htmlMsg = PluginUtility::getDocumentationHyperLink("", PluginUtility::$PLUGIN_NAME, $withIconURL); 20537748cd8SNickeau if ($canonical != null) { 206c3437056SNickeau $htmlMsg = PluginUtility::getDocumentationHyperLink($canonical, ucfirst(str_replace(":", " ", $canonical))); 20737748cd8SNickeau } 20837748cd8SNickeau 20937748cd8SNickeau /** 21037748cd8SNickeau * Adding page - context information 21137748cd8SNickeau * We are not creating the page 212c3437056SNickeau * direction from {@link Page::createPageFromRequestedPage()} 21337748cd8SNickeau * because it throws an error message when the environment 21437748cd8SNickeau * is not good, creating a recursive call. 21537748cd8SNickeau */ 216c3437056SNickeau $id = PluginUtility::getMainPageDokuwikiId(); 21737748cd8SNickeau if ($id != null) { 218c3437056SNickeau 219c3437056SNickeau /** 220c3437056SNickeau * We don't use any Page object to not 221c3437056SNickeau * create a cycle while building it 222c3437056SNickeau */ 223c3437056SNickeau $url = wl($id, [], true); 224c3437056SNickeau $htmlMsg .= " - <a href=\"$url\">$id</a>"; 225c3437056SNickeau 22637748cd8SNickeau } 22737748cd8SNickeau 22837748cd8SNickeau /** 22937748cd8SNickeau * 23037748cd8SNickeau */ 23137748cd8SNickeau $htmlMsg .= " - " . $message; 23237748cd8SNickeau if ($level > self::LVL_MSG_DEBUG) { 23337748cd8SNickeau $dokuWikiLevel = self::LVL_TO_MSG_LEVEL[$level]; 23437748cd8SNickeau msg($htmlMsg, $dokuWikiLevel, '', '', MSG_USERS_ONLY); 23537748cd8SNickeau } 23637748cd8SNickeau } 23737748cd8SNickeau } 23837748cd8SNickeau 23937748cd8SNickeau /** 24037748cd8SNickeau * Log a message to the browser console 24137748cd8SNickeau * @param $message 24237748cd8SNickeau */ 24337748cd8SNickeau public static function log2BrowserConsole($message) 24437748cd8SNickeau { 24537748cd8SNickeau // TODO 24637748cd8SNickeau } 247c3437056SNickeau 248c3437056SNickeau private static function throwErrorIfTest($level, $message) 249c3437056SNickeau { 250c3437056SNickeau if (PluginUtility::isTest() 251c3437056SNickeau && ($level >= self::LVL_MSG_WARNING) 252*0e43c1dbSgerardnico && self::$throwExceptionOnDevTest 253c3437056SNickeau ) { 254c3437056SNickeau throw new LogException($message); 255c3437056SNickeau } 256c3437056SNickeau } 257d61dea15Sgerardnico 258d61dea15Sgerardnico /** 259d61dea15Sgerardnico * @param string $message 260d61dea15Sgerardnico * @throws ExceptionCombo 261d61dea15Sgerardnico */ 262d61dea15Sgerardnico private static function messageNotEmpty(string $message) 263d61dea15Sgerardnico { 264d61dea15Sgerardnico $message = trim($message); 265d61dea15Sgerardnico if ($message === "" || $message === null) { 266d61dea15Sgerardnico $newMessage = "The passed message to the log was empty or null. BackTrace: \n"; 267d61dea15Sgerardnico ob_start(); 268d61dea15Sgerardnico debug_print_backtrace(); 269d61dea15Sgerardnico $trace = ob_get_contents(); 270d61dea15Sgerardnico ob_end_clean(); 271d61dea15Sgerardnico $newMessage .= $trace; 272d61dea15Sgerardnico throw new ExceptionCombo($newMessage); 273d61dea15Sgerardnico } 274d61dea15Sgerardnico } 275*0e43c1dbSgerardnico 276*0e43c1dbSgerardnico public static function disableThrowExceptionOnDevTest() 277*0e43c1dbSgerardnico { 278*0e43c1dbSgerardnico self::$throwExceptionOnDevTest = false; 279*0e43c1dbSgerardnico } 280*0e43c1dbSgerardnico 281*0e43c1dbSgerardnico public static function enableThrowExceptionOnDevTest() 282*0e43c1dbSgerardnico { 283*0e43c1dbSgerardnico self::$throwExceptionOnDevTest = true; 284*0e43c1dbSgerardnico } 28537748cd8SNickeau} 286