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 $message = trim($message); 69 if ($message === "") { 70 $level = LogUtility::LVL_MSG_ERROR; 71 $message = "The passed message to the log was empty. BackTrace: \n"; 72 ob_start(); 73 debug_print_backtrace(); 74 $trace = ob_get_contents(); 75 ob_end_clean(); 76 $message .= $trace; 77 self::log2file($message, $level, $canonical); 78 self::throwErrorIfTest($level, $message); 79 return; 80 } 81 82 /** 83 * Log to frontend 84 */ 85 self::log2FrontEnd($message, $level, $canonical); 86 87 /** 88 * Log level passed for a page (only for file used) 89 * to not allow an attacker to see all errors in frontend 90 */ 91 global $INPUT; 92 $loglevelProp = $INPUT->str(self::LOGLEVEL_URI_QUERY_PROPERTY, null); 93 if (!empty($loglevelProp)) { 94 $level = $loglevelProp; 95 } 96 /** 97 * TODO: Make it a configuration ? 98 */ 99 if ($level >= self::LVL_MSG_WARNING) { 100 self::log2file($message, $level, $canonical); 101 } 102 103 /** 104 * If test, we throw an error 105 */ 106 self::throwErrorIfTest($level, $message); 107 } 108 109 /** 110 * Print log to a file 111 * 112 * Adapted from {@link dbglog} 113 * Note: {@link dbg()} dbg print to the web page 114 * 115 * @param string $msg 116 * @param int $logLevel 117 * @param null $canonical 118 */ 119 static function log2file($msg, $logLevel = self::LVL_MSG_INFO, $canonical = null) 120 { 121 122 if (PluginUtility::isTest() || $logLevel >= self::LVL_MSG_WARNING) { 123 124 $prefix = PluginUtility::$PLUGIN_NAME; 125 if (!empty($canonical)) { 126 $prefix .= ' - ' . $canonical; 127 } 128 $msg = $prefix . ' - ' . $msg; 129 130 global $INPUT; 131 global $conf; 132 133 /** 134 * Adding page - context information 135 * We are not using {@link Page::createPageFromRequestedPage()} 136 * because it throws an error message when the environment 137 * is not good, creating a recursive call. 138 */ 139 $id = PluginUtility::getMainPageDokuwikiId(); 140 141 $file = $conf['cachedir'] . '/debug.log'; 142 $fh = fopen($file, 'a'); 143 if ($fh) { 144 $sep = " - "; 145 fwrite($fh, date('c') . $sep . self::LVL_NAME[$logLevel] . $sep . $msg . $sep . $INPUT->server->str('REMOTE_ADDR') . $sep . $id . "\n"); 146 fclose($fh); 147 } 148 149 150 self::throwErrorIfTest($logLevel, $msg); 151 152 153 } 154 155 } 156 157 /** 158 * @param $message 159 * @param $level 160 * @param $canonical 161 * @param bool $withIconURL 162 */ 163 public static function log2FrontEnd($message, $level, $canonical = "support", $withIconURL = true) 164 { 165 /** 166 * If we are not in the console 167 * and not in test 168 * we test that the message comes in the front end 169 * (example {@link \plugin_combo_frontmatter_test} 170 */ 171 $isTerminal = Console::isConsoleRun(); 172 if ($isTerminal) { 173 if (!defined('DOKU_UNITTEST')) { 174 /** 175 * such as {@link cli_plugin_combo} 176 */ 177 $userAgent = "cli"; 178 } else { 179 $userAgent = "phpunit"; 180 } 181 } else { 182 $userAgent = "browser"; 183 } 184 185 switch ($userAgent) { 186 case "cli": 187 echo "$message\n"; 188 break; 189 case "phpunit": 190 case "browser": 191 default: 192 $htmlMsg = PluginUtility::getDocumentationHyperLink("", PluginUtility::$PLUGIN_NAME, $withIconURL); 193 if ($canonical != null) { 194 $htmlMsg = PluginUtility::getDocumentationHyperLink($canonical, ucfirst(str_replace(":", " ", $canonical))); 195 } 196 197 /** 198 * Adding page - context information 199 * We are not creating the page 200 * direction from {@link Page::createPageFromRequestedPage()} 201 * because it throws an error message when the environment 202 * is not good, creating a recursive call. 203 */ 204 $id = PluginUtility::getMainPageDokuwikiId(); 205 if ($id != null) { 206 207 /** 208 * We don't use any Page object to not 209 * create a cycle while building it 210 */ 211 $url = wl($id, [], true); 212 $htmlMsg .= " - <a href=\"$url\">$id</a>"; 213 214 } 215 216 /** 217 * 218 */ 219 $htmlMsg .= " - " . $message; 220 if ($level > self::LVL_MSG_DEBUG) { 221 $dokuWikiLevel = self::LVL_TO_MSG_LEVEL[$level]; 222 msg($htmlMsg, $dokuWikiLevel, '', '', MSG_USERS_ONLY); 223 } 224 } 225 } 226 227 /** 228 * Log a message to the browser console 229 * @param $message 230 */ 231 public static function log2BrowserConsole($message) 232 { 233 // TODO 234 } 235 236 private static function throwErrorIfTest($level, $message) 237 { 238 if (PluginUtility::isTest() 239 && ($level >= self::LVL_MSG_WARNING) 240 ) { 241 throw new LogException($message); 242 } 243 } 244} 245