I agree with the assessment of the differences and the pros and cons of Logger and Monolog.
A potential follow-up could explore how the Monolog module might be made more suitable for site builders and address the needs and advantages mentioned for Logger here, if there is interest in pursuing that. (For the record, I also raised the question “Why not Monolog? Why (Extended) Logger?” in 
            
              
              
              📌
              [Meta] AI Logging/Observability
                Active
              
            , so perhaps interest in improving the site builder experience for Monolog will grow from that discussion as well.)
@murz, could you please update the project page with the changes from the README.md?
            
              https://www.drupal.org/project/logger →
            
I no longer have a personal interest in this project.
@berdir Could you please also set the project Maintenance status to Seeking co-maintainer or new maintainer accordingly and maybe initiate the related process → ? Maybe also change the Development status?
Hi,
I am interested to join the effort here. I have read the complete thread and something bothers me since the begging, with all respect to the effort that was spent on Extended Logger: Why Extended Logger? Why not Monolog?
Monolog is a de facto standard library for logging in the PHP ecosystem. It has several extensions, for example an official OT handler: https://github.com/opentelemetry-php/contrib-logger-monolog
Anything that (Extended) Logger does and born for should be already possible with Monolog library/
            
              module →
            ,see 
            
              
              
              📌
              Monolog stores metadata together with the log entry
                Active
              
            . 
Extended Logger also seems to be replaced by Logger → based on its project page,so if we do not go with Monolog (by why not?) then we should depend on the latter.
+1
The Monolog module can indeed provide equivalent functionality to this Logger module. As demonstrated in the issue, Monolog already supports storing structured contextual data alongside log entries through its context array and extra fields when using appropriate formatters (such as the JSON formatter) and handlers (such as file or database handlers).
Monolog has become the de facto standard for logging in the PHP ecosystem, implementing the PSR-3 interface and providing extensive flexibility through its handler, formatter, and processor architecture.
Let's decide if 🐛 Add Cache Context to Views when using entity QueryAccessHandler Active is a duplicate of this one and if yes, which fix addresses the problem the best possible way.
Even if this is a duplicate of the other, at least one of them needs test coverage so we can move forward with fixing the problem.
Great job everyone!
DDEV Contrib tests still passed after these changes so everything looks okay thefe
https://github.com/ddev/ddev-drupal-contrib/actions/runs/18923385758
In a related issue we will figure out what we could do there exactly with recipe testing: https://github.com/ddev/ddev-drupal-contrib/issues/151
How will this actually land in the actual plugin? https://github.com/drupal/core-recipe-unpack
AFAIK via git split from Drupal core, so there is no need to touch that project directly.
Changes and test coverage looks good to me, I am really glad that this problem could be fixed with such a simple change!
Could this be optional?
Good question. At this stage, we have decided to follow the approach used by Claude in their llms.txt file, where content in multiple languages is included within the same file.
See: https://docs.claude.com/llms.txt
We may conduct further research in the future to better understand how llms.txt evolves in the context of multilingual sites. Specifically, we aim to determine whether LLM bots can automatically retrieve language-specific content based on path aliases (or potentially through another mechanism, like the Accept-Language) header), or if they will continue to access the main /llms.txt file, from which they can discover links to language-specific subpages.
(P.S. The latter scenario could already be achieved by using LLM section entities or by referencing markdown pages in llms.txt that contain link trees for different languages.)
I discussed this issue with @alexpott in Vienna. Although the problem does exist and might be at least partially addressed by 📌 RecipeRunner::processInstall() installs modules one by one Active , my perspective on whether we should use Kernel tests to cover a recipe has changed after our conversation. Kernel tests are very low-level and require extensive setup to run successfully, which makes them unsuitable for applying even simple recipes. Such tests would likely fail for reasons unrelated to the actual functionality.
Recipe testing requires a fully configured site with all minimal dependencies fully installed.
Well, 🐛 Form build caching is broken in Drupal 11 Active is still a critical issue open so probably that is a stable blocker.
The change record is quite a bit longer than what I usually see,
I agree and had the same feeling as well, but I think this change definitely worth a proper introduction (and celebrating).
Discussed this with @Kingdutch and @alexpott and confirmed that we think this is a good idea.
\o/
Once that's written this can move back to "Needs review" and this should be good to go.
Draft a change record, please review.
Also tried to fix CS issues on the MR, but I could not fix one of them for some reasons.
I'll try to talk about the root cause with @phenaproxima today at DrupalCon.
The conclusion of that discussion can be also found here: ✨ Add getRecipePath() helper method to RecipeTestTrait Active
Should be fixed in https://www.drupal.org/project/llms_txt/releases/1.0.4 → . Please check and report back. Thank you
Please share any relevant settings of the Redirect module if you have it enabled
1. the Setting page contnet
2. If you have any URL redirects for any reasons for the /llms.txt path.
Please also update the issue title and especially the description with reproduction steps.
Thank you!
https://www.drupal.org/project/disable_route_normalizer → project page says:
This simple module is intended to be used on a multilingual site using redirect module when you need to have a language neutral content page without any redirection to a path prefix, which means there will not be any preferential language shown in the URL.
The functionality provided is available by default on clean Drupal installation but not when using redirect.
Okay, so you use Apache,we usually use Nginx. Do you also have Redirect module enabled? Or do you have an idea how to extend the current test coverage of the module with a failing test so it could be proved this change fixes a problem?
----------
Understanding _disable_route_normalizer in Drupal
_disable_route_normalizer is a special route/request attribute that tells Drupal's route normalizer (commonly exercised by the Redirect module's "Enforce clean and canonical URLs") to skip canonicalization and avoid issuing redirects for the current request or route, which is useful when custom path processing or non-canonical endpoints must not be altered or redirected.
What it does
- When set, it prevents the normalizer from rewriting or redirecting a path to its canonical form, so custom inbound path processors or language-specific paths can resolve without being forced to a different URL.
 - It is often used to avoid unwanted 301 redirects triggered by the Redirect module when paths are intentionally "non-canonical," such as virtual paths or special endpoints.
 
