Symfony 4:在非生产环境中隐藏调试堆栈跟踪

Symfony 4: hide debug stacktrace in non prod environment

我有 2 个生产环境,分别是 prod1prod2 它们在数据库和业务逻辑等配置上有所不同。基本上是一种主从关系。

我想像在正常生产环境中一样对用户隐藏调试堆栈跟踪。这应该可以通过在 .env 文件中设置 APP_DEBUG=0 来实现。

APP_DEBUG=0

但我得到了调试屏幕:

但奇怪的是,这不起作用,只有当我将 APP_ENV 设置为 prod 时,才会显示调试堆栈跟踪。

我的 .env 文件如下所示:

APP_ENV=prod1
APP_DEBUG=0
APP_SECRET=xxxxxxxaxaxaxa

我检查了public/index.php中的参数,并且有正确的传输:

$env = 'prod1';
$debug = false;
$kernel = new Kernel($env, $debug);

我正在使用 Symfony 4.2.2 symfony/env。

任何人都可以重现此行为吗?这可能是 Symfony 或 symfony/env 错误?

您的配置正确。

这很可能是缓存问题:尝试 运行 php bin/console cache:clear

我有几个用 Symfony 4 编写的项目。为了重现您的错误,我将我的一个项目更新为 Symfony 4.2.2。所有 Symfony 依赖项:

symfony/browser-kit                              v4.2.2             Symfony BrowserKit Component
symfony/cache                                    v4.2.2             Symfony Cache component with PSR-6, PSR-16, and tags
symfony/class-loader                             v3.4.21            Symfony ClassLoader Component
symfony/config                                   v4.2.2             Symfony Config Component
symfony/console                                  v4.2.2             Symfony Console Component
symfony/contracts                                v1.0.2             A set of abstractions extracted out of the Symfony components
symfony/css-selector                             v4.2.2             Symfony CssSelector Component
symfony/debug                                    v4.2.2             Symfony Debug Component
symfony/dependency-injection                     v4.2.2             Symfony DependencyInjection Component
symfony/doctrine-bridge                          v4.2.2             Symfony Doctrine Bridge
symfony/dom-crawler                              v4.2.2             Symfony DomCrawler Component
symfony/dotenv                                   v4.2.2             Registers environment variables from a .env file
symfony/event-dispatcher                         v4.2.2             Symfony EventDispatcher Component
symfony/filesystem                               v4.2.2             Symfony Filesystem Component
symfony/finder                                   v4.2.2             Symfony Finder Component
symfony/flex                                     v1.1.8             Composer plugin for Symfony
symfony/form                                     v4.2.2             Symfony Form Component
symfony/framework-bundle                         v4.2.2             Symfony FrameworkBundle
symfony/http-foundation                          v4.2.2             Symfony HttpFoundation Component
symfony/http-kernel                              v4.2.2             Symfony HttpKernel Component
symfony/inflector                                v4.2.2             Symfony Inflector Component
symfony/intl                                     v4.2.2             A PHP replacement layer for the C intl extension that includes additional data from the ICU library.
symfony/maker-bundle                             v1.11.3            Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.
symfony/monolog-bridge                           v4.2.2             Symfony Monolog Bridge
symfony/monolog-bundle                           v3.3.1             Symfony MonologBundle
symfony/options-resolver                         v4.2.2             Symfony OptionsResolver Component
symfony/orm-pack                                 v1.0.6             A pack for the Doctrine ORM
symfony/polyfill-intl-icu                        v1.10.0            Symfony polyfill for intl's ICU-related data and classes
symfony/polyfill-mbstring                        v1.10.0            Symfony polyfill for the Mbstring extension
symfony/polyfill-php72                           v1.10.0            Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions
symfony/process                                  v4.2.2             Symfony Process Component
symfony/profiler-pack                            v1.0.4             A pack for the Symfony web profiler
symfony/property-access                          v4.2.2             Symfony PropertyAccess Component
symfony/routing                                  v4.2.2             Symfony Routing Component
symfony/security-bundle                          v4.2.2             Symfony SecurityBundle
symfony/security-core                            v4.2.2             Symfony Security Component - Core Library
symfony/security-csrf                            v4.2.2             Symfony Security Component - CSRF Library
symfony/security-guard                           v4.2.2             Symfony Security Component - Guard
symfony/security-http                            v4.2.2             Symfony Security Component - HTTP Integration
symfony/stopwatch                                v4.2.2             Symfony Stopwatch Component
symfony/swiftmailer-bundle                       v3.2.5             Symfony SwiftmailerBundle
symfony/templating                               v4.2.2             Symfony Templating Component
symfony/translation                              v4.2.2             Symfony Translation Component
symfony/twig-bridge                              v4.2.2             Symfony Twig Bridge
symfony/twig-bundle                              v4.2.2             Symfony TwigBundle
symfony/validator                                v4.2.2             Symfony Validator Component
symfony/var-dumper                               v4.2.2             Symfony mechanism for exploring and dumping PHP variables
symfony/var-exporter                             v4.2.2             A blend of var_export() + serialize() to turn any serializable data structure to plain PHP code
symfony/web-profiler-bundle                      v4.2.2             Symfony WebProfilerBundle
symfony/web-server-bundle                        v4.2.2             Symfony WebServerBundle
symfony/yaml                                     v4.2.2             Symfony Yaml Component

