Log missing CSS assets in CSS Optimizer

Created on 2 December 2024, 21 days ago

Problem/Motivation

The Drupal\Core\Asset\CssOptimizer class presently suppresses errors in the call to file_get_contents. This makes it very difficult to perform root cause analysis in complicated scenarios involving incorrect assets being served by reverse proxies and such.

Steps to reproduce

  1. Remove a mission-critical CSS file from disk
  2. Flush the drupal cache and trigger re-aggregation that would include said asset
  3. Restore the mission-critical CSS file to disk
  4. Attempt to definitively prove to the hosting provider that the reason for a broken looking site over the Thanksgiving holiday break was that the file temporarily could not be read versus "end-user error"

Proposed resolution

Stop suppressing the error so that there's a paper trail.

Remaining tasks

TBD

User interface changes

None

Introduced terminology

None

API changes

None

Data model changes

None

Release notes snippet

TBD

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component

asset library system

Created by

πŸ‡ΊπŸ‡ΈUnited States luke.leber Pennsylvania

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @luke.leber
  • πŸ‡¬πŸ‡§United Kingdom catch

    This comment was added in #927406: Colorable themes fail when CSS aggregation is enabled due to improper stream wrapper usage β†’ but then I looked at the issue and the error suppression was replacing a file_exists(), which was added in #901062: Regression: Themes can no longer remove stylesheets by overriding them in .info files β†’ . I really wonder whether we even support that feature at all? Even if we do, we could deprecate it - stylesheets_remove exists.

  • πŸ‡ΊπŸ‡ΈUnited States luke.leber Pennsylvania

    Given the uncertainty about what's supported or not in #2, I think maybe we could add this behind an opt-in behavior to allow for a graceful path to deprecation?

    I'm sure that a lot of sites out there have missing assets that's considered very, very normal.

  • πŸ‡ΊπŸ‡ΈUnited States luke.leber Pennsylvania

    Ideally when we know that a corrupted asset is going to be generated, we'd want to issue an HTTP/500 to prevent the asset from becoming cached by downstream infrastructure, but that definitely can't happen without breaking huge swathes of sites.

  • πŸ‡ΊπŸ‡ΈUnited States luke.leber Pennsylvania

    Here's something for us to run temporarily in the new year...

    diff --git a/core/assets/scaffold/files/default.settings.php b/core/assets/scaffold/files/default.settings.php
    index cd364bb00d..751a5de5ff 100644
    --- a/core/assets/scaffold/files/default.settings.php
    +++ b/core/assets/scaffold/files/default.settings.php
    @@ -811,6 +811,14 @@
      */
     $settings['migrate_node_migrate_type_classic'] = FALSE;
     
    +/**
    + * Aggregated asset error logging.
    + *
    + * This is used to inform site owners when CSS or JS assets fail to be included
    + * in an aggregated asset.
    + */
    +$settings['log_aggregated_asset_errors'] = FALSE;
    +
     /**
      * The default settings for migration sources.
      *
    diff --git a/core/lib/Drupal/Core/Asset/CssOptimizer.php b/core/lib/Drupal/Core/Asset/CssOptimizer.php
    index 8f2d910250..61d00ea129 100644
    --- a/core/lib/Drupal/Core/Asset/CssOptimizer.php
    +++ b/core/lib/Drupal/Core/Asset/CssOptimizer.php
    @@ -5,6 +5,7 @@
     use Drupal\Component\Utility\Unicode;
     use Drupal\Core\StreamWrapper\StreamWrapperManager;
     use Drupal\Core\File\FileUrlGeneratorInterface;
    +use Drupal\Core\Site\Settings;
     
     /**
      * Optimizes a CSS asset.
    @@ -145,7 +146,11 @@ public function loadFile($file, $optimize = NULL, $reset_base_path = TRUE) {
         // stylesheets in their .info.yml file that don't exist in the theme's path,
         // but are merely there to disable certain module CSS files.
         $content = '';
    -    if ($contents = @file_get_contents($file)) {
    +
    +    // Temporary hack for debugging impossible to diagnose aggregation issues...
    +    $contents = Settings::get('log_aggregated_asset_errors') ? file_get_contents($file) : @file_get_contents($file);
    +
    +    if ($contents !== FALSE) {
           // If a BOM is found, convert the file to UTF-8, then use substr() to
           // remove the BOM from the result.
           if ($encoding = (Unicode::encodingFromBOM($contents))) {
    
    

    Unrelated - there should probably be a strict comparison against the file_get_contents result here -- otherwise some false positives can slip through depending on the file contents 😱!

Production build 0.71.5 2024