<p>Another perhaps simpler approach is to write to your own log file without using the Craft logger at all.</p>

<pre><code>$file = Craft::getAlias('@storage/logs/pluginhandle.log');
$log = date('Y-m-d H:i:s').' '.$message."\n";

\craft\helpers\FileHelper::writeToFile($file, $log, ['append' =&gt; true]);
</code></pre>

<p>UPDATE:</p>

<p>This logic has been put into a helper class for modules and plugins in Craft CMS:
<a href="https://github.com/putyourlightson/craft-log-to-file" rel="nofollow noreferrer">https://github.com/putyourlightson/craft-log-to-file</a></p>

CC BY-SA 4.0
2018-10-05T22:28:06.927Z
That's pretty slick! Just circumvent the whole system.
Lindsey D
CC BY-SA 4.0
2019-07-21T02:44:14.633Z
Just remember, there's a reason why Yii2's logging system jumps through the hoops that it does.
andrew.welch
CC BY-SA 4.0
2020-02-12T00:02:27.040Z
Thank you for this Ben!
Anna_MediaGirl
CC BY-SA 4.0
2020-02-12T06:06:46.947Z
Sure thing Anna, I now use this approach for all plugins with this helper class:
https://github.com/putyourlightson/craft-log-to-file
Ben Croker
CC BY-SA 4.0
2018-10-05T18:50:01.983Z
2020-02-12T06:05:59.830Z
2020-02-12T06:05:59.830Z
Ben Croker
<p>Adding this for posterity, for any future-traveller who happens to come along.</p>

<p>Here's everything you need to know about making a custom logger for Craft CMS:</p>

<p><a href="https://nystudio107.com/blog/creating-a-custom-logger-for-craft-cms" rel="nofollow noreferrer">Creating a Custom Logger for Craft CMS</a></p>

<p>And then once you have a log file, you need to know how to read it:</p>

<p><a href="https://nystudio107.com/blog/zen-and-the-art-of-craft-cms-log-file-reading" rel="nofollow noreferrer">Zen and the Art of Craft CMS Log File Reading</a></p>

<p>Godspeed, future traveller!</p>

CC BY-SA 4.0
2020-02-12T20:59:15.393Z
2020-02-12T20:59:15.393Z
andrew.welch
<h3>Edit 2</h3>

<p>I came up with a far far far better approach after a little brainstorming. I'll keep my old answer here but you should probably just use the first snipped.</p>

<p>In your main Pluginfile.php</p>

<pre class="lang-php prettyprint-override"><code>public static function log($message){
    Craft::getLogger()-&gt;log($message, \yii\log\Logger::LEVEL_INFO, 'your-plugin-handle');
}

public function init(){
    $fileTarget = new \craft\log\FileTarget([
        'logFile' =&gt; __DIR__ . '/pluginName.log', // &lt;--- path of the log file
        'categories' =&gt; ['your-plugin-handle'] // &lt;--- categories in the file
    ]);
    // include the new target file target to the dispatcher
    Craft::getLogger()-&gt;dispatcher-&gt;targets[] = $fileTarget;
    ....
}
</code></pre>

<p>Explanation: <code>categories</code> is an array of all categories that should be in the log file. You usually want to allow only categories of your own plugin (you have to use an unique identifier in case certain other developers try to add the same category). Whenever you want to log something with the Craft/Yii <code>log</code> method that match one of your given categories in the array it will be logged in your file as well.</p>

<h3>Old answer</h3>

<p>Create this file somewhere in your plugin</p>

<h3>FileTarget</h3>

