1<?php 2/** 3 * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4 * 5 * This source code is licensed under the GPL license found in the 6 * COPYING file in the root directory of this source tree. 7 * 8 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9 * @author ComboStrap <support@combostrap.com> 10 * 11 */ 12 13namespace ComboStrap; 14 15require_once(__DIR__ . '/PluginUtility.php'); 16 17class LogUtility 18{ 19 20 /** 21 * Constant for the function {@link msg()} 22 * -1 = error, 0 = info, 1 = success, 2 = notify 23 * (Not even in order of importance) 24 */ 25 const LVL_MSG_ERROR = 4; //-1; 26 const LVL_MSG_WARNING = 3; //2; 27 const LVL_MSG_SUCCESS = 2; //1; 28 const LVL_MSG_INFO = 1; //0; 29 const LVL_MSG_DEBUG = 0; //3; 30 31 32 /** 33 * Id level to name 34 */ 35 const LVL_NAME = array( 36 0 => "debug", 37 1 => "info", 38 3 => "warning", 39 2 => "success", 40 4 => "error" 41 ); 42 43 /** 44 * Id level to name 45 * {@link msg()} constant 46 */ 47 const LVL_TO_MSG_LEVEL = array( 48 0 => 3, 49 1 => 0, 50 2 => 1, 51 3 => 2, 52 4 => -1 53 ); 54 55 56 const LOGLEVEL_URI_QUERY_PROPERTY = "loglevel"; 57 58 /** 59 * Send a message to a manager and log it 60 * Fail if in test 61 * @param string $message 62 * @param int $level - the level see LVL constant 63 * @param string $canonical - the canonical 64 */ 65 public static function msg(string $message, int $level = self::LVL_MSG_ERROR, string $canonical = "support") 66 { 67 68 try { 69 self::messageNotEmpty($message); 70 } catch (ExceptionCombo $e) { 71 self::log2file($e->getMessage(), LogUtility::LVL_MSG_ERROR, $canonical); 72 } 73 74 /** 75 * Log to frontend 76 */ 77 self::log2FrontEnd($message, $level, $canonical); 78 79 /** 80 * Log level passed for a page (only for file used) 81 * to not allow an attacker to see all errors in frontend 82 */ 83 global $INPUT; 84 $loglevelProp = $INPUT->str(self::LOGLEVEL_URI_QUERY_PROPERTY, null); 85 if (!empty($loglevelProp)) { 86 $level = $loglevelProp; 87 } 88 /** 89 * TODO: Make it a configuration ? 90 */ 91 if ($level >= self::LVL_MSG_WARNING) { 92 self::log2file($message, $level, $canonical); 93 } 94 95 /** 96 * If test, we throw an error 97 */ 98 self::throwErrorIfTest($level, $message); 99 } 100 101 /** 102 * Print log to a file 103 * 104 * Adapted from {@link dbglog} 105 * Note: {@link dbg()} dbg print to the web page 106 * 107 * @param string $msg 108 * @param int $logLevel 109 * @param null $canonical 110 */ 111 static function log2file(string $msg, int $logLevel = self::LVL_MSG_INFO, $canonical = null) 112 { 113 114 try { 115 self::messageNotEmpty($msg); 116 } catch (ExceptionCombo $e) { 117 $msg = $e->getMessage(); 118 $logLevel = self::LVL_MSG_ERROR; 119 } 120 121 if (PluginUtility::isTest() || $logLevel >= self::LVL_MSG_WARNING) { 122 123 $prefix = PluginUtility::$PLUGIN_NAME; 124 if (!empty($canonical)) { 125 $prefix .= ' - ' . $canonical; 126 } 127 $msg = $prefix . ' - ' . $msg; 128 129 global $INPUT; 130 global $conf; 131 132 /** 133 * Adding page - context information 134 * We are not using {@link Page::createPageFromRequestedPage()} 135 * because it throws an error message when the environment 136 * is not good, creating a recursive call. 137 */ 138 $id = PluginUtility::getMainPageDokuwikiId(); 139 140 $file = $conf['cachedir'] . '/debug.log'; 141 $fh = fopen($file, 'a'); 142 if ($fh) { 143 $sep = " - "; 144 fwrite($fh, date('c') . $sep . self::LVL_NAME[$logLevel] . $sep . $msg . $sep . $INPUT->server->str('REMOTE_ADDR') . $sep . $id . "\n"); 145 fclose($fh); 146 } 147 148 149 self::throwErrorIfTest($logLevel, $msg); 150 151 152 } 153 154 } 155 156 /** 157 * @param $message 158 * @param $level 159 * @param string $canonical 160 * @param bool $withIconURL 161 */ 162 public static function log2FrontEnd($message, $level, $canonical = "support", $withIconURL = true) 163 { 164 165 try { 166 self::messageNotEmpty($message); 167 } catch (ExceptionCombo $e) { 168 $message = $e->getMessage(); 169 $level = self::LVL_MSG_ERROR; 170 } 171 172 /** 173 * If we are not in the console 174 * and not in test 175 * we test that the message comes in the front end 176 * (example {@link \plugin_combo_frontmatter_test} 177 */ 178 $isTerminal = Console::isConsoleRun(); 179 if ($isTerminal) { 180 if (!defined('DOKU_UNITTEST')) { 181 /** 182 * such as {@link cli_plugin_combo} 183 */ 184 $userAgent = "cli"; 185 } else { 186 $userAgent = "phpunit"; 187 } 188 } else { 189 $userAgent = "browser"; 190 } 191 192 switch ($userAgent) { 193 case "cli": 194 echo "$message\n"; 195 break; 196 case "phpunit": 197 case "browser": 198 default: 199 $htmlMsg = PluginUtility::getDocumentationHyperLink("", PluginUtility::$PLUGIN_NAME, $withIconURL); 200 if ($canonical != null) { 201 $htmlMsg = PluginUtility::getDocumentationHyperLink($canonical, ucfirst(str_replace(":", " ", $canonical))); 202 } 203 204 /** 205 * Adding page - context information 206 * We are not creating the page 207 * direction from {@link Page::createPageFromRequestedPage()} 208 * because it throws an error message when the environment 209 * is not good, creating a recursive call. 210 */ 211 $id = PluginUtility::getMainPageDokuwikiId(); 212 if ($id != null) { 213 214 /** 215 * We don't use any Page object to not 216 * create a cycle while building it 217 */ 218 $url = wl($id, [], true); 219 $htmlMsg .= " - <a href=\"$url\">$id</a>"; 220 221 } 222 223 /** 224 * 225 */ 226 $htmlMsg .= " - " . $message; 227 if ($level > self::LVL_MSG_DEBUG) { 228 $dokuWikiLevel = self::LVL_TO_MSG_LEVEL[$level]; 229 msg($htmlMsg, $dokuWikiLevel, '', '', MSG_USERS_ONLY); 230 } 231 } 232 } 233 234 /** 235 * Log a message to the browser console 236 * @param $message 237 */ 238 public static function log2BrowserConsole($message) 239 { 240 // TODO 241 } 242 243 private static function throwErrorIfTest($level, $message) 244 { 245 if (PluginUtility::isTest() 246 && ($level >= self::LVL_MSG_WARNING) 247 ) { 248 throw new LogException($message); 249 } 250 } 251 252 /** 253 * @param string $message 254 * @throws ExceptionCombo 255 */ 256 private static function messageNotEmpty(string $message) 257 { 258 $message = trim($message); 259 if ($message === "" || $message === null) { 260 $newMessage = "The passed message to the log was empty or null. BackTrace: \n"; 261 ob_start(); 262 debug_print_backtrace(); 263 $trace = ob_get_contents(); 264 ob_end_clean(); 265 $newMessage .= $trace; 266 throw new ExceptionCombo($newMessage); 267 } 268 } 269} 270