1

How can I incorporate both database and file logs in cakephp 3.6?

the function for log file already exist, and here it is(ProjectLog.php):

<?php

namespace Project\Util;

use Monolog\Formatter\LineFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

class ProjectLog
{
    /** @var Logger $logger */
    private static $logger;

    /** @var string $filePath */
    private static $filePath;

    /** @var string $key */
    private static $key;

    /**
     * @return string
     */
    public static function getFilePath(): string
    {
        return self::$filePath;
    }

    /**
     * @param string $name
     * @param string $category
     * @param string $client
     * @param string $mall
     * @param bool|null $stdoutFlag
     * @throws \Exception
     */
    public static function setConfig(
        string $name,
        string $category = 'client',
        string $client = 'etc',
        string $mall = 'etc',
        ?bool $stdoutFlag = false
    ) {
        if (empty($name) || empty($category) || empty($client) || empty($mall)) {
            $message = "Logger setting value is incomplete. name:{$name}, category:{$category}, client:{$client}, mall:{$mall}";
            throw new \Exception($message);
        }

        $dir = LOGS . $category . DS . $client . DS . $mall;
        if (!is_dir($dir)) {
            if (!mkdir($dir, 0777, true)) {
                throw new \Exception("Log directory creation failed. dir:{$dir}");
            }
        }

        self::$filePath = $dir . DS . $name . '_' . date('Ymd') . '.log';

        // monolog
        self::$key = self::$key ?? uniqid(mt_rand(10000000, 99999999)); // It looks like a key to be output to a log file. A different number for each process is numbered
        self::$logger = new Logger(self::$key);
        $logLevel = constant('Monolog\Logger::' . OUTPUT_LOG_LEVEL[KEY_ENVIRONMENT]);
        $stream = new StreamHandler(self::$filePath, $logLevel);
        $stream->setFormatter(new LineFormatter(null, null, true, true)); // Set monolog setting to have line feed
        self::$logger->pushHandler($stream);
        if ($stdoutFlag === true) {
            $stream = new StreamHandler("php://stdout", $logLevel);
            $stream->setFormatter(new LineFormatter(null, null, true, true)); // Set monolog setting to have line feed
            self::$logger->pushHandler($stream);
        }
    }

    /**
     * Grant user name to log
     *
     * @param string $userName
     */
    public static function addUserNameProcessor(string $userName): void
    {
        $handlers = self::$logger->getHandlers();

        foreach ($handlers as $handler) {
            $format = "[%extra.user_name%] [%datetime%] %channel%.%level_name%: %message% %context%\n";
            $handler->setFormatter(new LineFormatter($format, null, true, true)); // Set monolog setting to have line feed
        }

        self::$logger->pushProcessor(function ($record) use ($userName) {
            $record['extra']['user_name'] = $userName;
            return $record;
        });
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function emergency(string $message, $param = null): void
    {
        self::write(Logger::EMERGENCY, $message, $param);
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function critical(string $message, $param = null): void
    {
        self::write(Logger::CRITICAL, $message, $param);
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function alert(string $message, $param = null): void
    {
        self::write(Logger::ALERT, $message, $param);
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function error(string $message, $param = null): void
    {
        self::write(Logger::ERROR, $message, $param);
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function warning(string $message, $param = null): void
    {
        self::write(Logger::WARNING, $message, $param);
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function notice(string $message, $param = null): void
    {
        self::write(Logger::NOTICE, $message, $param);
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function info(string $message, $param = null): void
    {
        self::write(Logger::INFO, $message, $param);
    }

    /**
     * @param string $message
     * @param mixed $param
     */
    public static function debug(string $message, $param = null): void
    {
        self::write(Logger::DEBUG, $message, $param);
    }

    /**
     * @param int $level
     * @param string $message
     * @param null|array $param
     * @throws \LogicException
     */
    private static function write(int $level, string $message, $param = null): void
    {
        if (!self::$logger) {
            throw new \LogicException('Logger is not set.');
        }

        // Adjust so that $param can be passed to monolog if it is not an array
        if (is_null($param)) {
            $param = [];
        } elseif (is_object($param)) {
            $param = (array)$param;
        } elseif (!is_array($param)) {
            $param = [$param];
        }

        // Caller file and number of lines
        $backtrace = debug_backtrace();
        $file = $backtrace[1]['file'];
        $line = $backtrace[1]['line'];
        $context = ['file' => $file, 'line' => $line, 'param' => $param];

        self::$logger->addRecord($level, $message, $context);
    }
}

what I need now is to do the database log.

I've seen examples on the web but it uses plugins, and I don't want to do that.

What I want is to create custom table for logs(result_log).

Could it be possible?

Could somebody share his idea or link me to some guides on creating the database log?

andil01
  • 377
  • 4
  • 19

2 Answers2

2

You can create your own Log Adapter, and then use it instead of one provided by CakePHP.

If you want to write your logs to DB, create a table, bake a model and use it in your Log Adapter just as you would save "normal" entity. You can then save it also to file.

More info about creating Log Adapters can be found in docs: Creating Log Adapters

You can also check how it was made in one of plugins, for example in dereuromark/CakePHP-DatabaseLog

Szymon
  • 1,385
  • 1
  • 8
  • 10
1

What Szymon said is the Cake way to do it and I agree with their answer.

I am writing this because you have your own logging implementation that is not following the CakePHP logging configuration.

In your case, probably the easiest way to do it is to change your static::write() function and add some code at the end to also save the logs in the database.

Something along the lines:

$ResultLog = TableRegistry::get('ResultLog');

$data = []; //add here data relevant to your logs and the db structure you are using

$logEntry = $ResultLogs->newEntity($data);

if(!($ResultLog->save($logEntry)){
    throw new \LogicException('Could not store the log into the DB.');
}

Before you cause ResultLog you need to have Model\Table that is called like that. If you already have a db table called result_log you could try to bake the model with

bin/cake bake model ResultLog

(The cake way would be to call the table ResultLogs - plural form but with a bit of configuration you can have it in singular if that is what you want).

Good luck!

Ilie Pandia
  • 1,829
  • 14
  • 16