Recommended way/point of logging User actions

Hi,

How I do it atm:
I’m currently logging every user action in a BaseController’s afterAction().
Looks like this:

public function afterAction($action, $result)
    {
        $result = parent::afterAction($action, $result);
        UserAction::add( Yii::$app->user->id, $action, Yii::$app->controller->actionParams );
        return $result;
    }

And the class UserAction holds a static function to just store the data in a table:

public static function add($userId, $action, $actionParams="", $access=""){
    if( $action instanceof Action ){
        $action = $action->getUniqueId();
    }

    if( is_array($actionParams) && !empty($actionParams) ){
        //$actionParams['method'] = Yii::$app->request->method;
        $actionParams = Json::encode($actionParams);
    }else{
        $actionParams = "";
    }
    //todo: check proper escaping
    Yii::$app->db->createCommand()->insert( self::tableName(),
        [
            "user_id" => $userId,
            "requested_action" => $action,
            "requested_params" => $actionParams,
            "access" => $access, //-1 denied, 1 granted
            "requested_at" => time()
        ])->execute();
}

I’ve chosen this, because I thought, static function calls are cheaper then creating an UserAction object after every action.
Turns out, in this way structured, I can’t gather many information. For e.g.: identifying the request method POST, is never available there.
Also injecting additional data from other components, like, if access was granted or not gets complicated to implement.
Yii app by default has a log component at application level. Currently thinking of adopting this and add an extra userlog component with the logic in afterAction()placed there.

What do you guys suggest?

On our employee site I’m logging every action. I first modified the application config file:

$config = [
    // Your other config stuff here
    'as beforeRequestLog' => [
         'class'=>'app\filters\LogFilter'
     ],

I created a basic class that extends \yii\base\ActionFilter

<?php
/**
 * Date: 5/21/2015
 * Time: 1:29 PM
 */

namespace app\filters;

use Yii;

class LogFilter extends \yii\base\ActionFilter
{
    public function beforeAction($action)
    {
        if(isset(\Yii::$app->user) && !empty(\Yii::$app->user->getId()))
        {
            \common\models\logging\LogEmployeeAccess::ping();
        }

        return parent::beforeAction($action);
    }
}

Model

<?php

namespace common\models\logging;

use Yii;

/**
 * This is the model class for table "log_employee_access".
 *
 * @property int $id
 * @property string $user_id
 * @property string $remote_ip
 * @property string $url
 * @property string $created_at
 */
class LogEmployeeAccess extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'log_employee_access';
    }

    /**
     * @return \yii\db\Connection the database connection used by this AR class.
     */
    public static function getDb()
    {
        return Yii::$app->get('dbNc');
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'user_id' => 'User ID',
            'remote_ip' => 'Remote IP',
            'url' => 'Url',
            'created_at' => 'Created At',
        ];
    }

    public static function ping()
    {
        $model = new self;
        $model->save();
    }

    public function beforeSave($insert)
    {
        if($this->isNewRecord)
        {
            $this->url = $_SERVER['REQUEST_URI'];
            $this->user_id = \Yii::$app->user->getId();
            $this->remote_ip = \common\components\RemoteAddress::get();
            $this->created_at = date('Y-m-d H:i:s');
        }

        return parent::beforeSave($insert); // TODO: Change the autogenerated stub
    }
}

The application is being run behind a load balancer on AWS, so I have a simple class to grab the remote IP address:

<?php
/**
 * Created by PhpStorm.
 * Date: 5/19/2017
 * Time: 1:05 PM
 */

namespace common\components;

class RemoteAddress
{
    public static function get()
    {
        if(function_exists('apache_request_headers'))
        {
            $http_headers = apache_request_headers();

            if(isset($http_headers["X-Forwarded-For"]))
            {
                $parts = explode(',', $http_headers["X-Forwarded-For"]);
                $end = trim(end($parts));
                return $end;
            }
            else
            {
                if(isset(\Yii::$app->request))
                {
                    return \Yii::$app->request->userIP;
                }
            }
        }

        return '0.0.0.0';
    }
}

The last thing I’ll add is that I created a copy of my database component (dbNc) to use for logging. In this specific case I don’t believe it could be an issue, but in some cases if I attempt to log something in the middle of a database transaction and the transaction is rolled back, the log entry is rolled back too. Maybe there is a better way, but this was working for me.

**Edited to add:
I ran a quick test and I’m able to access the $_POST variable in my LogEmployeeAccess model. If you want to log the post data, you may want to write a class to make sure you get everything you want. (E.g. the $_POST variable will not include file uploads, JSON post data, etc) (Look into php://input, https://www.php.net/manual/en/wrappers.php.php)