Charles
Charles

Reputation: 199

How to capture an Artisan command name when calling from both CLI and within code?

THE GOAL: When specific commands are executed - either through me manually running via the app server's CLI and/or through the app's code itself - to log to a MySQL Db the name of the Artisan command, the time it took to run, etc.

MY APPROACH: Create an abstract LogCommandClass that extends Command and implements a LogCommandInterface which contains a single method, LogCommandToDb(). Then, have the 'desired-to-be-logged' Artisan command classes extend LogCommandClass.

THE ISSUE: Only when I actually type in a command via CLI on our server (ex. php artisan some-command) does the command get logged to Db. Commands that are called within the code via Artisan::call('some-command') do not get logged, but they need to be as well!

My only remaining approach is to replace within the code all instances of Artisan::call('some-command') with something like,

$string ="cd ".base_path(). " && php artisan some-command";
shell_exec($string);

but that doesn't seem ideal...


LogCommandClass.php

abstract class LogCommandClass extends Command implements LogCommandInterface {

    protected $commName, $startTime, $endTime;

    /**
    * @return void
    */
    public function __construct()
    {
        parent::__construct();
        $this->startTime = (new DateTime())->getTimestamp();
    }

    /**
    * @return void
    */
    public function __destruct()
    {
        if (Arr::get(request()->server(), 'argv.1')  == $this->getName()) {
            $this->LogCommandToDb();
        }
    }

    public function LogCommandToDb() {
        
        $this->endTime = (new DateTime())->getTimestamp();
        $diff = $this->endTime - $this->startTime;

        $comm = new CommandLog;
        $comm->LogCommand($this->getName(), $diff);
    }
}

LogCommandInterface.php

interface LogCommandInterface 
{
    public function LogCommandToDb();
}

SomeCommandClass.php

class SomeCommandClass extends LogCommandClass {

/**
 * The console command name.
 *
 * @var string
 */
protected $name = 'some-command';

/**
 * The console command description.
 *
 * @var string
 */
protected $description = 'A command that should be logged to Db.';

/**
 * Create a new command instance.
 *
 * @return void
 */
public function __construct() {
    parent::__construct();
}

/**
 * Execute the console command.
 *
 * @return mixed
 */
public function handle() {
    //do something
}

CommandLog.php

class CommandLog extends Model {

    protected $table = 'CommandLog';
    public $timestamps = true;

    public function LogCommand($name, $time)
    {
        $this->commandName = $name;
        $this->timeElapsed = $time;
        $this->save();
    }
}

Upvotes: 0

Views: 966

Answers (1)

Juan Eizmendi
Juan Eizmendi

Reputation: 1134

Each time a command run laravel dispatch Artisan events,

log with the event, I just give it a try and I'm getting both.

At different stages of the command running a event of one of that three types is dispatch. You can set a listener waiting for one.

First, you create your listener

php artisan make:listener CommandStartingLoggin --event=CommandStarting

This will create a class CommandStartingLoggin in app/Listeners that class will be called when in this case a command is starting. But first, for that you need to let laravel know this, in the provider's folder, you'll see EventServiceProvider in the listen array to add the entry:

\Illuminate\Console\Events\CommandStarting::class => [
    \App\Listeners\CommandStartingLoggin::class,
],

Finally in app/Listeners/CommandStartingLoggin you'll see a handle method, you can log there.

Upvotes: 3

Related Questions