<pre class="lang-php prettyprint-override"><code>class FileTarget extends \yii\log\FileTarget{

    // an array of categories that should be logged
    // note: your target receives all `Yii::log()` messages
    // but you can filter them with this array
    // if you want to track all messages of type 'application' as well
    // just include it in the array
    public $categories = ['a-category-that-you-would-like-to-log']; //&lt;- usually your plugin handle or something

    // set the file path
    public function setLogFile($path = __DIR__){
        // I'm lazy so I just use the current location since it's in my 'src/' folder anyway 
        // you can use Crafts default folders or whatever you like
        $this-&gt;logFile = $path . '/test.log';
        return $this;
    }

    // Optional -&gt; a callback that formats your messages
    // remove this function if there is
    // no formatting needed or if you want to use the default
    public function formatMessage($message)
    {
        list($text, $level, $category, $timestamp) = $message;
        $level = 'Your fancy plugin Name';

        if ($category == 'a-category-that-you-would-like-to-log') {
            return date('Y-m-d H:i:s', $timestamp) . " [$level][$category] $text";
        }
    }
}
</code></pre>

<h3>In your Plugin main file</h3>

<pre class="lang-php prettyprint-override"><code>public static $_fileTarget;

public static function log($message){
    if(self::$_fileTarget === null){
        /** @var FileTarget _fileTarget */
        self::$_fileTarget = Craft::createObject('namespace\pluginhandle\filetarget');
        // set the path
        self::$_fileTarget-&gt;setLogFile();
        // include your target to the current dispatcher targets 
        // -&gt; all messages are tracked in your target as well. 
        Craft::getLogger()-&gt;dispatcher-&gt;targets[] = self::$_fileTarget;
    }

    // just use the default Craft/Yii logging method but with your category
    Craft::getLogger()-&gt;log($message, \yii\log\Logger::LEVEL_INFO, 'a-category-that-you-would-like-to-log');
}
</code></pre>

<h3>Usage</h3>

<pre class="lang-php prettyprint-override"><code>Plugin::log('Lorem ipsum');
Craft::getLogger()-&gt;log('Lorem ipsum', \yii\log\Logger::LEVEL_INFO, 'a-category-that-you-would-like-to-log');
Craft::info('Lorem ipsum', 'a-category-that-you-would-like-to-log');
</code></pre>

<p>Maybe we could create a feature/pull request in order to create this feature by default. So every Plugin receives it's own category (by plugin handle) and if these categories are recognized the logger logs them in a separate file</p>

<h3>Edit (thanks Andrew for mentioning it):</h3>

<p>Note with this method I just add an additional <code>target</code> to the dispatcher that creates log files. So you'll insert 2 messages: one in the default <code>storage/logs</code> folder together with all the other requests and one in you custom log folder thus you are still able to use the great Yii2 Debug Toolbar and you can ask clients to hand over their plugin related logs to see what's going on</p>

CC BY-SA 4.0
2018-06-19T03:01:05.660Z
Hi Robin. Thanks for this. I must be missing something - I add the static log function in the main plugin file, then in init() I add the new log location to the dispatcher (and it shows up in the dispatcher targets array) but then calling it with (in my case) NewsletterSignup::log(); results in it being written to the web log only... not the new plugin log. Is there something missing from your static log method? It doesn't look like it does anything other than write to the Craft log? Thanks in advance!
JamesNZ
CC BY-SA 4.0
2018-06-19T04:53:11.650Z
What is your file location? Maybe you didn't specify a valid path?
Robin Schambach
CC BY-SA 4.0
2018-06-19T22:56:12.147Z
Hi Robin. The file location is the same as the example, which maps to /Users/me/Projects/project-name/src/plugins/newslettersignup/src/NewsletterSignup.log. I tried creating the file and setting it to 777 just to rule out permissions. Didn't make a difference. The path is definitely correct and points to a file (despite me having had created it). Thanks.
JamesNZ
CC BY-SA 4.0
2018-08-21T14:22:43.610Z
Rather than writing to your plugin's directory you can write to Craft's storage directory:

