xref: /plugin/combo/ComboStrap/LogUtility.php (revision e96d9362d1f22d36e4f794b44eacf372d51ce087)
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";
570e43c1dbSgerardnico    /**
580e43c1dbSgerardnico     *
590e43c1dbSgerardnico     * @var bool
600e43c1dbSgerardnico     */
610e43c1dbSgerardnico    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     *
112*e96d9362Sgerardnico     * @param null|string $msg - may be null always this is the default if a variable is not initialized.
11337748cd8SNickeau     * @param int $logLevel
11437748cd8SNickeau     * @param null $canonical
11537748cd8SNickeau     */
116*e96d9362Sgerardnico    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)
2520e43c1dbSgerardnico            && 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    }
2750e43c1dbSgerardnico
2760e43c1dbSgerardnico    public static function disableThrowExceptionOnDevTest()
2770e43c1dbSgerardnico    {
2780e43c1dbSgerardnico        self::$throwExceptionOnDevTest = false;
2790e43c1dbSgerardnico    }
2800e43c1dbSgerardnico
2810e43c1dbSgerardnico    public static function enableThrowExceptionOnDevTest()
2820e43c1dbSgerardnico    {
2830e43c1dbSgerardnico        self::$throwExceptionOnDevTest = true;
2840e43c1dbSgerardnico    }
28537748cd8SNickeau}
286