How to use it on a route
- In a route definition, add the option/attribute to the route so requests hitting that route are ignored by the normalizer, for example in routing.yml via a route option that sets the attribute at runtime (various contrib issues and examples describe "Add _disable_route_normalizer to route" to stop normalization).
 - Contrib modules and issue queues show adding this flag to routes like OpenID configuration endpoints or robots.txt analogs to prevent Redirect from interfering with expected behavior.
 
How to use it per request
- If setting per request (rather than per route), an event subscriber can set the request attribute before the Redirect module's kernel.request listener runs: set 
request->attributes['_disable_route_normalizer'] = truewith a higher priority than the Redirect subscriber, so the normalizer sees the flag and skips redirecting. 
When to reach for it
- Custom inbound-only path processors that map pretty or virtual paths to internal routes, where the Redirect module would otherwise bounce the user to a canonical URL and break the intended UX.
 - Special endpoints (e.g., OAuth/OpenID discovery, metrics, or robots-like outputs) where any normalization or redirect would cause client failures or break protocol expectations, thus requiring the normalizer to be disabled for those routes.
 
Practical example
A module implements an inbound path processor to map /some-random-path to an internal node without changing outbound links. To avoid a forced redirect back to the canonical node path, an event subscriber sets _disable_route_normalizer on the request early, so the Redirect module respects the custom mapping and does not redirect the user away from the intended path.
References
- Disable Route Normalizer - Drupal.org →
 - Add _disable_route_normalizer flag to monitoring_prometheus 📌 Add _disable_route_normalizer flag to monitoring_prometheus route Active
 - Disable route normalization from redirect module →
 - Drupal 10: Creating Custom Paths With Path Processors
 - Disable route normalization for OpenID configuration path →
 - Disable Route Normalizer →
 - Add _disable_route_normalizer in routing.yml
 