`'logFile' => Craft::getAlias('@storage/logs/pluginhandle.log'),`
John D Wells
CC BY-SA 4.0
2019-07-22T15:49:18.327Z
I published a refinement of this technique here in the [Zen and the Art of Craft CMS Log File Reading](https://nystudio107.com/blog/creating-a-custom-logger-for-craft-cms#custom-plugin-module-logging-behavior) article
andrew.welch
CC BY-SA 3.0
2018-04-01T21:46:07.350Z
Niiiiice. A PR for this would be awesome. There are support cases where asking the user to send you just your plugin's log file would be very useful.
andrew.welch
CC BY-SA 3.0
2018-04-01T09:31:07.517Z
2018-04-01T16:49:36.380Z
2018-04-01T16:49:36.380Z
Robin Schambach
<p>In Craft 2, it was possible to have your plugin log to a separate file. For example, it would record items to a <code>myplugin.log</code> file. You would simply be able to do it like this...</p>

<pre class="lang-php prettyprint-override"><code>MyPlugin::log('Lorem ipsum');
</code></pre>

<p>However, I can't seem to figure out how this would be done in Craft 3. I've seen <a href="https://craftcms.stackexchange.com/a/24082/45">this answer</a>, which demonstrates how to log items in Craft 3 in general. But that just dumps items into the shared <code>web.log</code> file.</p>

<p><strong>How can I log items to <code>myplugin.log</code> in Craft 3?</strong></p>

<plugin-development><craft3><logs>
Craft 3 plugins - Logging in a separate *.log file
# Craft 3 plugins - Logging in a separate *.log file

(Asked by: username_0 on 2018-04-01)

In Craft 2, it was possible to have your plugin log to a separate file. For example, it would record items to a myplugin.log file. You would simply be able to do it like this...
MyPlugin::log('Lorem ipsum');

However, I can't seem to figure out how this would be done in Craft 3. I've seen this answer, which demonstrates how to log items in Craft 3 in general. But that just dumps items into the shared web.log file.
How can I log items to myplugin.log in Craft 3?

**Tags:** plugin-development, craft3, logs


---

## 3 answers

### Answer by username_4 on 2018-04-01. Score: 10 | Accepted answer

Edit 2
I came up with a far far far better approach after a little brainstorming. I'll keep my old answer here but you should probably just use the first snipped.
In your main Pluginfile.php
public static function log($message){
    Craft::getLogger()->log($message, \yii\log\Logger::LEVEL_INFO, 'your-plugin-handle');
}

public function init(){
    $fileTarget = new \craft\log\FileTarget([
        'logFile' => __DIR__ . '/pluginName.log', // <--- path of the log file
        'categories' => ['your-plugin-handle'] // <--- categories in the file
    ]);
    // include the new target file target to the dispatcher
    Craft::getLogger()->dispatcher->targets[] = $fileTarget;
    ....
}

Explanation: categories is an array of all categories that should be in the log file. You usually want to allow only categories of your own plugin (you have to use an unique identifier in case certain other developers try to add the same category). Whenever you want to log something with the Craft/Yii log method that match one of your given categories in the array it will be logged in your file as well.
Old answer
Create this file somewhere in your plugin
FileTarget
class FileTarget extends \yii\log\FileTarget{

    // an array of categories that should be logged
    // note: your target receives all `Yii::log()` messages
    // but you can filter them with this array
    // if you want to track all messages of type 'application' as well
    // just include it in the array
    public $categories = ['a-category-that-you-would-like-to-log']; //<- usually your plugin handle or something

    // set the file path
    public function setLogFile($path = __DIR__){
        // I'm lazy so I just use the current location since it's in my 'src/' folder anyway 
        // you can use Crafts default folders or whatever you like
        $this->logFile = $path . '/test.log';
        return $this;
    }

    // Optional -> a callback that formats your messages
    // remove this function if there is
    // no formatting needed or if you want to use the default
    public function formatMessage($message)
    {
        list($text, $level, $category, $timestamp) = $message;
        $level = 'Your fancy plugin Name';

        if ($category == 'a-category-that-you-would-like-to-log') {
            return date('Y-m-d H:i:s', $timestamp) . " [$level][$category] $text";
        }
    }
}

In your Plugin main file
public static $_fileTarget;

public static function log($message){
    if(self::$_fileTarget === null){
        /** @var FileTarget _fileTarget */
        self::$_fileTarget = Craft::createObject('namespace\pluginhandle\filetarget');
        // set the path
        self::$_fileTarget->setLogFile();
        // include your target to the current dispatcher targets 
        // -> all messages are tracked in your target as well. 
        Craft::getLogger()->dispatcher->targets[] = self::$_fileTarget;
    }

    // just use the default Craft/Yii logging method but with your category
    Craft::getLogger()->log($message, \yii\log\Logger::LEVEL_INFO, 'a-category-that-you-would-like-to-log');
}

Usage
Plugin::log('Lorem ipsum');
Craft::getLogger()->log('Lorem ipsum', \yii\log\Logger::LEVEL_INFO, 'a-category-that-you-would-like-to-log');
Craft::info('Lorem ipsum', 'a-category-that-you-would-like-to-log');

Maybe we could create a feature/pull request in order to create this feature by default. So every Plugin receives it's own category (by plugin handle) and if these categories are recognized the logger logs them in a separate file
Edit (thanks Andrew for mentioning it):
Note with this method I just add an additional target to the dispatcher that creates log files. So you'll insert 2 messages: one in the default storage/logs folder together with all the other requests and one in you custom log folder thus you are still able to use the great Yii2 Debug Toolbar and you can ask clients to hand over their plugin related logs to see what's going on

> **Comments:**
> **username_2**: Niiiiice. A PR for this would be awesome. There are support cases where asking the user to send you just your plugin's log file would be very useful.  
> **username_5**: Hi Robin. Thanks for this. I must be missing something - I add the static log function in the main plugin file, then in init() I add the new log location to the dispatcher (and it shows up in the dispatcher targets array) but then calling it with (in my case) NewsletterSignup::log(); results in it being written to the web log only... not the new plugin log. Is there something missing from your static log method? It doesn't look like it does anything other than write to the Craft log? Thanks in advance!  
> **username_4**: What is your file location? Maybe you didn't specify a valid path?  
> **username_5**: Hi Robin. The file location is the same as the example, which maps to /Users/me/Projects/project-name/src/plugins/newslettersignup/src/NewsletterSignup.log. I tried creating the file and setting it to 777 just to rule out permissions. Didn't make a difference. The path is definitely correct and points to a file (despite me having had created it). Thanks.  
> **username_6**: Rather than writing to your plugin's directory you can write to Craft's storage directory:  `'logFile' => Craft::getAlias('@storage/logs/pluginhandle.log'),`  
> **username_2**: I published a refinement of this technique here in the [Zen and the Art of Craft CMS Log File Reading](https://nystudio107.com/blog/creating-a-custom-logger-for-craft-cms#custom-plugin-module-logging-behavior) article  

---

### Answer by username_1 on 2018-10-05. Score: 9

Another perhaps simpler approach is to write to your own log file without using the Craft logger at all.
$file = Craft::getAlias('@storage/logs/pluginhandle.log');
$log = date('Y-m-d H:i:s').' '.$message."\n";

\craft\helpers\FileHelper::writeToFile($file, $log, ['append' => true]);

UPDATE:
This logic has been put into a helper class for modules and plugins in Craft CMS:
https://github.com/putyourlightson/craft-log-to-file

> **Comments:**
> **username_0**: That's pretty slick! Just circumvent the whole system.  
> **username_2**: Just remember, there's a reason why Yii2's logging system jumps through the hoops that it does.  
> **username_3**: Thank you for this Ben!  
> **username_1**: Sure thing Anna, I now use this approach for all plugins with this helper class: https://github.com/putyourlightson/craft-log-to-file  

---

### Answer by username_2 on 2020-02-12. Score: 2

Adding this for posterity, for any future-traveller who happens to come along.

Here's everything you need to know about making a custom logger for Craft CMS:

[Creating a Custom Logger for Craft CMS](https://nystudio107.com/blog/creating-a-custom-logger-for-craft-cms)

And then once you have a log file, you need to know how to read it:

[Zen and the Art of Craft CMS Log File Reading](https://nystudio107.com/blog/zen-and-the-art-of-craft-cms-log-file-reading)

Godspeed, future traveller!


---

