2

I'm using OctoberCMS based on Laravel.

I have records in a database table vendor_log_.

Each day I need Laravel to save a list of the newly created records to a dated .log file.

In my Custom Component's Plugin.php file I created a registerSchedule function. I have it get() the records in the table by current date, then use file_put_contents to append them to a .log file.

However, no log file is being saved to my logs server path. I cannot tell if the schedule is not firing or what it could be, there are no errors.

public function registerSchedule($schedule)
{
    # Generate Daily Log File
    $schedule->call(function () {

        # Get Today's Date
        $date = date("Y-m-d");

        # Get Records by Today's Date
        # Match Record created_at date, exclude time
        $records = \Db::table('vendor_log_')
                    ->whereDay('created_at', date('d'))
                    ->whereMonth('created_at', date('m'))
                    ->whereYear('created_at', date('Y'))
                    ->get();

        # Write records to log file
        foreach ($records as $line) {
          file_put_contents("/var/www/mysite/logs/$date.log", $line . PHP_EOL, FILE_APPEND);
        }

    })->everyMinute();
}

Output of $line:

stdClass Object ( [user_id] => 1 [id] => 28 [username] => [name] => test [created_at] => 2017-03-22 02:39:13 )

Note: I have used ->everyMinute() in place of ->daily() to generate the logs quicker for testing.

Docs:
https://octobercms.com/docs/plugin/scheduling#defining-schedules
https://laravel.com/docs/5.4/scheduling#schedule-frequency-options

Matt McManis
  • 4,475
  • 5
  • 38
  • 93
  • Can you dump a $line for us? Could be that an object / array to string conversion does not work properly. If you want to check wether it creates errors or not, can you try this script in a controller or anything? – Douwe de Haan Mar 22 '17 at 10:43
  • 1
    Are you sure you set up the scheduler correctly: https://octobercms.com/docs/setup/installation#crontab-setup , i.e. put the correct line in your crontab. Is the path correct? If both is the case, verify with a simple ``traceLog('scheduler called')`` in your ``regsiterSchedule`` method that it is actually beeing called. – Alex Guth Mar 22 '17 at 10:44
  • @DouwedeHaan I think I found a conflict, my site is in /public folder and Laravel does not have permission to write outside of it to /log. – Matt McManis Mar 22 '17 at 10:48
  • @MattMcManis Is it an possibility to create a folder inside the public folder where you store your logs? You could protect it with an .htaccess file. – Douwe de Haan Mar 22 '17 at 10:51
  • @DouwedeHaan I updated the question with the output of $line. I actually dont want an array in the log file, but I will have to solve that later. I'm just trying to get the scheduler to create the file first. I have 'file_put_contents' now directed at /public instead of /log. It has permission to write, but the file does not appear. – Matt McManis Mar 22 '17 at 11:06
  • @AlexGuth I have crontab -e set and path to artisan /var/www/mysite/public/artisan. I put the traceLog in the function, but I do not see anything happen and no file is created. – Matt McManis Mar 22 '17 at 11:09
  • 1
    @MattMcManis You can't print an object to a string (not one of the stdClass type). So you have to create a way to convert the object to a string and append that to the log. – Douwe de Haan Mar 22 '17 at 11:10
  • @DouwedeHaan I think that is whats causing the scheduler to fail. I put the same code in the boot() function so that i runs the record loop when I visit a page and it says "Object of class stdClass could not be converted to string". – Matt McManis Mar 22 '17 at 11:16
  • @MattMcManis I wrote an answer below. Feel free to test it and comment on it! – Douwe de Haan Mar 22 '17 at 12:46

1 Answers1

2

A summary of the problem and the comments:

The problem consisted of two parts;

  • The location where the log is saved isn't writable by the user
  • The object returned by the DB class can't be written as a string

The first problem was solved by writing the log files to the public folder of the hosting that is used.

To write the DB object to a string could be done by the following code

foreach($records as $record) {
    $array = (array) $record;

    $string = '';

    foreach($record as $key => $value) {
        $string .= $key .' '. $value . ',';
    }

    // strip the trailing comma of the end of the string
    $line = rtrim($string, ',');

    file_put_contents("/link/to/public/$date.log", $line . PHP_EOL, FILE_APPEND);
}
Douwe de Haan
  • 6,247
  • 1
  • 30
  • 45
  • This is working well, the scheduler is appending the array to the log file. But http_build_query turns the array into "id=28&username=&created_at=2017-03-22+02%3A39%3A13". Is there a way to get it to use the actual values from the array separated by spaces or commas? Such as "id 28, username matt, created_at 2017-03-22". – Matt McManis Mar 22 '17 at 15:20
  • @MattMcManis Yes, I've updated the code. Check the code block! – Douwe de Haan Mar 22 '17 at 15:23
  • @MattMcManis Did you also change the first line? I changed the name of the variables in the foreach loop – Douwe de Haan Mar 22 '17 at 15:59
  • I had copied it wrong. It is all working perfect now. Do you know how it would be possible to use the /logs director outside of /public? Or does it have to do with www-data permissions? If not I'll have to use a dir in public and protect it. – Matt McManis Mar 22 '17 at 16:07
  • 1
    You could leverage Laravel itself to do the logging [Read more about logging here](https://laravel.com/docs/5.4/errors). If you set the logging in the config/app.php to daily, and instead of the `file_put_contents()` use `Log::info()` you can put everything in daily logs where Laravel stores them. If you can't get to the logs with OctoberCMS, you should store them in the public folder and deny access to the folder with .htaccess files – Douwe de Haan Mar 22 '17 at 16:30
  • Thanks for the info and solving this. – Matt McManis Mar 22 '17 at 16:38
  • No problem! Anytime! – Douwe de Haan Mar 22 '17 at 16:38