假设我们有一个助手函数,logDatabaseError($exception(,它将QueryException记录到一个特殊的日志中。
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(IlluminateDatabaseQueryException $exception) {
logDatabaseError($exception)
}
}
}
logDatabaseError函数是否可以在不将控制器名称和函数名称作为函数参数传递的情况下同时获取它们?
在这个特定的示例案例中,logDatabaseError函数中的$controller和$function变量将分别设置为BestControllerEver和writeStuffToDatabase。
我知道这是在堆栈跟踪中记录的,但它们在$exception对象中的位置并不总是相同的,从那里提取它是不可靠的,至少从我有限的经验来看是这样。
您可以使用php-debug_backtrace函数来跟踪错误帧。由于spatie/backtrace在幕后使用debug_backtrace,因此您可以使用包
通过运行将包安装到应用程序中
composer require spatie/backtrace
把它放在你的控制器里:
try {
IlluminateSupportFacadesDB::table('myunavialbetable')->get();
}
catch(IlluminateDatabaseQueryException $exception) {
logDatabaseError($exception);
}
在你的助手文件中
function logDatabaseError ($exception) {
$backtrace = SpatieBacktraceBacktrace::create();
$controllerResponsible = collect($backtrace->frames())
->filter(function(SpatieBacktraceFrame $frame){
return ($frame->class);
})
->filter(function(SpatieBacktraceFrame $frame){
return is_subclass_of($frame->class, AppHttpControllersController::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;
IlluminateSupportFacadesStorage::disk('logs')->append('database.log', $log_string);
// if you want to use on-demand log feature you can uncomment this
//此功能可从Laravel v8.66.0 获得
// IlluminateSupportFacadesLog::build([
// 'driver' => 'single',
// 'path' => storage_path('logs/database.log'),
// ])->info($log_string);
}
注意:控制器必须扩展
AppHttpControllersController
高级解决方案
要遵循的步骤:
- 安装spatie/backtrace
- 从控制器中删除try/catch块
- 将
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, AppHttpControllersController::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.
根据@waterloomat的评论,Route facade似乎提供了一个足够接近的解决方案:
使用
$route_action = Route::currentRouteAction();
我们可以得到输出
App\Http\Controllers\BestControllerEver@writeStuffToDatabase
写入日志。
虽然这实际上只返回注册为路由的函数,但知道用户在哪里遇到错误就足够了;再加上传递给logDatabaseError的$exception中还包括一个完整的堆栈跟踪。
第页。S.:如果您使用此解决方案,请记住设置
$route_action = (Route::currentRouteAction()) ? Route::currentRouteAction() : "Not registered as route!"
以防万一。