为清楚起见 - 我在 生产 环境中 运行 宁这个项目,我使用 Apache2 来完成这项工作。

在我的项目中,我只有一个 .env 文件,没有任何带有环境变量的 *.local 文件。该文件如下所示:

APP_ENV=prod
APP_SECRET=928374923784mysecretkey92837498273

使用这些设置我看不到调试页面 - 正确的行为

APP_ENV=prod
APP_DEBUG=1
APP_SECRET=928374923784mysecretkey92837498273

如果我添加值为 1 的变量 APP_DEBUG - 显示调试页面 - 正确行为

我添加了一个名为 prod1:

的自定义环境
APP_ENV=prod1
APP_SECRET=928374923784mysecretkey92837498273

现在调试页面默认可见 - 正确的行为

我添加了APP_DEBUG=0:

APP_ENV=prod1
APP_DEBUG=0
APP_SECRET=928374923784mysecretkey92837498273

并且没有出现调试页面 - 正确的行为


如您所见,我无法重现此错误。在 Symfony 4.2.2 中,一切似乎都正常工作。在您的项目或 Web 服务器配置中搜索错误,因为这是问题的原因。我认为您在某处覆盖了 APP_DEBUG 变量。也许在 Apache/Nginx 虚拟主机配置中:

More informations here

正如Siavas所写,它也可以是一个缓存。尝试手动删除var/log目录和运行composer install


遗憾的是我没有更多的想法,如果项目不是严格保密的,你可以分享到github,也许我可以帮助你更多

问题是,我在我的项目中配置错误 ExceptionController

  1. FOSRestBundle 有自己的 ExceptionController 用于显示 json/xml 异常

你在你的 config/fos_rest.yaml:

中配置它
fos_rest
    exception:
        enabled: true
        exception_controller: 'App\Controller\ExceptionController::handleExceptionAction'
  1. Twig 是 symfony 的默认异常控制器

你在你的 config/twig.yaml:

中配置它
twig:
    debug: '%kernel.debug%'
    exception_controller: App\Controller\ExceptionController::showException

使用的 Exception Handler 是 fos_rest,我在 services.yaml 中配置错误:

App\Controller\ExceptionController:
  - '@fos_rest.view_handler.default'
  - '@fos_rest.exception.messages_map'
  -  true   <------ this should be '%kernel.debug%'
  - '@templating.engine.twig'
  - '@logger'

问题是,这里的第三个参数应该是%kernel.debug%

最后,我最终得到了 2 个 ExceptionControllers,一个用于 API 和 json 格式的 Response,一个用于 Sonata Backend,标准的 Twig 异常控制器。唯一的解决方案是,将请求从一个控制器转发到另一个控制器:

 <?php

namespace App\Controller;

use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController as TwigExceptionController;

class ExceptionController
{
    protected $debug;

    /**
     * @var TwigExceptionController
     */
    protected $twigExceptionController;

    /**
     * @param bool $debug
     * @param TwigExceptionController $twigExceptionController
     */
    public function __construct(
        bool $debug,
        TwigExceptionController $twigExceptionController
    )
    {
        $this->debug = $debug;
        $this->twigExceptionController = $twigExceptionController;
    }

    /**
     * Converts an Exception to a Response.
     *
     * @param Request                   $request
     * @param FlattenException     $exception
     * @param LoggerInterface $logger
     *
     * @return Response
     */
    public function handleExceptionAction(Request $request, FlattenException $exception, LoggerInterface $logger): Response
    {    
        if (!stristr( strtolower($request->getUri()), '/api/')) {
            return $this->twigExceptionController->showAction($request, $exception, $logger);
        }

        $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
        if ($currentContent) {
            $logger->error('current content: '.$currentContent);
        }

        $code = $this->getStatusCode($exception);
        $response = new JsonResponse();
        $response->setStatusCode($code);
        $response->setData([
            'message' => $exception->getMessage(),
            'code' => $code
        ]);
        return $response;
    }


    /**
     * Determines the status code to use for the response.
     *
     * @param \Exception $exception
     *
     * @return int
     */
    protected function getStatusCode($exception): int
    {
        if (method_exists($exception, 'getCode') && $exception->getCode() > 0) {
            return $exception->getCode();
        }
        if (method_exists($exception, 'getStatusCode') && $exception->getStatusCode() > 0) {
            return $exception->getStatusCode();
        }

        return 500;
    }

    /**
     * Gets and cleans any content that was already outputted.
     *
     * This code comes from Symfony and should be synchronized on a regular basis
     * see src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php
     *
     * @return string
     */
    private function getAndCleanOutputBuffering($startObLevel)
    {
        if (ob_get_level() <= $startObLevel) {
            return '';
        }
        Response::closeOutputBuffers($startObLevel + 1, true);
        return ob_get_clean();
    }
}