Tested the change manually and also in the spin off issue where automated tests are finally running: https://git.drupalcode.org/project/ai_recipe_llm_optimized_content/-/pip...
/builds/project/ai_recipe_llm_optimized_content
$ # If the PROJECT_NAME variable has not been defined with an override value then get it by searching for *.info.yml # collapsed multi-line command
ls: cannot access '*.info.yml': No such file or directory
Just spotted this in the log, probably something to silence for a recipe build
I think I found a related one: ✨ Expand Drupal\Core\Recipe\RecipeDiscovery to allow discovering available recipes, likely for use in Project Browser Active . If anybody has any insights to add there, please do.
Also let me know if you disagree with my current understanding on that every changes is necessary here, but testing on a recipes with dependencies on other recipes are still failing - and the current workaround is no go - because the limitations of the Recipe Installer in Drupal core.
There are two current use cases for this:
I believe there's a third use case that may be relevant: running automated tests on recipes with dependencies on other (contrib) recipes in GitLab CI environments. The main roadblock we encountered in ✨ Add recipes path handling Active is how Recipe Installer currently discovers recipes in the filesystem.
Question: Do you think this problem relates to this issue, or should it be reported separately? Or perhaps it's not a core issue at all?
I haven't tested this patch with my POC playground → alongside the GitLab changes from the other issue. From what I can tell, this issue only introduces a new API that isn't yet leveraged by the Recipe Installer ecosystem.
I have concerns about how QA environments are configured on GitLab (and also by DDEV Drupal Contrib for local development). In these setups, the component under test (module, theme, recipe, etc.) gets symlinked to a special location while residing in the site root. This setup pattern might create additional complications for recipe discovery and installation workflows.
Has anyone considered how this new API would interact with symlinked recipe scenarios common in CI/CD environments?
PS.: I assume DrupalCMS recipes never discovered this problem because they live in a monorepo and QA-d there, instead of in many-repo splits.
It would be nice if the core getRecipePath() actually did the correct thing, but it doesn't.
Exactly the reason why I am considering opening a ticket for the Recipe project, because I think the roadblock is there.
Thanks for the quick reply (DM on Slack) :)
 I used the existing environment variable DRUPAL_PROJECT_FOLDER which is now set precisely to the required value for this. See this change to the d11-recipe branch test that is exactly what you could do.
Unfortunately, I do not think this is a valid solution because it couples the test to the execution environment.. The path discovery should only rely on what is inside the package under test for reference.
Recipes in Drupal core is special of course, but they also do dynamic discovery in tests: https://github.com/drupal/core/blob/11.2.5/modules/system/tests/src/Func...
Just as Drupal CMS and other recipes that has test coverage:
* https://git.drupalcode.org/project/drupal_cms_forms/-/blob/1.2.x/tests/s...
* https://git.drupalcode.org/search?search=%22applyRecipe%28%22&group_id=2... (yes, the list is not extensive)
Okay, so now after all this noise (sorry about that) I am probably at the point that I can report a valid problem that my test still fails because the llm_support dependency still cannot be installed by the recipe installer.
https://git.drupalcode.org/project/ai_recipe_llm_optimized_content/-/pip...
1) Drupal\Tests\ai_recipe_llm_optimized_content\Functional\RecipeApplicationTest::testApplicabilityAndIdempotency
Process exit code mismatch.
Expected: 0
Actual: 1
STDOUT:
STDERR:
In RecipeFileException.php line 56:
                                                                               
  Validation errors were found in /builds/project/ai_recipe_llm_optimized_con  
  tent/recipe.yml:                                                             
  - [recipes][0]: The llm_support recipe does not exist at /builds/project.    
                                                                               
