找出哪个控制器和方法执行了辅助函数(从辅助函数本身内部)

Find out what controller and method executed helper function (from within the helper function itself)

假设我们有一个辅助函数 logDatabaseError($exception),它将 QueryExceptions 记录到一个特殊的日志中。

helpers.php

function logDatabaseError ($exception) {
    $controller = ????;
    $function = ????;

    $log_string = "TIME: ".now().PHP_EOL;
    $log_string.= "User ID: ".Auth::user()->id.PHP_EOL;
    $log_string.= "Controller->Action:".$controller."->".$function.PHP_EOL;
    $log_string.= $exception.PHP_EOL;

    Storage::disk('logs')->append('database.log', $log_string);
}

此函数从多个控制器和这些控制器中的多个函数调用。

每当需要将某些内容写入数据库时​​,在 catch 部分,我们调用此 logDatabaseError 函数并将 \Illuminate\Database\QueryException 作为 $exception.

传递给它

BestControllerEverController.php

class BestControllerEver extends Controller
{
    function writeStuffToDatabase (Request $request) {
        try {
            DB::does-its-thing
        } 
        catch(\Illuminate\Database\QueryException $exception) {
            logDatabaseError($exception)
        }
    }
}

logDatabaseError 函数是否可以在不将它们作为函数参数传递的情况下获取控制器名称和函数名称?

在此特定示例中,logDatabaseError 函数中的 $controller 和 $function 变量将分别设置为 BestControllerEver 和 writeStuffToDatabase。

我知道这已记录在堆栈跟踪中,但它们在 $exception 对象中的位置并不总是相同,从那里提取它并不可靠,至少从我有限的经验来看是这样。

根据 @waterloomatt 的评论,Route facade 似乎提供了一个足够接近的解决方案:

使用

$route_action = Route::currentRouteAction();

我们可以得到输出

App\Http\Controllers\BestControllerEver@writeStuffToDatabase

写入日志。

虽然这实际上只有 returns 个注册为路由的函数,但知道用户在哪里遇到错误就足够了;结合事实,完整的堆栈跟踪也包含在传递给 logDatabaseError 的 $exception 中。

P.S.: 如果你使用这个解决方案,记得设置

$route_action = (Route::currentRouteAction()) ? Route::currentRouteAction() : "Not registered as route!"

以防万一。

您可以使用 php debug_backtrace function to trace the error frames. Since spatie/backtrace is using debug_backtrace behind the scenes You can use the package

通过运行

将包安装到应用程序中
composer require spatie/backtrace

将其放入您的控制器中:

try {
            \Illuminate\Support\Facades\DB::table('myunavialbetable')->get();
        } 
        catch(\Illuminate\Database\QueryException $exception) {
            logDatabaseError($exception);
        }

在你的帮助文件中

function logDatabaseError ($exception) {

    $backtrace = Spatie\Backtrace\Backtrace::create();

    $controllerResponsible = collect($backtrace->frames())   
    ->filter(function(Spatie\Backtrace\Frame $frame){
        return ($frame->class);
    })
    ->filter(function(Spatie\Backtrace\Frame $frame){
        return is_subclass_of($frame->class, App\Http\Controllers\Controller::class);
    })
    ->first();   

    $log_string = "TIME: " . now() . PHP_EOL;
    $log_string .= "User ID: " . auth()->id() . PHP_EOL;
    if ($controllerResponsible){
        $log_string .= "Controller->Action:" . $controllerResponsible->class . "->" . $controllerResponsible->method . PHP_EOL;
    }
    $log_string .= $exception . PHP_EOL;

    \Illuminate\Support\Facades\Storage::disk('logs')->append('database.log', $log_string);

// if you want to use on-demand log feature you can uncomment this

//此功能从 Laravel v8.66.0

开始提供
    // Illuminate\Support\Facades\Log::build([
    //     'driver' => 'single',
    //     'path' => storage_path('logs/database.log'),
    // ])->info($log_string);
}

NOTE:CONTROLLER MUST EXTEND App\Http\Controllers\Controller

ADVANCE SOLUTION

要遵循的步骤:

  1. 安装spatie/backtrace
  2. 从控制器中删除 try/catch 块。
  3. app/Exceptions/Handler.php修改为以下内容
class Handler extends ExceptionHandler
{
    public $controllerResponsible = null;

    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->reportable(function (Throwable $e) {

            $backtraceInstance = SpatieBacktrace::createForThrowable($e);

            $controllerResponsible = collect($backtraceInstance->frames())
                ->filter(function (SpatieBacktraceFrame $frame) {
                    return ($frame->class);
                })
                ->filter(function (SpatieBacktraceFrame $frame) {
                    return is_subclass_of($frame->class, \App\Http\Controllers\Controller::class);
                })
                ->first();

            $this->controllerResponsible = $controllerResponsible;
        });
    }

    /**
     * Get the default context variables for logging.
     *
     * @return array
     */
    protected function context()
    {
        $extraContext = [];

        if ($this->controllerResponsible instanceof SpatieBacktraceFrame) {
            $extraContext['controller'] = $this->controllerResponsible->class;
            $extraContext['method'] = $this->controllerResponsible->method;
            $extraContext['controller@method'] = $this->controllerResponsible->class . '@' . $this->controllerResponsible->method;
        }

        return array_merge(parent::context(), $extraContext);
    }
}```

So here is what happens.

By default you can add exta [content][3] by overriding context method inside `Handler.php`. And you dont need any other custom log. It will be logged by default logging.