recipe [-i|--input INPUT] [--] <path>
Failed asserting that 1 is identical to 0.
/builds/project/ai_recipe_llm_optimized_content/web/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeTestTrait.php:87
/builds/project/ai_recipe_llm_optimized_content/tests/src/Funtional/RecipeApplicationTest.php:49
FAILURES!
Thoughts? Error on my end?
ohh wait, even if my pipeline is on a fork, this shell script downloads the latest stable version of symlink-project.php? :O
get-file-via-curl.sh executing curl --retry 3 -OLf https://git.drupalcode.org/project/gitlab_templates/-/raw/default-ref/scripts/symlink_project.php
as far as I can tell the root cause of the failure that the recipe under testing was symlinked to the wrong folder, it is here instead of ../recipes.
https://git.drupalcode.org/project/ai_recipe_llm_optimized_content/-/job...
From pipeline log
Creating symlinks in /builds/project/ai_recipe_llm_optimized_content/web//ai_recipe_llm_optimized_content pointing back to files in /builds/project/ai_recipe_llm_optimized_content
Let me check if I can try the latest commit here in #3551179.
Composer jobs fail with
$ rm symlink_project.php
$ echo -e "\e[0Ksection_end:`date +%s`:symlink_output\r\e[0K"
$ if [[ -f composer.json.backup ]]; then # collapsed multi-line command
Restoring composer.json from backup
rm: cannot remove '/builds/project/ai_recipe_llm_optimized_content/recipes/ai_recipe_llm_optimized_content/composer.json': No such file or directory
Uploading artifacts for failed job 00:07
Cleaning up project directory and file based variables 00:01
ERROR: Job failed: command terminated with exit code 1https://git.drupalcode.org/project/ai_recipe_llm_optimized_content/-/job...
I think this is exactly the issue that I wanted to report and built a POC for this in 📌 POC: Testing on Gitlab with recipe dependencies is broken Active \o/
I am also glad to see my other recipe (llm_support) mentioned in this thread =], ai_recipe_llm_optimized_content adds a dependency on it and everything started to fall apart from that moment...
Let me check if I can try the latest commit here in #3551179.
Opened a follow up ✨ Ability to add markdown link field in a views view Active
Opened a follow up for resolving the main blocker in Markdownify #3551170: POC: Universal markdown conversion via .md URL suffix →
Opened 📌 Finalize repo initalization Active
I think this can be closed now. Feel free to re-open if you disagree.
Opened ✨ Add dependency on llm_support recipe Active .
So now that 
            
              llm_support has main- and footer menu added →
             the way we agreed on Slack, I would suggest closing this issue and opening two follow ups for:
1. creating a proper readme/project page
2. adding a dependency on llm_support recipe.
Due to preparations for DrupalCon, MR#2 was merged.
Created a follow up for test coverage: #3550980: Test coverage for "Add field "Include in llms.txt" for all content types" →
Due to preparations for DrupalCon, MR#6 was merged.
Created a follow up for test coverage: #3550976: Test coverage for Add token for rendering a markdown version of a Views view →
Let's discuss on Drupal Slack if this feature should be part of LLM support recipe directly or not, currently no objection for that.
However, we either include non-marked menu links as they are today, or we may have a problem with always exposing markdown links, see #3548981-4: Output markdown menu links when Markdownify is enabled →
There are some unnecessary extra new lines, but I think we are getting there. Now the next task is probably a bit of a test coverage for this and the patch for llms_txt.
# Drush Site-Install
### Our work
Lorem ipsum dolor sit amet consectetur adipiscing elit quisque faucibus.
- [Circus of All](https://llm-support.local.pronovix.net/node/2.md)
- [Hotel Lorem](https://llm-support.local.pronovix.net/node/1.md)
 
### Resources
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
- [Contact us](https://llm-support.local.pronovix.net/node/3.md)
Test coverage would be great, especially on cache invalidation.
Current state of MR#6 without changes on phpstan baseline so it could be applied on the llm_support recipe.
Also, the work is being done in ✨ Improve linking to md version of an entity Active could also help with directly generating markdown links for nodes.
Not alone, after that entity views data also has to be altered so the Markdown link would be available in Views field settings. \Drupal\views\EntityViewsData::addEntityLinks()
Maybe recent results in #3548981-4: Output markdown menu links when Markdownify is enabled → are just proves that we also do not and cannot serialize any kind of Views view to markdown. Links after the forced transformation may not work.
The special case we are building the ideal examole for transfermarion, maybe we should support those kinds of Views somehow...
Also, the work is being done in ✨ Improve linking to md version of an entity Active could also help with directly generating markdown links for nodes.
@a.dmitriiev shared the following code examples, thanks for this!
  $info['types']['llm_txt_views'] = [
    'name' => t('llms.txt: Views'),
    'description' => t('Render views in markdown for llms.txt'),
  ];
  $views = \Drupal::entityTypeManager()->getStorage('view')->loadMultiple();
  /** @var \Drupal\views\ViewEntityInterface $view */
  foreach ($views as $view) {
    foreach ($view->get('display') as $display) {
      $key = $view->id() . '=' . $display['id'];
      $info['tokens']['llm_txt_views'][$key] = [
        'name' => $view->label() . ' ' . $display['display_title'],
      ];
    }
  }
  if ($type === 'llm_txt_views') {
    foreach ($tokens as $name => $original) {
      [$view_id, $display_id] = explode('=', $name, 2);
      if (!empty($view_id) && !empty($display_id)) {
        $view = Views::getView($view_id);
        if (!empty($view) && $view->access($display_id)) {
          $view->setDisplay($display_id);
          $view->initHandlers();
          $preview = $view->preview($display_id);
          $html = \Drupal::service('renderer')->renderInIsolation($preview);
          if (\Drupal::moduleHandler()->moduleExists('markdownify')) {
            $base_url = \Drupal::service('router.request_context')->getCompleteBaseUrl();
            $markdown = \Drupal::service('markdownify.html_converter')->convert($html);
            $markdown = preg_replace('/('. preg_quote($base_url, '/') . '[a-zA-Z0-9-_\/]+)/', '${1}.md', $markdown);
            $replacements[$original] = $markdown;
          }
        }
      }
    }
  }
My spidey senses indicates that cacheability information is lost somewhere here, even if nothing in the call chain of $view->access($display_id) bubbles up cacheability information - why is that? probably there is a Core issue for this, nvm for now... - . So I think it would be better to run the complete rendering in a dedicated render context and bubbling up the collected cacheability information, just in case.
markdownify_views is surely needed for this feature. Second guessing a bit but should this feature be included in that module? What is your thought on this @iambatman?
I was also thinking about why views_embed_view() has not been leveraged in the original code, but then I realized that it is not important. The most important part probably doing the same what is done in \Drupal\markdownify_views\Controller\MarkdownifyViewPageController::handle(), or calling that code for consistently generating the markdown version of a Views view.
Thanks for the contribution, it looks like a simple change!
I would rebase from 1.x to be sure, but otherwise should be ready to be merged.
# Drush Site-Install
Main menu
- [All nodes](https://llms-txt.local.pronovix.net/node.md): All nodes on the site
- [Home](https://llms-txt.local.pronovix.net/.md)
User menu
- [My account](https://llms-txt.local.pronovix.net/user.md)
- [Log out](https://llms-txt.local.pronovix.net/user/logout?token=[redacted].md)
## Section 1
<p>Lorem ipsum...</p>
I think I see a couple of problem with appending .md suffix to all menu links.
For bravery and pushing the limits of this change, I have tried this with uid1.
1. https://llms-txt.local.pronovix.net/.md Makes no sense, but just the first proof that simple suffixing won't work.
2. https://llms-txt.local.pronovix.net/user.md HTTP 404
3. https://llms-txt.local.pronovix.net/user/logout?token=fzL0Ox4jS6qafdt6gz... Proves that simple suffixing does not work due to potential query parameters, it has to be figured out if and how the path can be modified. Probably we have to construct a new \Drupal\Core\Url object for that.
4. https://llms-txt.local.pronovix.net/node.md also HTTP 404 until markdownify_views is enabled, but this is just one proof for not all every internal url could be turned into a markdown url that actually resolves.
mxr576 → changed the visibility of the branch 3550681-add-field-include to active.
I have also evolved our original plain simple idea that was centered around a simple checkbox for including content in llms.txt to leveraging a including and grouping around taxonomy terms in llms.txt which could actually unleash the full potential of the CMS.
Please review and comment.
I just found out, that this recipe doesn't require the node module to be installed.
Not yet, it could be. As I wrote on Slack, I have started to work on this implementation in the weekend already. Everything is starting to click, other than that part of the original idea where the Views view is embeded to llms.txt with a token - unless we meant a custom token :)
Let's continue the discussion on Slack on this one.