[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: reidmorrison\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "### Environment\n\nProvide at least:\n* Ruby Version.\n* Rails Version.\n* Semantic Logger Version.\n* Rails Semantic Logger Version.\n* Other Application/framework names and versions (e.g. Puma, etc.).\n* Rails configuration. Only need the settings related to Rails Semantic Logger and Semantic Logger.\n* Full Stack Trace, if an exception is being raised.\n\nNote:\n* Issues reported here should be related to monkey patches applied to Rails to make it use Semantic Logger. \n* For other logging and appender related issues, please report the issue at [Semantic Logger](https://github.com/reidmorrison/semantic_logger/issues.\n\n### Expected Behavior\n\n* Describe your expectation of how Semantic Logger should behave, perhaps by showing how the builtin Rails logger behaves.\n* Provide a standalone Ruby script or a link to an example repository that helps reproduce the issue.\n\n### Actual Behavior\n\n* Describe or show the actual behavior.\n* Provide text or screen capture showing the behavior.\n\n### Pull Request\n\n* Consider submitting a Pull Request with a fix for the issue.\n    * This is particularly helpful when running newer Rails versions, since we are not running it yet.\n* Or, even a Pull request that only includes a test that reproduces the problem. \n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "### Issue # (if available)\n\n\n### Description of changes\n\n\nBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: build\n\non:\n  - push\n  - pull_request\n\njobs:\n  test:\n    name: \"Test: Rails ${{ matrix.rails }} on Ruby ${{ matrix.ruby }}\"\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - rails: \"6.1\"\n            ruby: \"3.2\"\n\n          - rails: \"7.0\"\n            ruby: \"3.2\"\n          - rails: \"7.0b\"\n            ruby: \"3.2\"\n\n          - rails: \"7.1\"\n            ruby: \"3.2\"\n          - rails: \"7.1b\"\n            ruby: \"3.2\"\n          - rails: \"7.1.1\"\n            ruby: \"3.2\"\n\n          - rails: \"7.2\"\n            ruby: \"3.3\"\n\n          - rails: \"8.0\"\n            ruby: \"3.4\"\n\n          - rails: \"8.1\"\n            ruby: \"3.4\"\n\n    env:\n      BUNDLE_GEMFILE: gemfiles/rails_${{ matrix.rails }}.gemfile\n      DISPLAY: \":99.0\"\n\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.ruby }}\n          # runs 'bundle install' and caches installed gems automatically\n          bundler-cache: true\n\n      - name: Gemfile\n        run: echo $BUNDLE_GEMFILE\n\n      - name: Ruby Version\n        run: ruby --version\n\n      - name: Run Tests\n        run: bundle exec rake test\n"
  },
  {
    "path": ".gitignore",
    "content": ".bundle/\n*.log\npkg/\ntest/dummy/tmp/\ntest/dummy/db/test.sqlite3-*\ntest/dummy/.sass-cache\n*.gem\n/.idea\ntags\n*.DS_Store\nGemfile.lock\n/gemfiles/*.lock\n\n*.sqlite3\n.rakeTasks\nTODO.md\n\n.tool-versions\ntest/dummy/db/test.sqlite3\nmise.toml\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "AllCops:\n  Exclude:\n    - \".git/**/*\"\n    - \"docs/**/*\"\n    - \"gemfiles/*\"\n  NewCops: enable\n  TargetRubyVersion: 2.5\n\n#\n# RuboCop built-in settings.\n#   For documentation on all settings see: https://docs.rubocop.org/en/stable\n#\n\n# Trailing periods.\nLayout/DotPosition:\n  EnforcedStyle: trailing\n\n# Turn on auto-correction of equals alignment.\nLayout/EndAlignment:\n  AutoCorrect: true\n\n# Prevent accidental windows line endings\nLayout/EndOfLine:\n  EnforcedStyle: lf\n\n# Use a table layout for hashes\nLayout/HashAlignment:\n  EnforcedHashRocketStyle: table\n  EnforcedColonStyle: table\n\n# Soften limits\nLayout/LineLength:\n  Max: 128\n  Exclude:\n    - \"**/test/**/*\"\n\n# Match existing layout\nLayout/SpaceInsideHashLiteralBraces:\n  EnforcedStyle: no_space\n\n# TODO: Soften Limits for phase 1 only\nMetrics/AbcSize:\n  Max: 40\n\n# Support long block lengths for tests\nMetrics/BlockLength:\n  Exclude:\n    - \"test/**/*\"\n    - \"**/*/cli.rb\"\n  AllowedMethods:\n    - \"aasm\"\n    - \"included\"\n\n# Soften limits\nMetrics/ClassLength:\n  Max: 250\n  Exclude:\n    - \"test/**/*\"\n\n# TODO: Soften Limits for phase 1 only\nMetrics/CyclomaticComplexity:\n  Max: 15\n\n# Soften limits\nMetrics/MethodLength:\n  Max: 50\n\n# Soften limits\nMetrics/ModuleLength:\n  Max: 250\n\nMetrics/ParameterLists:\n  CountKeywordArgs: false\n\n# TODO: Soften Limits for phase 1 only\nMetrics/PerceivedComplexity:\n  Max: 21\n\n# Initialization Vector abbreviation\nNaming/MethodParameterName:\n  AllowedNames: [ \"iv\", \"_\", \"io\", \"ap\", \"id\", \"_id\" ]\n\n# Does not allow Symbols to load\nSecurity/YAMLLoad:\n  AutoCorrect: false\n\n# Needed for testing DateTime\nStyle/DateTime:\n  Exclude: [ \"test/**/*\" ]\n\n# TODO: Soften Limits for phase 1 only\nStyle/Documentation:\n  Enabled: false\n\n# One line methods\nStyle/EmptyMethod:\n  EnforcedStyle: expanded\n\n# Ruby 3 compatibility feature\nStyle/FrozenStringLiteralComment:\n  Enabled: false\n\nStyle/NumericPredicate:\n  AutoCorrect: true\n\n# Incorrectly changes job.fail to job.raise\nStyle/SignalException:\n  Enabled: false\n\n# Since English may not be loaded, cannot force its use.\nStyle/SpecialGlobalVars:\n  Enabled: false\n\n# Make it easier for developers to move between Elixir and Ruby.\nStyle/StringLiterals:\n  EnforcedStyle: double_quotes\n"
  },
  {
    "path": "Appraisals",
    "content": "appraise \"rails_6.1\" do\n  gem \"rails\", \"~> 6.1.0\"\n  gem \"sidekiq\", \"~> 5.2\"\n  gem \"sqlite3\", \"~> 1.4\"\nend\n\nappraise \"rails_7.0\" do\n  gem \"rails\", \"~> 7.0.0\"\n  gem \"sidekiq\", \"~> 6.2.0\"\n  gem \"sqlite3\", \"~> 1.4\"\nend\n\nappraise \"rails_7.0b\" do\n  gem \"rails\", \"~> 7.0.0\"\n  gem \"sidekiq\", \"~> 6.5\"\n  gem \"sqlite3\", \"~> 1.4\"\nend\n\nappraise \"rails_7.1.1\" do\n  gem \"rails\", \"7.1.1\"\n  gem \"sidekiq\", \"~> 7.0.9\"\n  gem \"sqlite3\", \"~> 1.4\"\nend\n\nappraise \"rails_7.1\" do\n  gem \"rails\", \"~> 7.1.0\"\n  gem \"sidekiq\", \"~> 7.1.6\"\n  gem \"sqlite3\", \"~> 1.4\"\nend\n\nappraise \"rails_7.1b\" do\n  gem \"rails\", \"~> 7.1.0\"\n  gem \"sidekiq\", \"~> 7.3.0\"\n  gem \"sqlite3\", \"~> 1.4\"\nend\n\nappraise \"rails_7.2\" do\n  gem \"rails\", \"~> 7.2.0\"\n  gem \"sidekiq\", \"~> 7.2.4\"\nend\n\nappraise \"rails_8.0\" do\n  gem \"rails\", \"~> 8.0.0\"\n  gem \"sidekiq\", \"~> 7.2.4\"\nend\n\nappraise \"rails_8.1\" do\n  gem \"rails\", \"~> 8.1.1\"\n  gem \"sidekiq\", \"~> 7.2.4\"\nend\n"
  },
  {
    "path": "Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngemspec\n\ngem \"appraisal\"\ngem \"puma\"\n\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\n\ngem \"rails\", \"~> 8.1.0\"\ngem \"sidekiq\", \"~> 7.2.4\"\ngem \"sqlite3\"\n\ngem \"rubocop\"\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2012, 2013, 2014, 2015 Reid Morrison\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Rails Semantic Logger\n[![Gem Version](https://img.shields.io/gem/v/rails_semantic_logger.svg)](https://rubygems.org/gems/rails_semantic_logger) [![Build Status](https://github.com/reidmorrison/rails_semantic_logger/workflows/build/badge.svg)](https://github.com/reidmorrison/rails_semantic_logger/actions?query=workflow%3Abuild) [![Downloads](https://img.shields.io/gem/dt/rails_semantic_logger.svg)](https://rubygems.org/gems/rails_semantic_logger) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg)\n\nRails Semantic Logger replaces the Rails default logger with [Semantic Logger](https://logger.rocketjob.io/)\n\nWhen any large Rails application is deployed to production one of the first steps is to move to centralized logging, so that logs can be viewed and searched from a central location.\n\nCentralized logging quickly falls apart when trying to consume the current human readable log files:\n- Log entries often span multiple lines, resulting in unrelated log lines in the centralized logging system. For example, stack traces.\n- Complex Regular Expressions are needed to parse the text lines and make them machine readable. For example to build queries, or alerts that are looking for specific elements in the message.\n- Writing searches, alerts, or dashboards based on text logs is incredibly brittle, since a small change to the text logged can often break the parsing of those logs.\n- Every log entry often has a completely different format, making it difficult to make consistent searches against the data.\n\nFor these and many other reasons switching to structured logging, or logs in JSON format, in testing and production makes centralized logging incredibly powerful.\n\nFor example, adding these lines to `config/application.rb` and removing any other log overrides from other environments, will switch automatically to structured logging when running inside Kubernetes:\n~~~ruby\n    # Setup structured logging\n    config.semantic_logger.application = \"my_application\"\n    config.semantic_logger.environment = ENV[\"STACK_NAME\"] || Rails.env\n    config.log_level = ENV[\"LOG_LEVEL\"] || :info\n\n    # Switch to JSON Logging output to stdout when running on Kubernetes\n    if ENV[\"LOG_TO_CONSOLE\"] || ENV[\"KUBERNETES_SERVICE_HOST\"]\n      config.rails_semantic_logger.add_file_appender = false\n      config.semantic_logger.add_appender(io: $stdout, formatter: :json)\n    end\n~~~\n\nThen configure the centralized logging system to tell it that the data is in JSON format, so that it will parse it for you into a hierarchy.\n\nFor example, the following will instruct [Observe](https://www.observeinc.com/) to parse the JSON data and create machine readable data from it:\n~~~ruby\ninterface \"log\", \"log\":log\n\nmake_col event:parse_json(log)\n\nmake_col\n   time:parse_isotime(event.timestamp),\n   application:string(event.application),\n   environment:string(event.environment),\n   duration:duration_ms(event.duration_ms),\n   level:string(event.level),\n   name:string(event.name),\n   message:string(event.message),\n   named_tags:event.named_tags,\n   payload:event.payload,\n   metric:string(event.metric),\n   metric_amount:float64(event.metric_amount),\n   tags:array(event.tags),\n   exception:event.exception,\n   host:string(event.host),\n   pid:int64(event.pid),\n   thread:string(event.thread),\n   file:string(event.file),\n   line:int64(event.line),\n   dimensions:event.dimensions,\n   backtrace:array(event.backtrace),\n   level_index:int64(event.level_index)\n\nset_valid_from(time)\ndrop_col timestamp, log, event, stream\nrename_col timestamp:time\n~~~\n\nNow queries can be built to drill down into each of these fields, including `payload` which is a nested object.\n\nFor example to find all failed Sidekiq job calls where the causing exception class name is `NoMethodError`:\n~~~ruby\nfilter environment = \"uat2\"\nfilter level = \"error\"\nfilter metric = \"sidekiq.job.perform\"\nfilter (string(exception.cause.name) = \"NoMethodError\")\n~~~\n\nExample: create a dashboard showing the duration of all successful Sidekiq jobs:\n~~~ruby\nfilter environment = \"production\"\nfilter level = \"info\"\nfilter metric = \"sidekiq.job.perform\"\ntimechart duration:avg(duration), group_by(name)\n~~~\n\nExample: create a dashboard showing the queue latency of all Sidekiq jobs. \nThe queue latency is the time between when the job was enqueued and when it was started:\n~~~ruby\nfilter environment = \"production\"\nfilter level = \"info\"\nfilter metric = \"sidekiq.queue.latency\"\ntimechart latency:avg(metric_amount/1000), group_by(string(named_tags.queue))\n~~~\n\n* http://github.com/reidmorrison/rails_semantic_logger\n\n## Documentation\n\nFor complete documentation see: https://logger.rocketjob.io/rails\n\n## Upgrading to Semantic Logger V4.16 - Sidekiq Metrics Support\n\nRails Semantic Logger now supports Sidekiq metrics. \nBelow are the metrics that are now available when the JSON logging format is used:\n- `sidekiq.job.perform`\n  - The duration of each Sidekiq job.\n  - `duration` contains the time in milliseconds that the job took to run.\n- `sidekiq.queue.latency` \n  - The time between when a Sidekiq job was enqueued and when it was started.\n  - `metric_amount` contains the time in milliseconds that the job was waiting in the queue.\n\n## Upgrading to Semantic Logger v4.15 & V4.16 - Sidekiq Support\n\nRails Semantic Logger introduces direct support for Sidekiq v4, v5, v6, and v7. \nPlease remove any previous custom patches or configurations to make Sidekiq work with Semantic Logger.\nTo see the complete list of patches being made, and to contribute your own changes, see: [Sidekiq Patches](https://github.com/reidmorrison/rails_semantic_logger/blob/master/lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb)\n\n## Upgrading to Semantic Logger v4.4\n\nWith some forking frameworks it is necessary to call `reopen` after the fork. With v4.4 the\nworkaround for Ruby 2.5 crashes is no longer needed. \nI.e. Please remove the following line if being called anywhere:\n\n~~~ruby\nSemanticLogger::Processor.instance.instance_variable_set(:@queue, Queue.new)\n~~~\n\n## New Versions of Rails, etc.\n\nThe primary purpose of the Rails Semantic Logger gem is to patch other gems, primarily Rails, to make them support structured logging though Semantic Logger.\n\nWhen new versions of Rails and other gems are published they often make changes to the internals, so the existing patches stop working.\n\nRails Semantic Logger survives only when someone in the community upgrades to a newer Rails or other supported libraries, runs into problems, \nand then contributes the fix back to the community by means of a pull request.\n\nAdditionally, when new popular gems come out, we rely only the community to supply the necessary patches in Rails Semantic Logger to make those gems support structured logging.\n\n## Supported Platforms\n\nFor the complete list of supported Ruby and Rails versions, see the [Testing file](https://github.com/reidmorrison/rails_semantic_logger/blob/master/.github/workflows/ci.yml).\n\n## Author\n\n[Reid Morrison](https://github.com/reidmorrison)\n\n[Contributors](https://github.com/reidmorrison/rails_semantic_logger/graphs/contributors)\n\n## Versioning\n\nThis project uses [Semantic Versioning](http://semver.org/).\n"
  },
  {
    "path": "Rakefile",
    "content": "# Setup bundler to avoid having to run bundle exec all the time.\nrequire \"rubygems\"\nrequire \"bundler/setup\"\n\nrequire \"rake/testtask\"\nrequire_relative \"lib/rails_semantic_logger/version\"\n\ntask :gem do\n  system \"gem build rails_semantic_logger.gemspec\"\nend\n\ntask publish: :gem do\n  system \"git tag -a v#{RailsSemanticLogger::VERSION} -m 'Tagging #{RailsSemanticLogger::VERSION}'\"\n  system \"git push --tags\"\n  system \"gem push rails_semantic_logger-#{RailsSemanticLogger::VERSION}.gem\"\n  system \"rm rails_semantic_logger-#{RailsSemanticLogger::VERSION}.gem\"\nend\n\nRake::TestTask.new(:test) do |t|\n  t.pattern = \"test/**/*_test.rb\"\n  t.verbose = true\n  t.warning = false\nend\n\n# By default run tests against all appraisals\nif !ENV[\"APPRAISAL_INITIALIZED\"] && !ENV[\"TRAVIS\"]\n  require \"appraisal\"\n  task default: :appraisal\nelse\n  task default: :test\nend\n"
  },
  {
    "path": "TESTING.md",
    "content": "## Installation\n\nInstall all needed gems to run the tests:\n\n    appraisal install\n\nThe gems are installed into the global gem list.\nThe Gemfiles in the `gemfiles` folder are also re-generated.\n\n## Run Tests\n\nFor all supported Rails/ActiveRecord versions:\n\n    rake\n\nOr for specific rails version:\n\n    appraisal rails_4.2 rake\n\nOr for one particular test file:\n\n    appraisal rails_5.0 ruby test/controllers/articles_controller_test.rb \n\nOr down to one test case:\n\n    appraisal rails_5.0 ruby test/controllers/articles_controller_test.rb  -n \"/shows new article/\"\n"
  },
  {
    "path": "gemfiles/rails_6.1.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 6.1.0\"\ngem \"sidekiq\", \"~> 5.2\"\ngem \"sqlite3\", \"~> 1.4\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.0.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 7.0.0\"\ngem \"sidekiq\", \"~> 6.2.0\"\ngem \"sqlite3\", \"~> 1.4\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.0b.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 7.0.0\"\ngem \"sidekiq\", \"~> 6.5\"\ngem \"sqlite3\", \"~> 1.4\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.1.1.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"7.1.1\"\ngem \"sidekiq\", \"~> 7.0.9\"\ngem \"sqlite3\", \"~> 1.4\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.1.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 7.1.0\"\ngem \"sidekiq\", \"~> 7.1.6\"\ngem \"sqlite3\", \"~> 1.4\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.1b.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 7.1.0\"\ngem \"sidekiq\", \"~> 7.3.0\"\ngem \"sqlite3\", \"~> 1.4\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.2.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 7.2.0\"\ngem \"sidekiq\", \"~> 7.2.4\"\ngem \"sqlite3\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_8.0.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 8.0.0\"\ngem \"sidekiq\", \"~> 7.2.4\"\ngem \"sqlite3\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_8.1.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"appraisal\"\ngem \"puma\"\ngem \"active_model_serializers\"\ngem \"amazing_print\"\ngem \"minitest\"\ngem \"minitest-rails\"\ngem \"rake\"\ngem \"sprockets\", \"< 4.0\"\ngem \"rails\", \"~> 8.1.1\"\ngem \"sidekiq\", \"~> 7.2.4\"\ngem \"sqlite3\"\ngem \"rubocop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "lib/rails_semantic_logger/action_controller/log_subscriber.rb",
    "content": "module RailsSemanticLogger\n  module ActionController\n    class LogSubscriber < ActiveSupport::LogSubscriber\n      INTERNAL_PARAMS = %w[controller action format _method only_path].freeze\n\n      class << self\n        attr_accessor :action_message_format\n      end\n\n      # Log as debug to hide Processing messages in production\n      def start_processing(event)\n        controller_logger(event).debug { action_message(\"Processing\", event.payload) }\n      end\n\n      def process_action(event)\n        controller_logger(event).info do\n          payload = event.payload.dup\n\n          # Unused, but needed for Devise 401 status code monkey patch to still work.\n          ::ActionController::Base.log_process_action(payload)\n\n          params = payload[:params]\n\n          if params.is_a?(Hash) || params.is_a?(::ActionController::Parameters)\n            # According to PR https://github.com/reidmorrison/rails_semantic_logger/pull/37/files\n            # params is not always a Hash.\n            payload[:params] = params.to_unsafe_h unless params.is_a?(Hash)\n            payload[:params] = params.except(*INTERNAL_PARAMS)\n\n            if payload[:params].empty?\n              payload.delete(:params)\n            elsif params[\"file\"]\n              # When logging to JSON the entire tempfile is logged, so convert it to a string.\n              payload[:params][\"file\"] = params[\"file\"].inspect\n            end\n          end\n\n          format           = payload[:format]\n          payload[:format] = format.to_s.upcase if format.is_a?(Symbol)\n\n          payload[:path]   = extract_path(payload[:path]) if payload.key?(:path)\n\n          exception = payload.delete(:exception)\n          if payload[:status].nil? && exception.present?\n            exception_class_name = exception.first\n            payload[:status]     = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)\n          end\n\n          # Rounds off the runtimes. For example, :view_runtime, :mongo_runtime, etc.\n          payload.keys.each do |key|\n            payload[key] = payload[key].to_f.round(2) if key.to_s =~ /(.*)_runtime/\n          end\n\n          # Rails 6+ includes allocation count\n          payload[:allocations] = event.allocations if event.respond_to?(:allocations)\n\n          payload[:status_message] = ::Rack::Utils::HTTP_STATUS_CODES[payload[:status]] if payload[:status].present?\n\n          # Causes excessive log output with Rails 5 RC1\n          payload.delete(:headers)\n          # Causes recursion in Rails 6.1.rc1\n          payload.delete(:request)\n          payload.delete(:response)\n\n          {\n            message:  action_message(\"Completed\", event.payload),\n            duration: event.duration,\n            payload:  payload\n          }\n        end\n      end\n\n      def halted_callback(event)\n        controller_logger(event).info { \"Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected\" }\n      end\n\n      def send_file(event)\n        controller_logger(event).info(message: \"Sent file\", payload: {path: event.payload[:path]}, duration: event.duration)\n      end\n\n      def redirect_to(event)\n        controller_logger(event).info(message: \"Redirected to\", payload: {location: event.payload[:location]})\n      end\n\n      def send_data(event)\n        controller_logger(event).info(message:  \"Sent data\",\n                                      payload:  {file_name: event.payload[:filename]},\n                                      duration: event.duration)\n      end\n\n      def unpermitted_parameters(event)\n        controller_logger(event).debug do\n          unpermitted_keys = event.payload[:keys]\n          \"Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(', ')}\"\n        end\n      end\n\n      %w[write_fragment read_fragment exist_fragment?\n         expire_fragment expire_page write_page].each do |method|\n        class_eval <<-METHOD, __FILE__, __LINE__ + 1\n          def #{method}(event)\n            # enable_fragment_cache_logging as of Rails 5\n            return if ::ActionController::Base.respond_to?(:enable_fragment_cache_logging) && !::ActionController::Base.enable_fragment_cache_logging\n            controller_logger(event).info do\n              key_or_path = event.payload[:key] || event.payload[:path]\n              {message: \"#{method.to_s.humanize} \\#{key_or_path}\", duration: event.duration}\n            end\n          end\n        METHOD\n      end\n\n      private\n\n      # Returns the logger for the supplied event.\n      # Returns ActionController::Base.logger if no controller is present\n      def controller_logger(event)\n        controller = event.payload[:controller]\n        return ::ActionController::Base.logger unless controller\n\n        controller.constantize.logger || ::ActionController::Base.logger\n      rescue NameError\n        ::ActionController::Base.logger\n      end\n\n      def extract_path(path)\n        index = path.index(\"?\")\n        index ? path[0, index] : path\n      end\n\n      def action_message(message, payload)\n        if self.class.action_message_format\n          self.class.action_message_format.call(message, payload)\n        else\n          \"#{message} ##{payload[:action]}\"\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/action_mailer/log_subscriber.rb",
    "content": "require \"active_support/log_subscriber\"\nrequire \"action_mailer\"\n\nmodule RailsSemanticLogger\n  module ActionMailer\n    class LogSubscriber < ::ActiveSupport::LogSubscriber\n      def deliver(event)\n        ex = event.payload[:exception_object]\n        message_id = event.payload[:message_id]\n        duration = event.duration.round(1)\n        if ex\n          log_with_formatter event: event, log_duration: true, level: :error do |_fmt|\n            {\n              message:   \"Error delivering mail #{message_id} (#{duration}ms)\",\n              exception: ex\n            }\n          end\n        else\n          message =\n            if event.payload[:perform_deliveries]\n              \"Delivered mail #{message_id} (#{duration}ms)\"\n            else\n              \"Skipped delivery of mail #{message_id} as `perform_deliveries` is false\"\n            end\n\n          log_with_formatter event: event, log_duration: true do |_fmt|\n            {message: message}\n          end\n        end\n      end\n\n      # An email was generated.\n      def process(event)\n        mailer   = event.payload[:mailer]\n        action   = event.payload[:action]\n        duration = event.duration.round(1)\n        log_with_formatter event: event do |_fmt|\n          {message: \"#{mailer}##{action}: processed outbound mail in #{duration}ms\"}\n        end\n      end\n\n      private\n\n      class EventFormatter\n        def initialize(event:, log_duration: false)\n          @event = event\n          @log_duration = log_duration\n        end\n\n        def mailer\n          event.payload[:mailer]\n        end\n\n        def payload\n          {}.tap do |h|\n            h[:event_name]         = event.name\n            h[:mailer]             = mailer\n            h[:action]             = action\n            h[:message_id]         = event.payload[:message_id]\n            h[:perform_deliveries] = event.payload[:perform_deliveries]\n            h[:subject]            = event.payload[:subject]\n            h[:to]                 = event.payload[:to]\n            h[:from]               = event.payload[:from]\n            h[:bcc]                = event.payload[:bcc]\n            h[:cc]                 = event.payload[:cc]\n            h[:date]               = date\n            h[:duration]           = event.duration.round(2) if log_duration?\n            h[:args]               = formatted_args\n          end\n        end\n\n        def date\n          if event.payload[:date].respond_to?(:to_time)\n            event.payload[:date].to_time.utc\n          elsif event.payload[:date].is_a?(String)\n            Time.parse(date).utc\n          end\n        end\n\n        private\n\n        attr_reader :event\n\n        def mailer\n          event.payload[:mailer]\n        end\n\n        def action\n          event.payload[:action]\n        end\n\n        def formatted_args\n          if defined?(mailer.constantize.log_arguments?) && !mailer.constantize.log_arguments?\n            \"\"\n          elsif event.payload[:args].present?\n            JSON.pretty_generate(event.payload[:args].map { |arg| format(arg) })\n          end\n        end\n\n        def format(arg)\n          case arg\n          when Hash\n            arg.transform_values { |value| format(value) }\n          when Array\n            arg.map { |value| format(value) }\n          when GlobalID::Identification\n            begin\n              arg.to_global_id\n            rescue StandardError\n              arg\n            end\n          else\n            arg\n          end\n        end\n\n        def log_duration?\n          @log_duration\n        end\n      end\n\n      def log_with_formatter(level: :info, **kw_args)\n        fmt = EventFormatter.new(**kw_args)\n        msg = yield fmt\n        logger.public_send(level, **msg, payload: fmt.payload)\n      end\n\n      def logger\n        ::ActionMailer::Base.logger\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/action_view/log_subscriber.rb",
    "content": "require \"active_support/log_subscriber\"\n\nmodule RailsSemanticLogger\n  module ActionView\n    # Output Semantic logs from Action View.\n    class LogSubscriber < ActiveSupport::LogSubscriber\n      VIEWS_PATTERN = %r{^app/views/}.freeze\n\n      class << self\n        attr_reader :logger\n        attr_accessor :rendered_log_level\n      end\n\n      def initialize\n        @rails_root = nil\n        super\n      end\n\n      def render_template(event)\n        return unless should_log?\n\n        payload = {\n          template: from_rails_root(event.payload[:identifier])\n        }\n        payload[:within]      = from_rails_root(event.payload[:layout]) if event.payload[:layout]\n        payload[:allocations] = event.allocations if event.respond_to?(:allocations)\n\n        logger.measure(\n          self.class.rendered_log_level,\n          \"Rendered\",\n          payload:  payload,\n          duration: event.duration\n        )\n      end\n\n      def render_partial(event)\n        return unless should_log?\n\n        payload = {\n          partial: from_rails_root(event.payload[:identifier])\n        }\n        payload[:within]      = from_rails_root(event.payload[:layout]) if event.payload[:layout]\n        payload[:cache]       = event.payload[:cache_hit] unless event.payload[:cache_hit].nil?\n        payload[:allocations] = event.allocations if event.respond_to?(:allocations)\n\n        logger.measure(\n          self.class.rendered_log_level,\n          \"Rendered\",\n          payload:  payload,\n          duration: event.duration\n        )\n      end\n\n      def render_collection(event)\n        return unless should_log?\n\n        identifier = event.payload[:identifier] || \"templates\"\n\n        payload = {\n          template: from_rails_root(identifier),\n          count:    event.payload[:count]\n        }\n        payload[:cache_hits]  = event.payload[:cache_hits] if event.payload[:cache_hits]\n        payload[:allocations] = event.allocations if event.respond_to?(:allocations)\n\n        logger.measure(\n          self.class.rendered_log_level,\n          \"Rendered\",\n          payload:  payload,\n          duration: event.duration\n        )\n      end\n\n      def start(name, id, payload)\n        if [\"render_template.action_view\", \"render_layout.action_view\"].include?(name) && should_log?\n          qualifier        = \" layout\" if name == \"render_layout.action_view\"\n          payload          = {template: from_rails_root(payload[:identifier])}\n          payload[:within] = from_rails_root(payload[:layout]) if payload[:layout]\n\n          logger.send(self.class.rendered_log_level, message: \"Rendering#{qualifier}\", payload: payload)\n        end\n\n        super\n      end\n\n      if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7\n        class Start\n          def start(name, _id, payload)\n            return unless %w[render_template.action_view render_layout.action_view].include?(name)\n\n            qualifier        = \" layout\" if name == \"render_layout.action_view\"\n            payload          = {template: from_rails_root(payload[:identifier])}\n            payload[:within] = from_rails_root(payload[:layout]) if payload[:layout]\n\n            logger.debug(message: \"Rendering#{qualifier}\", payload: payload)\n          end\n\n          def finish(name, id, payload)\n          end\n\n          private\n\n          def from_rails_root(string)\n            string = string.sub(rails_root, \"\")\n            string.sub!(VIEWS_PATTERN, \"\")\n            string\n          end\n\n          def rails_root\n            @root ||= \"#{Rails.root}/\"\n          end\n\n          def logger\n            @logger ||= SemanticLogger[\"ActionView\"]\n          end\n        end\n\n        def self.attach_to(*)\n          ActiveSupport::Notifications.unsubscribe(\"render_template.action_view\")\n          ActiveSupport::Notifications.unsubscribe(\"render_layout.action_view\")\n          ActiveSupport::Notifications.subscribe(\"render_template.action_view\",\n                                                 RailsSemanticLogger::ActionView::LogSubscriber::Start.new)\n          ActiveSupport::Notifications.subscribe(\"render_layout.action_view\",\n                                                 RailsSemanticLogger::ActionView::LogSubscriber::Start.new)\n\n          super\n        end\n      end\n\n      private\n\n      @logger             = SemanticLogger[\"ActionView\"]\n      @rendered_log_level = :debug\n\n      EMPTY = \"\".freeze\n\n      def should_log?\n        logger.send(\"#{self.class.rendered_log_level}?\")\n      end\n\n      def from_rails_root(string)\n        string = string.sub(rails_root, EMPTY)\n        string.sub!(VIEWS_PATTERN, EMPTY)\n        string\n      end\n\n      def rails_root\n        @rails_root ||= \"#{Rails.root}/\"\n      end\n\n      def logger\n        self.class.logger\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/active_job/log_subscriber.rb",
    "content": "require \"active_job\"\n\nmodule RailsSemanticLogger\n  module ActiveJob\n    class LogSubscriber < ::ActiveSupport::LogSubscriber\n      def enqueue(event)\n        ex = event.payload[:exception_object]\n\n        if ex\n          log_with_formatter level: :error, event: event do |fmt|\n            {\n              message:   \"Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})\",\n              exception: ex\n            }\n          end\n        elsif event.payload[:aborted]\n          log_with_formatter level: :info, event: event do |fmt|\n            {message: \"Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution.\"}\n          end\n        else\n          log_with_formatter event: event do |fmt|\n            {message: \"Enqueued #{fmt.job_info}\"}\n          end\n        end\n      end\n\n      def enqueue_at(event)\n        ex = event.payload[:exception_object]\n\n        if ex\n          log_with_formatter level: :error, event: event do |fmt|\n            {\n              message:   \"Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})\",\n              exception: ex\n            }\n          end\n        elsif event.payload[:aborted]\n          log_with_formatter level: :info, event: event do |fmt|\n            {message: \"Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution.\"}\n          end\n        else\n          log_with_formatter event: event do |fmt|\n            {message: \"Enqueued #{fmt.job_info} at #{fmt.scheduled_at}\"}\n          end\n        end\n      end\n\n      def perform_start(event)\n        log_with_formatter event: event do |fmt|\n          {message: \"Performing #{fmt.job_info}\"}\n        end\n      end\n\n      def perform(event)\n        ex = event.payload[:exception_object]\n        if ex\n          log_with_formatter event: event, log_duration: true, level: :error do |fmt|\n            {\n              message:   \"Error performing #{fmt.job_info} in #{event.duration.round(2)}ms\",\n              exception: ex\n            }\n          end\n        else\n          log_with_formatter event: event, log_duration: true do |fmt|\n            {message: \"Performed #{fmt.job_info} in #{event.duration.round(2)}ms\"}\n          end\n        end\n      end\n\n      private\n\n      class EventFormatter\n        def initialize(event:, log_duration: false)\n          @event = event\n          @log_duration = log_duration\n        end\n\n        def job_info\n          \"#{job.class.name} (Job ID: #{job.job_id}) to #{queue_name}\"\n        end\n\n        def payload\n          {}.tap do |h|\n            h[:event_name]      = event.name\n            h[:adapter]         = adapter_name\n            h[:queue]           = job.queue_name\n            h[:job_class]       = job.class.name\n            h[:job_id]          = job.job_id\n            h[:provider_job_id] = job.try(:provider_job_id) # Not available in Rails 4.2\n            h[:duration]        = event.duration.round(2) if log_duration?\n            h[:arguments]       = formatted_args\n          end\n        end\n\n        def queue_name\n          adapter_name + \"(#{job.queue_name})\"\n        end\n\n        def scheduled_at\n          Time.at(event.payload[:job].scheduled_at).utc\n        end\n\n        private\n\n        attr_reader :event\n\n        def job\n          event.payload[:job]\n        end\n\n        def adapter_name\n          event.payload[:adapter].class.name.demodulize.remove(\"Adapter\")\n        end\n\n        def formatted_args\n          if defined?(job.class.log_arguments?) && !job.class.log_arguments?\n            \"\"\n          else\n            JSON.pretty_generate(job.arguments.map { |arg| format(arg) })\n          end\n        end\n\n        def format(arg)\n          case arg\n          when String\n            arg.encode(\"UTF-8\", invalid: :replace, undef: :replace)\n          when Hash\n            arg.transform_values { |value| format(value) }\n          when Array\n            arg.map { |value| format(value) }\n          when GlobalID::Identification\n            begin\n              arg.to_global_id\n            rescue StandardError\n              arg\n            end\n          else\n            arg\n          end\n        end\n\n        def log_duration?\n          @log_duration\n        end\n      end\n\n      def log_with_formatter(level: :info, **kw_args)\n        fmt = EventFormatter.new(**kw_args)\n        msg = yield fmt\n        logger.public_send(level, **msg, payload: fmt.payload)\n      end\n\n      def logger\n        ::ActiveJob::Base.logger\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/active_record/log_subscriber.rb",
    "content": "module RailsSemanticLogger\n  module ActiveRecord\n    class LogSubscriber < ActiveSupport::LogSubscriber\n      IGNORE_PAYLOAD_NAMES = %w[SCHEMA EXPLAIN].freeze\n\n      class << self\n        attr_reader :logger\n      end\n\n      # Rails 7.1 stopped using runtime in log subscribers\n      if Rails.version.to_f < 7.1\n        def self.runtime=(value)\n          ::ActiveRecord::RuntimeRegistry.sql_runtime = value\n        end\n\n        def self.runtime\n          ::ActiveRecord::RuntimeRegistry.sql_runtime ||= 0\n        end\n\n        def self.reset_runtime\n          rt           = runtime\n          self.runtime = 0\n          rt\n        end\n      end\n\n      def sql(event)\n        self.class.runtime += event.duration if self.class.respond_to?(:runtime)\n        return unless logger.debug?\n\n        payload = event.payload\n        name    = payload[:name]\n        return if IGNORE_PAYLOAD_NAMES.include?(name)\n\n        log_payload         = {sql: payload[:sql]}\n        log_payload[:binds] = bind_values(payload) unless (payload[:binds] || []).empty?\n        log_payload[:allocations] = event.allocations if event.respond_to?(:allocations)\n        log_payload[:cached] = event.payload[:cached]\n        log_payload[:async] = true if event.payload[:async]\n\n        log = {\n          message:  name,\n          payload:  log_payload,\n          duration: event.duration\n        }\n\n        # Log the location of the query itself.\n        if logger.send(:level_index) >= SemanticLogger.backtrace_level_index\n          log[:backtrace] = SemanticLogger::Utils.strip_backtrace(caller)\n        end\n\n        logger.debug(log)\n      end\n\n      private\n\n      @logger = SemanticLogger[\"ActiveRecord\"]\n\n      # When multiple values are received for a single bound field, it is converted into an array\n      def add_bind_value(binds, key, value)\n        key = key.downcase.to_sym unless key.nil?\n\n        if rails_filter_params_include?(key)\n          value = \"[FILTERED]\"\n        elsif binds.key?(key)\n          value = (Array(binds[key]) << value)\n        end\n\n        binds[key] = value\n      end\n\n      def rails_filter_params_include?(key)\n        filter_parameters = Rails.configuration.filter_parameters\n\n        return filter_parameters.first.match? key if filter_parameters.first.is_a? Regexp\n\n        filter_parameters.include? key\n      end\n\n      def logger\n        self.class.logger\n      end\n\n      #\n      # Rails 3,4,5 hell trying to get the bind values\n      #\n\n      def bind_values_v3(payload)\n        binds = {}\n        payload[:binds].each do |col, v|\n          if col\n            add_bind_value(binds, col.name, v)\n          else\n            binds[nil] = v\n          end\n        end\n        binds\n      end\n\n      def bind_values_v4(payload)\n        binds = {}\n        payload[:binds].each do |col, v|\n          attr_name, value = render_bind(col, v)\n          add_bind_value(binds, attr_name, value)\n        end\n        binds\n      end\n\n      def bind_values_v5_0_0(payload)\n        binds = {}\n        payload[:binds].each do |attr|\n          attr_name, value = render_bind(attr)\n          add_bind_value(binds, attr_name, value)\n        end\n        binds\n      end\n\n      def bind_values_v5_0_3(payload)\n        binds         = {}\n        casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])\n        payload[:binds].zip(casted_params).map do |attr, value|\n          attr_name, value = render_bind(attr, value)\n          add_bind_value(binds, attr_name, value)\n        end\n        binds\n      end\n\n      def bind_values_v5_1_5(payload)\n        binds         = {}\n        casted_params = type_casted_binds(payload[:type_casted_binds])\n        payload[:binds].zip(casted_params).map do |attr, value|\n          attr_name, value = render_bind(attr, value)\n          add_bind_value(binds, attr_name, value)\n        end\n        binds\n      end\n\n      def bind_values_v6_1(payload)\n        binds         = {}\n        casted_params = type_casted_binds(payload[:type_casted_binds])\n        payload[:binds].each_with_index do |attr, i|\n          attr_name, value = render_bind(attr, casted_params[i])\n          add_bind_value(binds, attr_name, value)\n        end\n        binds\n      end\n\n      def render_bind_v4_2(column, value)\n        if column\n          if column.binary?\n            # This specifically deals with the PG adapter that casts bytea columns into a Hash.\n            value = value[:value] if value.is_a?(Hash)\n            value = value ? \"<#{value.bytesize} bytes of binary data>\" : \"<NULL binary data>\"\n          end\n\n          [column.name, value]\n        else\n          [nil, value]\n        end\n      end\n\n      def render_bind_v5_0_0(attribute)\n        value =\n          if attribute.type.binary? && attribute.value\n            if attribute.value.is_a?(Hash)\n              \"<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>\"\n            else\n              \"<#{attribute.value.bytesize} bytes of binary data>\"\n            end\n          else\n            attribute.value_for_database\n          end\n\n        [attribute.name, value]\n      end\n\n      def render_bind_v5_0_3(attr, value)\n        if attr.is_a?(Array)\n          attr = attr.first\n        elsif attr.type.binary? && attr.value\n          value = \"<#{attr.value_for_database.to_s.bytesize} bytes of binary data>\"\n        end\n\n        [attr&.name, value]\n      end\n\n      def render_bind_v6_1(attr, value)\n        case attr\n        when ActiveModel::Attribute\n          value = \"<#{attr.value_for_database.to_s.bytesize} bytes of binary data>\" if attr.type.binary? && attr.value\n        when Array\n          attr = attr.first\n        else\n          attr = nil\n        end\n\n        [attr&.name || :nil, value]\n      end\n\n      def type_casted_binds_v5_0_3(binds, casted_binds)\n        casted_binds || ::ActiveRecord::Base.connection.type_casted_binds(binds)\n      end\n\n      def type_casted_binds_v5_1_5(casted_binds)\n        casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds\n      end\n\n      if Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR.zero? && Rails::VERSION::TINY <= 2 # 5.0.0 - 5.0.2\n        alias bind_values bind_values_v5_0_0\n        alias render_bind render_bind_v5_0_0\n      elsif Rails::VERSION::MAJOR == 5 &&\n            ((Rails::VERSION::MINOR.zero? && Rails::VERSION::TINY <= 6) ||\n              (Rails::VERSION::MINOR == 1 && Rails::VERSION::TINY <= 4)) # 5.0.3 - 5.0.6 && 5.1.0 - 5.1.4\n        alias bind_values bind_values_v5_0_3\n        alias render_bind render_bind_v5_0_3\n        alias type_casted_binds type_casted_binds_v5_0_3\n      elsif (Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR > 0) ||\n            Rails::VERSION::MAJOR >= 7 # ~> 6.1.0 && >= 7.x.x\n        alias bind_values bind_values_v6_1\n        alias render_bind render_bind_v6_1\n        alias type_casted_binds type_casted_binds_v5_1_5\n      elsif Rails::VERSION::MAJOR >= 5 # ~> 5.1.5 && ~> 5.0.7 && 6.x.x\n        alias bind_values bind_values_v5_1_5\n        alias render_bind render_bind_v5_0_3\n        alias type_casted_binds type_casted_binds_v5_1_5\n      elsif Rails.version.to_i >= 4 # 4.x\n        alias bind_values bind_values_v4\n        alias render_bind render_bind_v4_2\n      else # 3.x\n        alias bind_values bind_values_v3\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/delayed_job/plugin.rb",
    "content": "module RailsSemanticLogger\n  module DelayedJob\n    class Plugin < Delayed::Plugin\n      callbacks do |lifecycle|\n        lifecycle.before(:execute) do |_job|\n          ::SemanticLogger.reopen\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/engine.rb",
    "content": "require \"rails\"\nrequire \"rails_semantic_logger/options\"\n\nmodule RailsSemanticLogger\n  class Engine < ::Rails::Engine\n    # Make the SemanticLogger config available in the Rails application config\n    #\n    # Example: Add the MongoDB logging appender in the Rails environment\n    #          initializer in file config/environments/development.rb\n    #\n    #   Rails::Application.configure do\n    #     # Add the MongoDB logger appender only once Rails is initialized\n    #     config.after_initialize do\n    #       appender = SemanticLogger::Appender::Mongo.new(\n    #         uri: 'mongodb://127.0.0.1:27017/test'\n    #       )\n    #       config.semantic_logger.add_appender(appender: appender)\n    #     end\n    #   end\n    config.semantic_logger = ::SemanticLogger\n\n    config.rails_semantic_logger = RailsSemanticLogger::Options.new\n\n    # Initialize SemanticLogger. In a Rails environment it will automatically\n    # insert itself above the configured rails logger to add support for its\n    # additional features\n\n    # Replace Rails logger initializer\n    Rails::Application::Bootstrap.initializers.delete_if { |i| i.name == :initialize_logger }\n\n    initializer :initialize_logger, group: :all do\n      config = Rails.application.config\n\n      # Set the default log level based on the Rails config\n      SemanticLogger.default_level = config.log_level\n\n      if defined?(Rails::Rack::Logger) && config.rails_semantic_logger.semantic\n        config.middleware.swap(Rails::Rack::Logger, RailsSemanticLogger::Rack::Logger, config.log_tags)\n      end\n\n      # Existing loggers are ignored because servers like trinidad supply their\n      # own file loggers which would result in duplicate logging to the same log file\n      Rails.logger = config.logger =\n        begin\n          if config.rails_semantic_logger.add_file_appender\n            path = config.paths[\"log\"].first\n            FileUtils.mkdir_p(File.dirname(path)) unless File.exist?(File.dirname(path))\n\n            # Add the log file to the list of appenders\n            # Use the colorized formatter if Rails colorized logs are enabled\n            ap_options = config.rails_semantic_logger.ap_options\n            formatter  = config.rails_semantic_logger.format\n            formatter  = {color: {ap: ap_options}} if (formatter == :default) && (config.colorize_logging != false)\n\n            # Set internal logger to log to file only, in case another appender experiences errors during writes\n            appender                         = SemanticLogger::Appender::File.new(path, formatter: formatter)\n            appender.name                    = \"SemanticLogger\"\n            SemanticLogger::Processor.logger = appender\n\n            # Check for previous file or stdout loggers\n            SemanticLogger.appenders.each do |app|\n              next unless app.is_a?(SemanticLogger::Appender::File) || app.is_a?(SemanticLogger::Appender::IO)\n\n              app.formatter = formatter\n            end\n            SemanticLogger.add_appender(file_name: path, formatter: formatter, filter: config.rails_semantic_logger.filter)\n          end\n\n          SemanticLogger[Rails]\n        rescue StandardError => e\n          # If not able to log to file, log to standard error with warning level only\n          SemanticLogger.default_level = :warn\n\n          SemanticLogger::Processor.logger = SemanticLogger::Appender::IO.new($stderr)\n          SemanticLogger.add_appender(io: $stderr)\n\n          logger = SemanticLogger[Rails]\n          logger.warn(\n            \"Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. \" \\\n            \"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed.\",\n            e\n          )\n          logger\n        end\n\n      # Replace Rails loggers\n      %i[active_record action_controller action_mailer action_view].each do |name|\n        ActiveSupport.on_load(name) { include SemanticLogger::Loggable }\n      end\n      ActiveSupport.on_load(:action_cable) { self.logger = SemanticLogger[\"ActionCable\"] }\n    end\n\n    # Before any initializers run, but after the gems have been loaded\n    config.before_initialize do\n      if config.respond_to?(:assets) && defined?(Rails::Rack::Logger) && config.rails_semantic_logger.semantic\n        config.rails_semantic_logger.quiet_assets = true if config.assets.quiet\n\n        # Otherwise Sprockets can't find the Rails::Rack::Logger middleware\n        config.assets.quiet = false\n      end\n\n      # Replace the Mongo Loggers\n      Mongoid.logger       = SemanticLogger[Mongoid] if defined?(Mongoid)\n      Moped.logger         = SemanticLogger[Moped] if defined?(Moped)\n      Mongo::Logger.logger = SemanticLogger[Mongo] if defined?(Mongo::Logger)\n\n      # Replace the Resque Logger\n      Resque.logger        = SemanticLogger[Resque] if defined?(Resque) && Resque.respond_to?(:logger=)\n\n      # Replace the Sidekiq logger\n      if config.rails_semantic_logger.replace_sidekiq_logger && defined?(::Sidekiq)\n        ::Sidekiq.configure_client do |config|\n          config.logger = ::SemanticLogger[::Sidekiq]\n        end\n\n        ::Sidekiq.configure_server do |config|\n          config.logger = ::SemanticLogger[::Sidekiq]\n          if config.respond_to?(:options)\n            config.options[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger\n          else\n            config[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger\n          end\n\n          # Add back the default console logger unless already added\n          SemanticLogger.add_appender(io: $stdout, formatter: :color) unless SemanticLogger.appenders.console_output?\n\n          # Replace default error handler when present\n          existing = RailsSemanticLogger::Sidekiq::Defaults.delete_default_error_handler(config.error_handlers)\n          config.error_handlers << RailsSemanticLogger::Sidekiq::Defaults::ERROR_HANDLER if existing\n        end\n\n        if defined?(::Sidekiq::Job) && (::Sidekiq::VERSION.to_i != 5)\n          ::Sidekiq::Job.singleton_class.prepend(RailsSemanticLogger::Sidekiq::Loggable)\n        else\n          ::Sidekiq::Worker.singleton_class.prepend(RailsSemanticLogger::Sidekiq::Loggable)\n        end\n      end\n\n      # Replace the Sidetiq logger\n      Sidetiq.logger = SemanticLogger[Sidetiq] if defined?(Sidetiq) && Sidetiq.respond_to?(:logger=)\n\n      # Replace the DelayedJob logger\n      if defined?(Delayed::Worker)\n        Delayed::Worker.logger = SemanticLogger[Delayed::Worker]\n        Delayed::Worker.plugins << RailsSemanticLogger::DelayedJob::Plugin\n      end\n\n      # Replace the Bugsnag logger\n      Bugsnag.configure(false) { |config| config.logger = SemanticLogger[Bugsnag] } if defined?(Bugsnag)\n\n      # Set the IOStreams PGP logger\n      IOStreams::Pgp.logger = SemanticLogger[\"IOStreams::Pgp\"] if defined?(IOStreams)\n    end\n\n    # After any initializers run, but after the gems have been loaded\n    config.after_initialize do\n      config = Rails.application.config\n\n      # Replace the Bugsnag logger\n      Bugsnag.configure(false) { |bugsnag_config| bugsnag_config.logger = SemanticLogger[Bugsnag] } if defined?(Bugsnag)\n\n      # Rails Patches\n      require(\"rails_semantic_logger/extensions/action_cable/tagged_logger_proxy\") if defined?(::ActionCable)\n      require(\"rails_semantic_logger/extensions/action_controller/live\") if defined?(::ActionController::Live)\n      if defined?(::ActionDispatch::DebugExceptions)\n        require(\"rails_semantic_logger/extensions/action_dispatch/debug_exceptions\")\n      end\n      if defined?(::ActionView::StreamingTemplateRenderer::Body)\n        require(\"rails_semantic_logger/extensions/action_view/streaming_template_renderer\")\n      end\n      require(\"rails_semantic_logger/extensions/active_job/logging\") if defined?(::ActiveJob)\n      require(\"rails_semantic_logger/extensions/active_model_serializers/logging\") if defined?(::ActiveModelSerializers)\n\n      if config.rails_semantic_logger.semantic\n        # Active Job\n        if defined?(::ActiveJob::Logging::LogSubscriber)\n          RailsSemanticLogger.swap_subscriber(\n            ::ActiveJob::Logging::LogSubscriber,\n            RailsSemanticLogger::ActiveJob::LogSubscriber,\n            :active_job\n          )\n        end\n\n        if defined?(::ActiveJob::LogSubscriber)\n          RailsSemanticLogger.swap_subscriber(\n            ::ActiveJob::LogSubscriber,\n            RailsSemanticLogger::ActiveJob::LogSubscriber,\n            :active_job\n          )\n        end\n\n        # Active Record\n        if defined?(::ActiveRecord)\n          require \"active_record/log_subscriber\"\n\n          RailsSemanticLogger.swap_subscriber(\n            ::ActiveRecord::LogSubscriber,\n            RailsSemanticLogger::ActiveRecord::LogSubscriber,\n            :active_record\n          )\n        end\n\n        # Rack\n        RailsSemanticLogger::Rack::Logger.started_request_log_level = :info if config.rails_semantic_logger.started\n\n        # Silence asset logging by applying a filter to the Rails logger itself, not any of the appenders.\n        if config.rails_semantic_logger.quiet_assets && config.respond_to?(:assets) && config.assets.prefix\n          assets_root                                     = config.relative_url_root.to_s + config.assets.prefix\n          assets_regex                                    = %r(\\A/{0,2}#{assets_root})\n          RailsSemanticLogger::Rack::Logger.logger.filter = ->(log) { log.payload[:path] !~ assets_regex if log.payload }\n        end\n\n        # Action View\n        if defined?(::ActionView)\n          require \"action_view/log_subscriber\"\n\n          RailsSemanticLogger::ActionView::LogSubscriber.rendered_log_level = :info if config.rails_semantic_logger.rendered\n          RailsSemanticLogger.swap_subscriber(\n            ::ActionView::LogSubscriber,\n            RailsSemanticLogger::ActionView::LogSubscriber,\n            :action_view\n          )\n        end\n\n        # Action Controller\n        if defined?(::ActionController)\n          require \"action_controller/log_subscriber\"\n\n          RailsSemanticLogger::ActionController::LogSubscriber.action_message_format = config.rails_semantic_logger.action_message_format\n          RailsSemanticLogger.swap_subscriber(\n            ::ActionController::LogSubscriber,\n            RailsSemanticLogger::ActionController::LogSubscriber,\n            :action_controller\n          )\n        end\n\n        # Action Mailer\n        if defined?(::ActionMailer)\n          require \"action_mailer/log_subscriber\"\n\n          RailsSemanticLogger.swap_subscriber(\n            ::ActionMailer::LogSubscriber,\n            RailsSemanticLogger::ActionMailer::LogSubscriber,\n            :action_mailer\n          )\n        end\n\n        if config.rails_semantic_logger.replace_sidekiq_logger && defined?(::Sidekiq)\n          require(\"rails_semantic_logger/extensions/sidekiq/sidekiq\")\n        end\n      end\n\n      #\n      # Forking Frameworks\n      #\n\n      # Passenger provides the :starting_worker_process event for executing\n      # code after it has forked, so we use that and reconnect immediately.\n      if defined?(PhusionPassenger)\n        PhusionPassenger.on_event(:starting_worker_process) do |forked|\n          SemanticLogger.reopen if forked\n        end\n      end\n\n      # Re-open appenders after Resque has forked a worker\n      Resque.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Resque.after_fork)\n\n      # Re-open appenders after Spring has forked a process\n      Spring.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Spring.after_fork)\n\n      # Re-open appenders after SolidQueue worker/dispatcher/scheduler has finished booting\n      SolidQueue.on_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_start)\n      SolidQueue.on_worker_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_worker_start)\n      SolidQueue.on_dispatcher_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_dispatcher_start)\n      SolidQueue.on_scheduler_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_scheduler_start)\n\n      console do |_app|\n        # Don't use a background thread for logging\n        SemanticLogger.sync!\n        # Add a stderr logger when running inside a Rails console unless one has already been added.\n        if config.rails_semantic_logger.console_logger && !SemanticLogger.appenders.console_output?\n          SemanticLogger.add_appender(io: STDERR, formatter: :color)\n        end\n\n        # Include method names on log entries in the console\n        SemanticLogger.backtrace_level = SemanticLogger.default_level\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/action_cable/tagged_logger_proxy.rb",
    "content": "require \"action_cable/connection/tagged_logger_proxy\"\n\nmodule ActionCable\n  module Connection\n    class TaggedLoggerProxy\n      def tag(logger, &block)\n        current_tags = tags - logger.tags\n        logger.tagged(*current_tags, &block)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/action_controller/live.rb",
    "content": "# Log actual exceptions, not a string representation\nrequire \"action_controller\"\n\nmodule ActionController\n  module Live\n    undef_method :log_error\n    def log_error(exception)\n      logger.fatal(exception)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/action_dispatch/debug_exceptions.rb",
    "content": "# Log actual exceptions, not a string representation\nrequire \"action_dispatch\"\n\nmodule ActionDispatch\n  class DebugExceptions\n    private\n\n    undef_method :log_error\n    if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7\n      def log_error(request, wrapper)\n        Rails.application.deprecators.silence do\n          return if !log_rescued_responses?(request) && wrapper.rescue_response?\n\n          level = request.get_header(\"action_dispatch.debug_exception_log_level\")\n          ActionController::Base.logger.log(level, wrapper.exception)\n        end\n      end\n    else\n      def log_error(_request, wrapper)\n        ActiveSupport::Deprecation.silence do\n          level = wrapper.respond_to?(:rescue_response?) && wrapper.rescue_response? ? :debug : :fatal\n          ActionController::Base.logger.log(level, wrapper.exception)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/action_view/streaming_template_renderer.rb",
    "content": "# Log actual exceptions, not a string representation\nrequire \"action_view/renderer/streaming_template_renderer\"\n\nmodule ActionView\n  class StreamingTemplateRenderer\n    class Body\n      private\n\n      undef_method :log_error\n      def log_error(exception)\n        ActionView::Base.logger.fatal(exception)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/active_job/logging.rb",
    "content": "# Patch ActiveJob logger\nrequire \"active_job/logging\"\n\nmodule ActiveJob\n  module Logging\n    include SemanticLogger::Loggable\n\n    private\n\n    undef_method :tag_logger\n    def tag_logger(*tags, &block)\n      if logger.respond_to?(:tagged)\n        logger.tagged(*tags, &block)\n      else\n        yield\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/active_model_serializers/logging.rb",
    "content": "# Patch ActiveModelSerializers logger\nrequire \"active_model_serializers/logging\"\n\nmodule ActiveModelSerializers\n  module Logging\n    include SemanticLogger::Loggable\n\n    private\n\n    def tag_logger(*tags, &block)\n      logger.tagged(*tags, &block)\n    end\n  end\n\n  class SerializableResource\n    include SemanticLogger::Loggable\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/active_support/log_subscriber.rb",
    "content": "if ActiveSupport::VERSION::STRING == \"7.1.1\"\n  require \"active_support/log_subscriber\"\n\n  module ActiveSupport\n    class LogSubscriber\n      # @override Rails 7.1\n      def silenced?(event)\n        native_log_level = @event_levels.fetch(event, ::Logger::Severity::FATAL)\n        logger.nil? || SemanticLogger::Levels.index(logger.level) > SemanticLogger::Levels.index(native_log_level)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/active_support/logger.rb",
    "content": "require \"active_support/logger\"\n\nmodule ActiveSupport\n  # More hacks to try and stop Rails from being it's own worst enemy.\n  class Logger\n    class << self\n      undef :logger_outputs_to?\n\n      # Prevent broadcasting since SemanticLogger already supports multiple loggers\n      if method_defined?(:broadcast)\n        undef :broadcast\n        def broadcast(_logger)\n          Module.new\n        end\n      end\n    end\n\n    # Prevent Console from trying to merge loggers\n    def self.logger_outputs_to?(*_args)\n      true\n    end\n\n    def self.new(*_args, **_kwargs)\n      SemanticLogger[self]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/active_support/tagged_logging.rb",
    "content": "module ActiveSupport\n  module TaggedLogging\n    # Semantic Logger already does tagged logging\n    def self.new(logger)\n      logger\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/mongoid/config.rb",
    "content": "require \"mongoid/config\"\n\nmodule Mongoid\n  module Config\n    private\n\n    # Remove log overrides\n    def set_log_levels\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/rack/server.rb",
    "content": "module RailsSemanticLogger\n  module Rack\n    module Server\n      def daemonize_app\n        super\n        SemanticLogger.reopen\n      end\n    end\n  end\nend\n\nRack::Server.prepend(RailsSemanticLogger::Rack::Server)\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/rackup/server.rb",
    "content": "module RailsSemanticLogger\n  module Rackup\n    module Server\n      def daemonize_app\n        super\n        SemanticLogger.reopen\n      end\n    end\n  end\nend\n\nRackup::Server.prepend(RailsSemanticLogger::Rackup::Server)\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/rails/server.rb",
    "content": "# Patch the Rails::Server log_to_stdout so that it logs via SemanticLogger\nrequire \"rails\"\n\nmodule Rails\n  class Server\n    private\n\n    undef_method :log_to_stdout if method_defined?(:log_to_stdout)\n    def log_to_stdout\n      wrapped_app # touch the app so the logger is set up\n\n      SemanticLogger.add_appender(io: $stdout, formatter: :color) unless SemanticLogger.appenders.console_output?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb",
    "content": "# Sidekiq patches\nif Sidekiq::VERSION.to_i == 4\n  require \"sidekiq/logging\"\n  require \"sidekiq/middleware/server/logging\"\n  require \"sidekiq/processor\"\nelsif Sidekiq::VERSION.to_i == 5\n  require \"sidekiq/logging\"\nend\n\nmodule Sidekiq\n  # Sidekiq v4 & v5\n  if defined?(::Sidekiq::Logging)\n    # Replace Sidekiq logging context\n    module Logging\n      def self.with_context(msg, &block)\n        SemanticLogger.tagged(msg, &block)\n      end\n\n      def self.job_hash_context(job_hash)\n        h         = {jid: job_hash[\"jid\"]}\n        h[:bid]   = job_hash[\"bid\"] if job_hash[\"bid\"]\n        h[:queue] = job_hash[\"queue\"] if job_hash[\"queue\"]\n        h\n      end\n    end\n  end\n\n  # Sidekiq v4\n  if defined?(::Sidekiq::Middleware::Server::Logging)\n    # Convert string to machine readable format\n    class Processor\n      def log_context(job_hash)\n        h         = {jid: job_hash[\"jid\"]}\n        h[:bid]   = job_hash[\"bid\"] if job_hash[\"bid\"]\n        h[:queue] = job_hash[\"queue\"] if job_hash[\"queue\"]\n        h\n      end\n    end\n\n    # Let Semantic Logger handle duration logging\n    module Middleware\n      module Server\n        class Logging\n          # rubocop:disable Style/ExplicitBlockArgument\n          def call(worker, item, queue)\n            SemanticLogger.tagged(queue: queue) do\n              if perform_messages_enabled?\n                worker.logger.info(\n                  \"Start #perform\",\n                  metric:        \"sidekiq.queue.latency\",\n                  metric_amount: job_latency_ms(item)\n                )\n\n                worker.logger.measure_info(\n                  \"Completed #perform\",\n                  on_exception_level: :error,\n                  log_exception:      :full,\n                  metric:             \"sidekiq.job.perform\"\n                ) { yield }\n              else\n                yield\n              end\n            end\n          end\n\n          def perform_messages_enabled?\n            RailsSemanticLogger::Sidekiq::JobLogger.perform_messages != false\n          end\n\n          def job_latency_ms(job)\n            return unless job && job[\"enqueued_at\"]\n\n            enqueued_at = job[\"enqueued_at\"]\n            if enqueued_at.is_a?(Float)\n              # Sidekiq <= 7: seconds since epoch\n              (Time.now.to_f - enqueued_at) * 1000\n            else\n              # Sidekiq 8+: milliseconds since epoch\n              now = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)\n              now - enqueued_at\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/options.rb",
    "content": "module RailsSemanticLogger\n  # Options for controlling Rails Semantic Logger behavior\n  #\n  # * Convert Action Controller and Active Record text messages to semantic data\n  #\n  #     Rails -- Started -- { :ip => \"127.0.0.1\", :method => \"GET\", :path => \"/dashboards/inquiry_recent_activity\" }\n  #     UserController -- Completed #index -- { :action => \"index\", :db_runtime => 54.64, :format => \"HTML\", :method => \"GET\", :mongo_runtime => 0.0, :path => \"/users\", :status => 200, :status_message => \"OK\", :view_runtime => 709.88 }\n  #\n  #     config.rails_semantic_logger.semantic = true\n  #\n  # * Change Rack started message to debug so that it does not appear in production\n  #\n  #     config.rails_semantic_logger.started = false\n  #\n  # * Change Processing message to debug so that it does not appear in production\n  #\n  #     config.rails_semantic_logger.processing = false\n  #\n  # * Change Action View render log messages to debug so that they do not appear in production\n  #\n  #     ActionView::Base --   Rendered data/search/_user.html.haml (46.7ms)\n  #\n  #     config.rails_semantic_logger.rendered = false\n  #\n  # * Override the Amazing Print options for logging Hash data as text:\n  #\n  #     Any valid Amazing Print option for rendering data.\n  #     The defaults can changed be creating a `~/.aprc` file.\n  #     See: https://github.com/amazing-print/amazing_print\n  #\n  #     Note: The option :multiline is set to false if not supplied.\n  #     Note: Has no effect if Amazing Print is not installed.\n  #\n  #        config.rails_semantic_logger.ap_options = {multiline: false}\n  #\n  # * Whether to automatically add an environment specific log file appender.\n  #     For Example: 'log/development.log'\n  #\n  #     Note:\n  #       When Semantic Logger fails to log to an appender it logs the error to an\n  #       internal logger, which by default writes to STDERR.\n  #       Example, change the default internal logger to log to stdout:\n  #         SemanticLogger::Processor.logger = SemanticLogger::Appender::IO.new($stdout, level: :warn)\n  #\n  #       config.rails_semantic_logger.add_file_appender = true\n  #\n  # * Silence asset logging\n  #\n  #     config.rails_semantic_logger.quiet_assets = false\n  #\n  # * Disable automatic logging to stderr when running a Rails console.\n  #\n  #     config.rails_semantic_logger.console_logger = false\n  #\n  # * Override the output format for the primary Rails log file.\n  #\n  #     Valid options:\n  #     * :default\n  #         Plain text output with no color.\n  #     * :color\n  #         Plain text output with color.\n  #     * :json\n  #         JSON output format.\n  #     * class\n  #\n  #     * Proc\n  #         A block that will be called to format the output.\n  #         It is supplied with the `log` entry and should return the formatted data.\n  #\n  #     Note:\n  #     * `:default` is automatically changed to `:color` if `config.colorize_logging` is `true`.\n  #\n  #     JSON Example, in `application.rb`:\n  #        config.rails_semantic_logger.format = :json\n  #\n  #     Custom Example, create `app/lib/my_formatter.rb`:\n  #\n  #       # My Custom colorized formatter\n  #       class MyFormatter < SemanticLogger::Formatters::Color\n  #         # Return the complete log level name in uppercase\n  #         def level\n  #           \"#{color}log.level.upcase#{color_map.clear}\"\n  #         end\n  #       end\n  #\n  #      # In application.rb:\n  #      config.rails_semantic_logger.format = MyFormatter.new\n  #\n  #\n  #      config.rails_semantic_logger.format = :default\n  #\n  # * Add a filter to the file logger [Regexp|Proc]\n  #   RegExp: Only include log messages where the class name matches the supplied\n  #           regular expression. All other messages will be ignored.\n  #   Proc: Only include log messages where the supplied Proc returns true.\n  #         The Proc must return true or false.\n  #\n  #     config.rails_semantic_logger.filter = nil\n  #\n  # * named_tags: *DEPRECATED*\n  #   Instead, supply a Hash to config.log_tags\n  #   config.rails_semantic_logger.named_tags = nil\n  #\n  # * Change the message format of Action Controller action.\n  #   A block that will be called to format the message.\n  #   It is supplied with the `message` and `payload` and should return the formatted data.\n  #\n  #     config.rails_semantic_logger.action_message_format = -> (message, payload) do\n  #       \"#{message} - #{payload[:controller]}##{payload[:action]}\"\n  #     end\n  #\n  # * Do not replace the Sidekiq logger with a Semantic Logger logger.\n  #\n  #     config.rails_semantic_logger.replace_sidekiq_logger = false\n  class Options\n    attr_accessor :semantic, :started, :processing, :rendered, :ap_options, :add_file_appender,\n                  :quiet_assets, :format, :named_tags, :filter, :console_logger, :action_message_format,\n                  :replace_sidekiq_logger\n\n    # Setup default values\n    def initialize\n      @semantic               = true\n      @started                = false\n      @processing             = false\n      @rendered               = false\n      @ap_options             = {multiline: false}\n      @add_file_appender      = true\n      @quiet_assets           = false\n      @format                 = :default\n      @named_tags             = nil\n      @filter                 = nil\n      @console_logger         = true\n      @action_message_format  = nil\n      @replace_sidekiq_logger = true\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/rack/logger.rb",
    "content": "require \"active_support/core_ext/time/conversions\"\nrequire \"active_support/core_ext/object/blank\"\nrequire \"active_support/log_subscriber\"\nrequire \"action_dispatch/http/request\"\nrequire \"rack/body_proxy\"\n\nmodule RailsSemanticLogger\n  module Rack\n    class Logger < ActiveSupport::LogSubscriber\n      class << self\n        attr_reader :logger\n        attr_accessor :started_request_log_level\n      end\n\n      def initialize(app, taggers = nil)\n        @app     = app\n        @taggers = taggers || []\n      end\n\n      def call(env)\n        request = ActionDispatch::Request.new(env)\n\n        # Check for named tags (Hash)\n        if @taggers && !@taggers.empty?\n          tags = @taggers.is_a?(Hash) ? compute_named_tags(request) : compute_tags(request)\n          logger.tagged(tags) { call_app(request, env) }\n        else\n          call_app(request, env)\n        end\n      end\n\n      private\n\n      @logger                    = SemanticLogger[\"Rack\"]\n      @started_request_log_level = :debug\n\n      def call_app(request, env)\n        instrumenter        = ActiveSupport::Notifications.instrumenter\n        if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7\n          handle              = instrumenter.build_handle \"request.action_dispatch\", request: request\n          instrumenter_finish = lambda {\n            handle.finish\n          }\n          handle.start\n        else\n          instrumenter_state  = instrumenter.start \"request.action_dispatch\", request: request\n          instrumenter_finish = lambda {\n            instrumenter.finish_with_state(instrumenter_state, \"request.action_dispatch\", request: request)\n          }\n        end\n\n        logger.send(self.class.started_request_log_level) { started_request_message(request) }\n        status, headers, body = @app.call(env)\n        body                  = ::Rack::BodyProxy.new(body, &instrumenter_finish)\n        [status, headers, body]\n      rescue Exception\n        instrumenter_finish.call\n        raise\n      end\n\n      def started_request_message(request)\n        {\n          message: \"Started\",\n          payload: {\n            method: request.request_method,\n            path:   request.filtered_path,\n            ip:     request.remote_ip\n          }\n        }\n      end\n\n      def compute_tags(request)\n        @taggers.collect do |tag|\n          case tag\n          when Proc\n            tag.call(request)\n          when Symbol\n            request.send(tag)\n          else\n            tag\n          end\n        end\n      end\n\n      # Leave out any named tags with a nil value\n      def compute_named_tags(request)\n        tagged = {}\n        @taggers.each_pair do |tag, value|\n          resolved    =\n            case value\n            when Proc\n              value.call(request)\n            when Symbol\n              request.send(value)\n            else\n              value\n            end\n          tagged[tag] = resolved unless resolved.nil?\n        end\n        tagged\n      end\n\n      def logger\n        self.class.logger\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/sidekiq/defaults.rb",
    "content": "module RailsSemanticLogger\n  module Sidekiq\n    module Defaults\n      # Prevent exception logging during standard error handling since the Job Logger below already logs the exception.\n      ERROR_HANDLER =\n        if ::Sidekiq::VERSION.to_f < 7.1 ||\n           (::Sidekiq::VERSION.to_f == 7.1 && ::Sidekiq::VERSION.split(\".\").last.to_i < 6)\n          lambda do |_ex, ctx|\n            unless ctx.empty?\n              job_hash = ctx[:job] || {}\n              klass    = job_hash[\"display_class\"] || job_hash[\"wrapped\"] || job_hash[\"class\"]\n              logger   = klass ? SemanticLogger[klass] : ::Sidekiq.logger\n              ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)\n            end\n          end\n        else\n          lambda do |_ex, ctx, _default_configuration|\n            unless ctx.empty?\n              job_hash = ctx[:job] || {}\n              klass    = job_hash[\"display_class\"] || job_hash[\"wrapped\"] || job_hash[\"class\"]\n              logger   = klass ? SemanticLogger[klass] : ::Sidekiq.logger\n              ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)\n            end\n          end\n        end\n\n      # Returns the default logger after removing from the supplied list.\n      # Returns [nil] when the default logger was not present.\n      def self.delete_default_error_handler(error_handlers)\n        return error_handlers.delete(::Sidekiq::Config::ERROR_HANDLER) if defined?(::Sidekiq::Config::ERROR_HANDLER)\n        return error_handlers.delete(::Sidekiq::DEFAULT_ERROR_HANDLER) if defined?(::Sidekiq::DEFAULT_ERROR_HANDLER)\n\n        return unless defined?(::Sidekiq::ExceptionHandler)\n\n        existing = error_handlers.find { |handler| handler.is_a?(::Sidekiq::ExceptionHandler::Logger) }\n        error_handlers.delete(existing) if existing\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/sidekiq/job_logger.rb",
    "content": "module RailsSemanticLogger\n  module Sidekiq\n    class JobLogger\n      class << self\n        attr_writer :perform_messages\n\n        def perform_messages\n          instance_variable_defined?(:@perform_messages) ? @perform_messages : true\n        end\n      end\n\n      # Sidekiq 6.5 does not take any arguments, whereas v7 is given a logger\n      def initialize(*_args)\n      end\n\n      def call(item, queue, &block)\n        klass  = item[\"wrapped\"] || item[\"class\"]\n        logger = klass ? SemanticLogger[klass] : Sidekiq.logger\n\n        SemanticLogger.tagged(queue: queue) do\n          if perform_messages_enabled?\n          # Latency is the time between when the job was enqueued and when it started executing.\n            logger.info(\n              \"Start #perform\",\n              metric:        \"sidekiq.queue.latency\",\n              metric_amount: job_latency_ms(item)\n            )\n          end\n\n          # Measure the duration of running the job\n          if perform_messages_enabled?\n            logger.measure_info(\n              \"Completed #perform\",\n              on_exception_level: :error,\n              log_exception:      :full,\n              metric:             \"sidekiq.job.perform\",\n              &block\n            )\n          else\n            yield if block_given?\n          end\n        end\n      end\n\n      def prepare(job_hash, &block)\n        level = job_hash[\"log_level\"]\n        if level\n          SemanticLogger.silence(level) do\n            SemanticLogger.tagged(job_hash_context(job_hash), &block)\n          end\n        else\n          SemanticLogger.tagged(job_hash_context(job_hash), &block)\n        end\n      end\n\n      private\n\n      def perform_messages_enabled?\n        self.class.perform_messages != false\n      end\n\n      def job_hash_context(job_hash)\n        h         = {jid: job_hash[\"jid\"]}\n        h[:bid]   = job_hash[\"bid\"] if job_hash[\"bid\"]\n        h[:tags]  = job_hash[\"tags\"] if job_hash[\"tags\"]\n        h[:queue] = job_hash[\"queue\"] if job_hash[\"queue\"]\n        h\n      end\n\n      def job_latency_ms(job)\n        return unless job && job[\"enqueued_at\"]\n\n        (Time.now.to_f - job[\"enqueued_at\"].to_f) * 1000\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/sidekiq/loggable.rb",
    "content": "module RailsSemanticLogger\n  module Sidekiq\n    module Loggable\n      def included(base)\n        super\n        base.include(SemanticLogger::Loggable)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger/version.rb",
    "content": "module RailsSemanticLogger\n  VERSION = \"4.20.0\".freeze\nend\n"
  },
  {
    "path": "lib/rails_semantic_logger.rb",
    "content": "require \"semantic_logger\"\nrequire \"rails_semantic_logger/extensions/rails/server\" if defined?(Rails::Server)\nrequire \"rails_semantic_logger/engine\"\n\nmodule RailsSemanticLogger\n  module ActionController\n    autoload :LogSubscriber, \"rails_semantic_logger/action_controller/log_subscriber\"\n  end\n\n  module ActionMailer\n    autoload :LogSubscriber, \"rails_semantic_logger/action_mailer/log_subscriber\"\n  end\n\n  module ActionView\n    autoload :LogSubscriber, \"rails_semantic_logger/action_view/log_subscriber\"\n  end\n\n  module ActiveJob\n    autoload :LogSubscriber, \"rails_semantic_logger/active_job/log_subscriber\"\n  end\n\n  module ActiveRecord\n    autoload :LogSubscriber, \"rails_semantic_logger/active_record/log_subscriber\"\n  end\n\n  module Rack\n    autoload :Logger, \"rails_semantic_logger/rack/logger\"\n  end\n\n  module DelayedJob\n    autoload :Plugin, \"rails_semantic_logger/delayed_job/plugin\"\n  end\n\n  module Sidekiq\n    autoload :Defaults, \"rails_semantic_logger/sidekiq/defaults\"\n    autoload :JobLogger, \"rails_semantic_logger/sidekiq/job_logger\"\n    autoload :Loggable, \"rails_semantic_logger/sidekiq/loggable\"\n  end\n\n  autoload :Options, \"rails_semantic_logger/options\"\n\n  # Swap an existing subscriber with a new one\n  def self.swap_subscriber(old_class, new_class, notifier)\n    subscribers = ActiveSupport::LogSubscriber.subscribers.select { |s| s.is_a?(old_class) }\n    subscribers.each { |subscriber| unattach(subscriber) }\n\n    new_class.attach_to(notifier)\n  end\n\n  def self.unattach(subscriber)\n    subscriber_patterns(subscriber).each do |pattern|\n      listeners_for(ActiveSupport::Notifications.notifier, pattern).each do |sub|\n        next unless sub.instance_variable_get(:@delegate) == subscriber\n\n        ActiveSupport::Notifications.unsubscribe(sub)\n      end\n    end\n\n    ActiveSupport::LogSubscriber.subscribers.delete(subscriber)\n  end\n\n  def self.subscriber_patterns(subscriber)\n    if subscriber.patterns.respond_to?(:keys)\n      subscriber.patterns.keys\n    else\n      subscriber.patterns\n    end\n  end\n\n  def self.listeners_for(notifier, pattern)\n    if notifier.respond_to?(:all_listeners_for) # Rails >= 7.1\n      notifier.all_listeners_for(pattern)\n    else\n      notifier.listeners_for(pattern)\n    end\n  end\n\n  private_class_method :listeners_for, :subscriber_patterns, :unattach\nend\n\nrequire(\"rails_semantic_logger/extensions/mongoid/config\") if defined?(Mongoid)\nrequire(\"rails_semantic_logger/extensions/active_support/logger\") if defined?(ActiveSupport::Logger)\nrequire(\"rails_semantic_logger/extensions/active_support/log_subscriber\") if defined?(ActiveSupport::LogSubscriber)\n\nbegin\n  require \"rackup\"\nrescue LoadError\n  # No need to do anything, will fall back to Rack\nend\nif defined?(Rackup::Server)\n  require(\"rails_semantic_logger/extensions/rackup/server\")\nelsif defined?(Rack::Server)\n  require(\"rails_semantic_logger/extensions/rack/server\")\nend\n"
  },
  {
    "path": "rails_semantic_logger.gemspec",
    "content": "$LOAD_PATH.push File.expand_path(\"lib\", __dir__)\n\n# Maintain your gem's version:\nrequire \"rails_semantic_logger/version\"\n\n# Describe your gem and declare its dependencies:\nGem::Specification.new do |s|\n  s.name                  = \"rails_semantic_logger\"\n  s.version               = RailsSemanticLogger::VERSION\n  s.platform              = Gem::Platform::RUBY\n  s.authors               = [\"Reid Morrison\"]\n  s.homepage              = \"https://logger.rocketjob.io\"\n  s.summary               = \"Feature rich logging framework that replaces the Rails logger.\"\n  s.files                 = Dir[\"lib/**/*\", \"LICENSE.txt\", \"Rakefile\", \"README.md\"]\n  s.license               = \"Apache-2.0\"\n  s.required_ruby_version = \">= 2.5\"\n  s.add_dependency \"rack\"\n  s.add_dependency \"railties\", \">= 5.1\"\n  s.add_dependency \"semantic_logger\", \"~> 4.16\"\n  s.metadata = {\n    \"bug_tracker_uri\"       => \"https://github.com/reidmorrison/rails_semantic_logger/issues\",\n    \"documentation_uri\"     => \"https://logger.rocketjob.io\",\n    \"source_code_uri\"       => \"https://github.com/reidmorrison/rails_semantic_logger/tree/v#{RailsSemanticLogger::VERSION}\",\n    \"rubygems_mfa_required\" => \"true\"\n  }\nend\n"
  },
  {
    "path": "test/action_controller_test.rb",
    "content": "require_relative \"test_helper\"\n\nclass ActionControllerTest < Minitest::Test\n  describe \"RailsSemanticLogger::ActionController::LogSubscriber\" do\n    let(:subscriber) { RailsSemanticLogger::ActionController::LogSubscriber.new }\n\n    describe \"#process_action\" do\n      it \"does not fail if params is not a Hash nor an instance of ActionController::Parameters\" do\n        event = ActiveSupport::Notifications::Event.new(\n          \"start_processing.action_controller\",\n          5.seconds.ago,\n          Time.zone.now,\n          SecureRandom.uuid,\n          {\n            payload: \"{}\"\n          }\n        )\n\n        messages = semantic_logger_events do\n          subscriber.process_action(event)\n        end\n\n        assert_equal 1, messages.count, messages\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/action_mailer_test.rb",
    "content": "require_relative \"test_helper\"\n\nclass ActionMailerTest < Minitest::Test\n  class MyMailer < ActionMailer::Base\n    def some_email(opts)\n      mail(to: opts[:to], from: opts[:from], subject: opts[:subject], body: \"Hello\")\n    end\n  end\n\n  describe \"ActionMailer\" do\n    describe \"#deliver\" do\n      it \"sets the ActionMailer logger\" do\n        assert_kind_of SemanticLogger::Logger, MyMailer.logger\n      end\n\n      it \"sends the email\" do\n        MyMailer.some_email(to: \"test@test.com\", from: \"test@test.com\", subject: \"test\").deliver_now\n      end\n\n      it \"writes log messages\" do\n        messages = semantic_logger_events do\n          MyMailer.some_email(to: \"test@test.com\", from: \"test@test.com\", subject: \"test\").deliver_now\n        end\n        assert_equal 2, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:            :info,\n          name:             \"ActionMailer::Base\",\n          message_includes: \"ActionMailerTest::MyMailer#some_email: processed outbound mail\",\n          payload_includes: {\n            event_name: \"process.action_mailer\",\n            mailer:     \"ActionMailerTest::MyMailer\",\n            action:     :some_email\n          }\n        )\n\n        assert_semantic_logger_event(\n          messages[1],\n          level:            :info,\n          name:             \"ActionMailer::Base\",\n          message_includes: Rails::VERSION::MAJOR >= 6 ? \"Delivered mail\" : \"Skipped delivery\",\n          payload_includes: {\n            event_name:         \"deliver.action_mailer\",\n            mailer:             \"ActionMailerTest::MyMailer\",\n            perform_deliveries: Rails::VERSION::MAJOR >= 6 ? true : nil,\n            subject:            \"test\",\n            to:                 [\"test@test.com\"],\n            from:               [\"test@test.com\"]\n          }\n        )\n      end\n    end\n\n    describe \"Logging::LogSubscriber\" do\n      before do\n        skip \"Older rails does not support ActiveSupport::Notification\" unless defined?(ActiveSupport::Notifications)\n      end\n\n      let(:subscriber) { RailsSemanticLogger::ActionMailer::LogSubscriber.new }\n\n      let(:event) do\n        ActiveSupport::Notifications::Event.new(event_name, 5.seconds.ago, Time.zone.now, SecureRandom.uuid, payload)\n      end\n\n      let(:payload) do\n        {\n          mailer: \"MyMailer\",\n          action: :some_email\n        }\n      end\n\n      let(:event_name) { \"deliver.action_mailer\" }\n\n      let(:mailer) do\n        MyMailer.some_email(to: \"test@test.com\", from: \"test@test.com\", subject: \"test\")\n      end\n\n      %i[deliver process].each do |method|\n        describe \"##{method}\" do\n          specify do\n            assert ActionMailer::Base.logger.info\n            subscriber.public_send(method, event)\n          end\n        end\n      end\n\n      describe \"ActiveJob::Logging::LogSubscriber::EventFormatter\" do\n        let(:formatter) do\n          RailsSemanticLogger::ActionMailer::LogSubscriber::EventFormatter.new(event: event, log_duration: true)\n        end\n\n        let(:event_name) { \"deliver.action_mailer\" }\n\n        describe \"#payload\" do\n          specify do\n            assert_equal(formatter.payload[:event_name], \"deliver.action_mailer\")\n            assert_equal(formatter.payload[:mailer], \"MyMailer\")\n            assert_equal(formatter.payload[:action], :some_email)\n            assert_kind_of(Float, formatter.payload[:duration])\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/active_job_test.rb",
    "content": "require_relative \"test_helper\"\n\nclass ActiveJobTest < Minitest::Test\n  if defined?(ActiveJob)\n    class MyJob < ActiveJob::Base\n      queue_as :my_jobs\n\n      def perform(record)\n        \"Received: #{record}\"\n      end\n    end\n\n    class SensitiveJob < ActiveJob::Base\n      queue_as :my_jobs\n\n      if Rails.version.to_f >= 6.1\n        self.log_arguments = false\n      else\n        def self.log_arguments?\n          false\n        end\n      end\n\n      def perform(record)\n        \"Received: #{record}\"\n      end\n    end\n\n    class TestModel\n      include GlobalID::Identification\n\n      def id\n        15\n      end\n    end\n  end\n\n  describe \"ActiveJob\" do\n    before do\n      skip \"Older rails does not support ActiveJob\" unless defined?(ActiveJob)\n    end\n\n    describe \".perform_now\" do\n      it \"sets the ActiveJob logger\" do\n        assert_kind_of SemanticLogger::Logger, MyJob.logger\n      end\n\n      it \"runs the job\" do\n        MyJob.perform_now(\"hello\")\n      end\n    end\n\n    describe \"Logging::LogSubscriber\" do\n      before do\n        skip \"Older rails does not support ActiveSupport::Notification\" unless defined?(ActiveSupport::Notifications)\n      end\n\n      let(:subscriber) { RailsSemanticLogger::ActiveJob::LogSubscriber.new }\n\n      let(:event) do\n        ActiveSupport::Notifications::Event.new(event_name, 5.seconds.ago, Time.zone.now, SecureRandom.uuid, payload)\n      end\n\n      let(:event_name) { \"enqueue.active_job\" }\n\n      let(:payload) do\n        {\n          adapter: ActiveJob::QueueAdapters::InlineAdapter.new,\n          job:     job\n        }\n      end\n\n      let(:job) do\n        MyJob.new(TestModel.new, 1, \"string\", foo: \"bar\")\n      end\n\n      %i[enqueue enqueue_at perform_start perform].each do |method|\n        describe \"##{method}\" do\n          specify do\n            job.stub(:scheduled_at, Time.zone.now.to_i) do\n              assert ActiveJob::Base.logger.info\n              subscriber.public_send(method, event)\n            end\n          end\n        end\n      end\n\n      describe \"#perform with exception object\" do\n        let(:event_name) { \"perform.active_job\" }\n\n        let(:payload) do\n          {\n            adapter:          ActiveJob::QueueAdapters::InlineAdapter.new,\n            job:              job,\n            exception_object: ArgumentError.new(\"error\")\n          }\n        end\n\n        it \"logs messages\" do\n          messages = semantic_logger_events do\n            subscriber.perform(event)\n          end\n          assert_equal 1, messages.count, messages\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:            :error,\n            name:             \"Rails\",\n            message_includes: \"Error performing ActiveJobTest::MyJob\",\n            payload_includes: {\n              job_class:  \"ActiveJobTest::MyJob\",\n              queue:      \"my_jobs\",\n              event_name: \"perform.active_job\"\n            }\n          )\n          assert_includes messages[0].payload, :job_id\n\n          exception = messages[0].exception\n          assert exception.is_a?(ArgumentError)\n          assert_equal \"error\", exception.message\n        end\n      end\n\n      describe \"#enqueue with exception object\" do\n        let(:event_name) { \"enqueue.active_job\" }\n\n        let(:payload) do\n          {\n            adapter:          ActiveJob::QueueAdapters::InlineAdapter.new,\n            job:              job,\n            exception_object: ArgumentError.new(\"error\")\n          }\n        end\n\n        it \"logs message\" do\n          messages = semantic_logger_events do\n            subscriber.enqueue(event)\n          end\n          assert_equal 1, messages.count, messages\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:            :error,\n            name:             \"Rails\",\n            message_includes: \"Failed enqueuing ActiveJobTest::MyJob\",\n            payload_includes: {\n              job_class:  \"ActiveJobTest::MyJob\",\n              queue:      \"my_jobs\",\n              event_name: \"enqueue.active_job\"\n            }\n          )\n          assert_includes messages[0].payload, :job_id\n\n          exception = messages[0].exception\n          assert exception.is_a?(ArgumentError)\n          assert_equal \"error\", exception.message\n        end\n      end\n\n      describe \"#enqueue with throwing :abort\" do\n        let(:event_name) { \"enqueue.active_job\" }\n\n        let(:payload) do\n          {\n            adapter: ActiveJob::QueueAdapters::InlineAdapter.new,\n            job:     job,\n            aborted: true\n          }\n        end\n\n        it \"logs message\" do\n          messages = semantic_logger_events do\n            subscriber.enqueue(event)\n          end\n          assert_equal 1, messages.count, messages\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:            :info,\n            name:             \"Rails\",\n            message_includes: \"Failed enqueuing ActiveJobTest::MyJob\",\n            payload_includes: {\n              job_class:  \"ActiveJobTest::MyJob\",\n              queue:      \"my_jobs\",\n              event_name: \"enqueue.active_job\"\n            }\n          )\n          assert_match(/Failed enqueuing .*, a before_enqueue callback halted the enqueuing execution/, messages[0].message)\n          assert_includes messages[0].payload, :job_id\n        end\n      end\n\n      describe \"#enqueue_at with exception object\" do\n        let(:event_name) { \"enqueue.active_job\" }\n\n        let(:payload) do\n          {\n            adapter:          ActiveJob::QueueAdapters::InlineAdapter.new,\n            job:              job,\n            exception_object: ArgumentError.new(\"error\")\n          }\n        end\n\n        it \"logs message\" do\n          messages = semantic_logger_events do\n            subscriber.enqueue_at(event)\n          end\n          assert_equal 1, messages.count, messages\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:            :error,\n            name:             \"Rails\",\n            message_includes: \"Failed enqueuing ActiveJobTest::MyJob\",\n            payload_includes: {\n              job_class:  \"ActiveJobTest::MyJob\",\n              queue:      \"my_jobs\",\n              event_name: \"enqueue.active_job\"\n            }\n          )\n          assert_includes messages[0].payload, :job_id\n\n          exception = messages[0].exception\n          assert exception.is_a?(ArgumentError)\n          assert_equal \"error\", exception.message\n        end\n      end\n\n      describe \"#enqueue_at with throwing :abort\" do\n        let(:event_name) { \"enqueue.active_job\" }\n\n        let(:payload) do\n          {\n            adapter: ActiveJob::QueueAdapters::InlineAdapter.new,\n            job:     job,\n            aborted: true\n          }\n        end\n\n        it \"logs message\" do\n          messages = semantic_logger_events do\n            subscriber.enqueue_at(event)\n          end\n          assert_equal 1, messages.count, messages\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:            :info,\n            name:             \"Rails\",\n            message_includes: \"Failed enqueuing ActiveJobTest::MyJob\",\n            payload_includes: {\n              job_class:  \"ActiveJobTest::MyJob\",\n              queue:      \"my_jobs\",\n              event_name: \"enqueue.active_job\"\n            }\n          )\n          assert_match(/Failed enqueuing .*, a before_enqueue callback halted the enqueuing execution/, messages[0].message)\n          assert_includes messages[0].payload, :job_id\n        end\n      end\n\n      describe \"ActiveJob::Logging::LogSubscriber::EventFormatter\" do\n        let(:formatter) do\n          RailsSemanticLogger::ActiveJob::LogSubscriber::EventFormatter.new(event: event, log_duration: true)\n        end\n\n        let(:event_name) { \"perform.active_job\" }\n\n        describe \"#payload\" do\n          specify do\n            assert_equal(formatter.payload[:event_name], \"perform.active_job\")\n            assert_equal(formatter.payload[:adapter], \"Inline\")\n            assert_equal(formatter.payload[:queue], \"my_jobs\")\n            assert_kind_of(String, formatter.payload[:job_id])\n            assert_kind_of(Float, formatter.payload[:duration])\n          end\n\n          describe \"Show arguments in log\" do\n            let(:job) do\n              MyJob.new(TestModel.new, 1, \"string\", foo: \"bar\")\n            end\n\n            specify do\n              assert_equal(formatter.payload[:job_class], \"ActiveJobTest::MyJob\")\n              arguments = <<~ARGS.chomp\n                [\n                  \"gid://dummy/ActiveJobTest::TestModel/15\",\n                  1,\n                  \"string\",\n                  {\n                    \"foo\": \"bar\"\n                  }\n                ]\n              ARGS\n              assert_equal(formatter.payload[:arguments], arguments)\n            end\n          end\n\n          describe \"Hide arguments from log\" do\n            let(:job) do\n              SensitiveJob.new(TestModel.new, 1, \"string\", foo: \"bar\")\n            end\n\n            specify do\n              assert_equal(formatter.payload[:job_class], \"ActiveJobTest::SensitiveJob\")\n              assert_equal(formatter.payload[:arguments], \"\")\n            end\n          end\n        end\n\n        describe \"#job_info\" do\n          specify do\n            assert_match(/^ActiveJobTest::MyJob \\(Job ID: [a-z\\-0-9]+\\) to Inline\\(my_jobs\\)$/, formatter.job_info)\n          end\n        end\n\n        describe \"#queue_name\" do\n          specify do\n            assert_equal(formatter.queue_name, \"Inline(my_jobs)\")\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/active_record_test.rb",
    "content": "require_relative \"test_helper\"\n\nclass ActiveRecordTest < Minitest::Test\n  describe \"ActiveRecord\" do\n    # Rails 5 has an extra space\n    let(:extra_space) { Rails::VERSION::MAJOR >= 6 ? \"\" : \" \" }\n\n    describe \"logs\" do\n      it \"sql\" do\n        expected_sql = \"SELECT #{extra_space}\\\"samples\\\".* FROM \\\"samples\\\" ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n\n        messages = semantic_logger_events do\n          Sample.first\n        end\n        assert_equal 1, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:            :debug,\n          name:             \"ActiveRecord\",\n          message:          \"Sample Load\",\n          payload_includes: {\n            sql:   expected_sql,\n            binds: {limit: 1}\n          }\n        )\n        assert_instance_of Integer, messages[0].payload[:allocations] if Rails.version.to_i >= 6\n      end\n\n      it \"sql with query cache\" do\n        expected_sql = \"SELECT #{extra_space}\\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"name\\\" = ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n\n        messages = semantic_logger_events do\n          Sample.cache { 2.times { Sample.where(name: \"foo\").first } }\n        end\n        assert_equal 2, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:            :debug,\n          name:             \"ActiveRecord\",\n          message:          \"Sample Load\",\n          payload_includes: {\n            sql:   expected_sql,\n            binds: {name: \"foo\", limit: 1}\n          }\n        )\n        assert_instance_of Integer, messages[0].payload[:allocations] if Rails.version.to_i >= 6\n\n        assert_semantic_logger_event(\n          messages[1],\n          level:            :debug,\n          name:             \"ActiveRecord\",\n          message:          \"Sample Load\",\n          payload_includes: {\n            sql:    expected_sql,\n            binds:  {name: \"foo\", limit: 1},\n            cached: true\n          }\n        )\n        assert_instance_of Integer, messages[0].payload[:allocations] if Rails.version.to_i >= 6\n      end\n\n      it \"single bind value\" do\n        expected_sql =\n          if Rails.version.to_f >= 5.2\n            \"SELECT #{extra_space}\\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"name\\\" = ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n          else\n            \"SELECT  \\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"name\\\" = ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n          end\n\n        messages = semantic_logger_events do\n          Sample.where(name: \"Jack\").first\n        end\n        assert_equal 1, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:            :debug,\n          name:             \"ActiveRecord\",\n          message:          \"Sample Load\",\n          payload_includes: {\n            sql:   expected_sql,\n            binds: {name: \"Jack\", limit: 1}\n          }\n        )\n        assert_instance_of Integer, messages[0].payload[:allocations] if Rails.version.to_i >= 6\n      end\n\n      it \"filtered bind value\" do\n        filter_params_setting true, %i[name] do\n          expected_sql =\n            if Rails.version.to_f >= 5.2\n              \"SELECT #{extra_space}\\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"name\\\" = ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n            else\n              \"SELECT  \\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"name\\\" = ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n            end\n\n          messages = semantic_logger_events do\n            Sample.where(name: \"Jack\").first\n          end\n          assert_equal 1, messages.count, messages\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:            :debug,\n            name:             \"ActiveRecord\",\n            message:          \"Sample Load\",\n            payload_includes: {\n              sql:   expected_sql,\n              binds: {name: \"[FILTERED]\", limit: 1}\n            }\n          )\n          assert_instance_of Integer, messages[0].payload[:allocations] if Rails.version.to_i >= 6\n        end\n      end\n\n      it \"filtered bind value when filter_parameters set as regex\" do\n        filter_params_regex_setting true, %i[name] do\n          expected_sql =\n            if Rails.version.to_f >= 5.2\n              \"SELECT #{extra_space}\\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"name\\\" = ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n            else\n              \"SELECT  \\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"name\\\" = ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n            end\n\n          messages = semantic_logger_events do\n            Sample.where(name: \"Jack\").first\n          end\n          assert_equal 1, messages.count, messages\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:            :debug,\n            name:             \"ActiveRecord\",\n            message:          \"Sample Load\",\n            payload_includes: {\n              sql:   expected_sql,\n              binds: {name: \"[FILTERED]\", limit: 1}\n            }\n          )\n          assert_instance_of Integer, messages[0].payload[:allocations] if Rails.version.to_i >= 6\n        end\n      end\n\n      it \"multiple bind values\" do\n        skip \"Not applicable to older rails\" if Rails.version.to_f <= 5.1\n\n        expected_sql = \"SELECT #{extra_space}\\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"age\\\" BETWEEN ? AND ? ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n\n        messages = semantic_logger_events do\n          Sample.where(age: 2..21).first\n        end\n        assert_equal 1, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:            :debug,\n          name:             \"ActiveRecord\",\n          message:          \"Sample Load\",\n          payload_includes: {\n            sql:   expected_sql,\n            binds: {age: [2, 21], limit: 1}\n          }\n        )\n        assert_instance_of Integer, messages[0].payload[:allocations] if Rails.version.to_i >= 6\n      end\n\n      it \"works with an IN clause\" do\n        skip \"Not applicable to older rails\" if Rails.version.to_f <= 5.1\n\n        expected_sql = \"SELECT #{extra_space}\\\"samples\\\".* FROM \\\"samples\\\" WHERE \\\"samples\\\".\\\"age\\\" IN (?, ?) ORDER BY \\\"samples\\\".\\\"id\\\" ASC LIMIT ?\"\n\n        messages = semantic_logger_events do\n          Sample.where(age: [2, 3]).first\n        end\n        assert_equal 1, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:            :debug,\n          name:             \"ActiveRecord\",\n          message:          \"Sample Load\",\n          payload_includes: {\n            sql:   expected_sql,\n            binds: {age: [2, 3], limit: 1}\n          }\n        )\n      end\n    end\n\n    describe \"async queries\" do\n      before do\n        skip \"Not applicable to older rails\" if Rails.version.to_f < 7.1\n        ActiveRecord::Base.asynchronous_queries_tracker.start_session\n      end\n\n      after do\n        ActiveRecord::Base.asynchronous_queries_tracker.finalize_session\n      end\n\n      it \"marks async queries with async: true\" do\n        expected_sql = 'SELECT COUNT(*) FROM \"samples\"'\n\n        messages = semantic_logger_events do\n          Sample.count\n          Sample.async_count.value\n        end\n        assert_equal 2, messages.count, messages\n\n        messages.each do |message|\n          assert_semantic_logger_event(\n            message,\n            level:            :debug,\n            name:             \"ActiveRecord\",\n            message:          \"Sample Count\",\n            payload_includes: {sql: expected_sql}\n          )\n        end\n\n        # On Rails prior to 8.0.2, these assertions will mostly pass, but not always.\n        # https://github.com/rails/rails/pull/54344\n        skip \"Older Rails has flakey async instrumentation\" if Rails::VERSION::MAJOR < 8\n        refute messages[0].payload.key?(:async)\n        assert_equal true, messages[1].payload[:async]\n      end\n    end\n\n    # we could feasibly pull this back to rails 7.1.  This update is related to rails 8.1\n    # https://github.com/reidmorrison/rails_semantic_logger/pull/276#issuecomment-3533151110\n    describe \"runtime=\" do\n      it \"allow reads and writes to the runtime\" do\n        unless RailsSemanticLogger::ActiveRecord::LogSubscriber.respond_to?(:runtime)\n          skip \"runtime support ended with Rails v7.1\"\n        end\n\n        RailsSemanticLogger::ActiveRecord::LogSubscriber.runtime = 5.0\n\n        assert_equal RailsSemanticLogger::ActiveRecord::LogSubscriber.runtime, 5.0\n      end\n\n      it \"write to the runtime\" do\n        unless RailsSemanticLogger::ActiveRecord::LogSubscriber.respond_to?(:runtime)\n          skip \"runtime support ended with Rails v7.1\"\n        end\n\n        initial_value = RailsSemanticLogger::ActiveRecord::LogSubscriber.runtime\n        RailsSemanticLogger::ActiveRecord::LogSubscriber.runtime = initial_value + 5000.0\n        assert_equal RailsSemanticLogger::ActiveRecord::LogSubscriber.runtime, initial_value + 5000.0\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/controllers/articles_controller_test.rb",
    "content": "require_relative \"../test_helper\"\n\nclass ArticlesControllerTest < ActionDispatch::IntegrationTest\n  describe ArticlesController do\n    let(:params) { {article: {text: \"Text1\", title: \"Title1\"}} }\n\n    describe \"#new\" do\n      it \"shows new article\" do\n        get article_url(:new)\n\n        assert_response :success\n      end\n    end\n\n    describe \"#create\" do\n      it \"has no errors\" do\n        post articles_url(params: params)\n\n        assert_response :success\n      end\n\n      it \"successfully logs message\" do\n        messages = semantic_logger_events do\n          post articles_url(params: params)\n        end\n        assert_equal 5, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          message: \"Started\",\n          name:    \"Rack\",\n          level:   :debug,\n          payload: {\n            method: \"POST\",\n            path:   \"/articles?article%5Btext%5D=Text1&article%5Btitle%5D=Title1\",\n            ip:     \"127.0.0.1\"\n          }\n        )\n\n        assert_semantic_logger_event(\n          messages[1],\n          message: \"Processing #create\",\n          name:    \"ArticlesController\",\n          level:   :debug\n        )\n\n        assert_semantic_logger_event(\n          messages[2],\n          message: \"Rendering\",\n          name:    \"ActionView\",\n          level:   :debug,\n          payload: {\n            template: \"text template\"\n          }\n        )\n\n        assert_semantic_logger_event(\n          messages[3],\n          message: \"Rendered\",\n          name:    \"ActionView\",\n          level:   :debug\n        )\n\n        assert_semantic_logger_event(\n          messages[4],\n          message:          \"Completed #create\",\n          name:             \"ArticlesController\",\n          level:            :info,\n          payload_includes: {\n            controller:     \"ArticlesController\",\n            action:         \"create\",\n            params:         {\n              \"article\" => {\n                \"text\"  => \"Text1\",\n                \"title\" => \"Title1\"\n              }\n            },\n            format:         \"HTML\",\n            method:         \"POST\",\n            path:           \"/articles\",\n            status:         200,\n            status_message: \"OK\"\n          }\n        )\n      end\n\n      it \"customize action message\" do\n        old_action_message_format = RailsSemanticLogger::ActionController::LogSubscriber.action_message_format\n        RailsSemanticLogger::ActionController::LogSubscriber.action_message_format = -> (message, payload) do\n          \"#{message} #{payload[:controller]}##{payload[:action]}\"\n        end\n\n        messages = semantic_logger_events do\n          post articles_url(params: params)\n        end\n        assert_equal 5, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          message: \"Started\"\n        )\n\n        assert_semantic_logger_event(\n          messages[1],\n          message: \"Processing ArticlesController#create\"\n        )\n\n        assert_semantic_logger_event(\n          messages[2],\n          message: \"Rendering\"\n        )\n\n        assert_semantic_logger_event(\n          messages[3],\n          message: \"Rendered\"\n        )\n\n        assert_semantic_logger_event(\n          messages[4],\n          message: \"Completed ArticlesController#create\",\n        )\n      ensure\n        RailsSemanticLogger::ActionController::LogSubscriber.action_message_format = old_action_message_format\n      end\n    end\n\n    describe \"#show\" do\n      it \"raises and logs exception\" do\n        # we're testing ActionDispatch::DebugExceptions in fact\n        messages = semantic_logger_events do\n          old_show = Rails.application.env_config[\"action_dispatch.show_exceptions\"]\n          begin\n            Rails.application.env_config[\"action_dispatch.show_exceptions\"] = :all\n            get article_url(:show)\n          rescue ActiveRecord::RecordNotFound => e\n            # expected\n          ensure\n            Rails.application.env_config[\"action_dispatch.show_exceptions\"] = old_show\n          end\n        end\n        assert_equal 4, messages.count, messages\n        assert_kind_of ActiveRecord::RecordNotFound, messages[3].exception\n      end\n\n      it \"raises and does not log exception when action_dispatch.log_rescued_responses is false\" do\n        skip \"Not applicable to older rails\" if Rails.version.to_f < 7.1\n        # we're testing ActionDispatch::DebugExceptions here too\n        messages = semantic_logger_events do\n          old_show = Rails.application.env_config[\"action_dispatch.show_exceptions\"]\n          old_log_rescued_responses = Rails.application.env_config[\"action_dispatch.log_rescued_responses\"]\n\n          begin\n            Rails.application.env_config[\"action_dispatch.show_exceptions\"] = :all\n            Rails.application.env_config[\"action_dispatch.log_rescued_responses\"] = false\n            get article_url(:show)\n          rescue ActiveRecord::RecordNotFound => e\n            # expected\n          ensure\n            Rails.application.env_config[\"action_dispatch.show_exceptions\"] = old_show\n            Rails.application.env_config[\"action_dispatch.log_rescued_responses\"] = old_log_rescued_responses\n          end\n        end\n        assert_equal 3, messages.count, messages\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/controllers/dashboard_controller_test.rb",
    "content": "require_relative \"../test_helper\"\n\nclass DashboardControllerTest < ActionDispatch::IntegrationTest\n  describe DashboardController do\n    describe \"#show\" do\n      it \"has no errors\" do\n        get dashboard_url\n\n        assert_response :success\n      end\n\n      it \"logs message\" do\n        messages = semantic_logger_events do\n          get dashboard_url\n        end\n        assert_equal 3, messages.count, messages\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:   :debug,\n          name:    \"Rack\",\n          message: \"Started\",\n          payload: {\n            method: \"GET\",\n            path:   \"/dashboard\",\n            ip:     \"127.0.0.1\"\n          }\n        )\n\n        assert_semantic_logger_event(\n          messages[1],\n          level:   :debug,\n          name:    \"Rails\",\n          message: \"Processing #show\",\n          payload: nil\n        )\n\n        assert_semantic_logger_event(\n          messages[2],\n          level:            :info,\n          name:             \"Rails\",\n          message:          \"Completed #show\",\n          payload_includes: {\n            controller:     \"DashboardController\",\n            action:         \"show\",\n            format:         \"HTML\",\n            method:         \"GET\",\n            path:           \"/dashboard\",\n            status:         200,\n            status_message: \"OK\"\n          }\n        )\n      end\n\n      it \"does not break rails notifications\" do\n        PayloadCollector.wrap do\n          get dashboard_url\n        end\n\n        payload = PayloadCollector.last\n        assert_equal payload[:params], {\"controller\" => \"dashboard\", \"action\" => \"show\"}\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/controllers/welcome_controller_test.rb",
    "content": "require_relative \"../test_helper\"\n\nclass WelcomeControllerTest < ActionDispatch::IntegrationTest\n  describe WelcomeController do\n    describe \"#index\" do\n      it \"succeeds\" do\n        get \"/welcome/index\"\n\n        assert_response :success\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/dummy/README.rdoc",
    "content": "== Welcome to Rails\n\nRails is a web-application framework that includes everything needed to create\ndatabase-backed web applications according to the Model-View-Control pattern.\n\nThis pattern splits the view (also called the presentation) into \"dumb\"\ntemplates that are primarily responsible for inserting pre-built data in between\nHTML tags. The model contains the \"smart\" domain objects (such as Account,\nProduct, Person, Post) that holds all the business logic and knows how to\npersist themselves to a database. The controller handles the incoming requests\n(such as Save New Account, Update Product, Show Post) by manipulating the model\nand directing data to the view.\n\nIn Rails, the model is handled by what's called an object-relational mapping\nlayer entitled Active Record. This layer allows you to present the data from\ndatabase rows as objects and embellish these data objects with business logic\nmethods. You can read more about Active Record in\nlink:files/vendor/rails/activerecord/README.html.\n\nThe controller and view are handled by the Action Pack, which handles both\nlayers by its two parts: Action View and Action Controller. These two layers\nare bundled in a single package due to their heavy interdependence. This is\nunlike the relationship between the Active Record and Action Pack that is much\nmore separate. Each of these packages can be used independently outside of\nRails. You can read more about Action Pack in\nlink:files/vendor/rails/actionpack/README.html.\n\n\n== Getting Started\n\n1. At the command prompt, create a new Rails application:\n       <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)\n\n2. Change directory to <tt>myapp</tt> and start the web server:\n       <tt>cd myapp; rails server</tt> (run with --help for options)\n\n3. Go to http://localhost:3000/ and you'll see:\n       \"Welcome aboard: You're riding Ruby on Rails!\"\n\n4. Follow the guidelines to start developing your application. You can find\nthe following resources handy:\n\n* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html\n* Ruby on Rails Tutorial Book: http://www.railstutorial.org/\n\n\n== Debugging Rails\n\nSometimes your application goes wrong. Fortunately there are a lot of tools that\nwill help you debug it and get it back on the rails.\n\nFirst area to check is the application log files. Have \"tail -f\" commands\nrunning on the server.log and development.log. Rails will automatically display\ndebugging and runtime information to these files. Debugging info will also be\nshown in the browser on requests from 127.0.0.1.\n\nYou can also log your own messages directly into the log file from your code\nusing the Ruby logger class from inside your controllers. Example:\n\n  class WeblogController < ActionController::Base\n    def destroy\n      @weblog = Weblog.find(params[:id])\n      @weblog.destroy\n      logger.info(\"#{Time.now} Destroyed Weblog ID ##{@weblog.id}!\")\n    end\n  end\n\nThe result will be a message in your log file along the lines of:\n\n  Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!\n\nMore information on how to use the logger is at http://www.ruby-doc.org/core/\n\nAlso, Ruby documentation can be found at http://www.ruby-lang.org/. There are\nseveral books available online as well:\n\n* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)\n* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)\n\nThese two books will bring you up to speed on the Ruby language and also on\nprogramming in general.\n\n\n== Debugger\n\nDebugger support is available through the debugger command when you start your\nMongrel or WEBrick server with --debugger. This means that you can break out of\nexecution at any point in the code, investigate and change the model, and then,\nresume execution! You need to install ruby-debug to run the server in debugging\nmode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:\n\n  class WeblogController < ActionController::Base\n    def index\n      @posts = Post.all\n      debugger\n    end\n  end\n\nSo the controller will accept the action, run the first line, then present you\nwith a IRB prompt in the server window. Here you can do things like:\n\n  >> @posts.inspect\n  => \"[#<Post:0x14a6be8\n          @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,\n       #<Post:0x14a6620\n          @attributes={\"title\"=>\"Rails\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]\"\n  >> @posts.first.title = \"hello from a debugger\"\n  => \"hello from a debugger\"\n\n...and even better, you can examine how your runtime objects actually work:\n\n  >> f = @posts.first\n  => #<Post:0x13630c4 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>\n  >> f.\n  Display all 152 possibilities? (y or n)\n\nFinally, when you're ready to resume execution, you can enter \"cont\".\n\n\n== Console\n\nThe console is a Ruby shell, which allows you to interact with your\napplication's domain model. Here you'll have all parts of the application\nconfigured, just like it is when the application is running. You can inspect\ndomain models, change values, and save to the database. Starting the script\nwithout arguments will launch it in the development environment.\n\nTo start the console, run <tt>rails console</tt> from the application\ndirectory.\n\nOptions:\n\n* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications\n  made to the database.\n* Passing an environment name as an argument will load the corresponding\n  environment. Example: <tt>rails console production</tt>.\n\nTo reload your controllers and models after launching the console run\n<tt>reload!</tt>\n\nMore information about irb can be found at:\nlink:http://www.rubycentral.org/pickaxe/irb.html\n\n\n== dbconsole\n\nYou can go to the command line of your database directly through <tt>rails\ndbconsole</tt>. You would be connected to the database with the credentials\ndefined in database.yml. Starting the script without arguments will connect you\nto the development database. Passing an argument will connect you to a different\ndatabase, like <tt>rails dbconsole production</tt>. Currently works for MySQL,\nPostgreSQL and SQLite 3.\n\n== Description of Contents\n\nThe default directory structure of a generated Ruby on Rails application:\n\n  |-- app\n  |   |-- assets\n  |       |-- images\n  |       |-- javascripts\n  |       `-- stylesheets\n  |   |-- controllers\n  |   |-- helpers\n  |   |-- mailers\n  |   |-- models\n  |   `-- views\n  |       `-- layouts\n  |-- config\n  |   |-- environments\n  |   |-- initializers\n  |   `-- locales\n  |-- db\n  |-- doc\n  |-- lib\n  |   `-- tasks\n  |-- log\n  |-- public\n  |-- script\n  |-- test\n  |   |-- fixtures\n  |   |-- functional\n  |   |-- integration\n  |   |-- performance\n  |   `-- unit\n  |-- tmp\n  |   |-- cache\n  |   |-- pids\n  |   |-- sessions\n  |   `-- sockets\n  `-- vendor\n      |-- assets\n          `-- stylesheets\n      `-- plugins\n\napp\n  Holds all the code that's specific to this particular application.\n\napp/assets\n  Contains subdirectories for images, stylesheets, and JavaScript files.\n\napp/controllers\n  Holds controllers that should be named like weblogs_controller.rb for\n  automated URL mapping. All controllers should descend from\n  ApplicationController which itself descends from ActionController::Base.\n\napp/models\n  Holds models that should be named like post.rb. Models descend from\n  ActiveRecord::Base by default.\n\napp/views\n  Holds the template files for the view that should be named like\n  weblogs/index.html.erb for the WeblogsController#index action. All views use\n  eRuby syntax by default.\n\napp/views/layouts\n  Holds the template files for layouts to be used with views. This models the\n  common header/footer method of wrapping views. In your views, define a layout\n  using the <tt>layout :default</tt> and create a file named default.html.erb.\n  Inside default.html.erb, call <% yield %> to render the view using this\n  layout.\n\napp/helpers\n  Holds view helpers that should be named like weblogs_helper.rb. These are\n  generated for you automatically when using generators for controllers.\n  Helpers can be used to wrap functionality for your views into methods.\n\nconfig\n  Configuration files for the Rails environment, the routing map, the database,\n  and other dependencies.\n\ndb\n  Contains the database schema in schema.rb. db/migrate contains all the\n  sequence of Migrations for your schema.\n\ndoc\n  This directory is where your application documentation will be stored when\n  generated using <tt>rake doc:app</tt>\n\nlib\n  Application specific libraries. Basically, any kind of custom code that\n  doesn't belong under controllers, models, or helpers. This directory is in\n  the load path.\n\npublic\n  The directory available for the web server. Also contains the dispatchers and the\n  default HTML files. This should be set as the DOCUMENT_ROOT of your web\n  server.\n\nscript\n  Helper scripts for automation and generation.\n\ntest\n  Unit and functional tests along with fixtures. When using the rails generate\n  command, template test files will be generated for you and placed in this\n  directory.\n\nvendor\n  External libraries that the application depends on. Also includes the plugins\n  subdirectory. If the app has frozen rails, those gems also go here, under\n  vendor/rails/. This directory is in the load path.\n"
  },
  {
    "path": "test/dummy/Rakefile",
    "content": "#!/usr/bin/env rake\n# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path(\"config/application\", __dir__)\n\nDummy::Application.load_tasks\n"
  },
  {
    "path": "test/dummy/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// the compiled file.\n//\n// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD\n// GO AFTER THE REQUIRES BELOW.\n//\n//# require jquery\n//# require jquery_ujs\n//# require_tree .\n"
  },
  {
    "path": "test/dummy/app/assets/javascripts/articles.js",
    "content": "// Place all the behaviors and hooks related to the matching controller here.\n// All this logic will automatically be available in application.js.\n"
  },
  {
    "path": "test/dummy/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the top of the\n * compiled file, but it's generally better to create a new file per style scope.\n *\n *= require_self\n *= require_tree .\n*/\n"
  },
  {
    "path": "test/dummy/app/assets/stylesheets/articles.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "test/dummy/app/assets/stylesheets/welcome.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "test/dummy/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  protect_from_forgery with: :exception\nend\n"
  },
  {
    "path": "test/dummy/app/controllers/application_metal_controller.rb",
    "content": "class ApplicationMetalController < ActionController::Metal\n  MODULES = [\n    ActionController::Instrumentation,\n    AbstractController::Rendering,\n    ActionController::Rendering,\n    ActionController::Renderers::All\n    # Helpers::Controller\n  ].freeze\n\n  MODULES.each do |mod|\n    include mod\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/controllers/articles_controller.rb",
    "content": "class ArticlesController < ApplicationController\n  def new\n  end\n\n  def create\n    render plain: params[:article].inspect\n  end\n\n  def show\n    raise ActiveRecord::RecordNotFound\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/controllers/dashboard_controller.rb",
    "content": "class DashboardController < ApplicationMetalController\n  def show\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/controllers/welcome_controller.rb",
    "content": "class WelcomeController < ApplicationController\n  def index\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/dummy/app/helpers/articles_helper.rb",
    "content": "module ArticlesHelper\nend\n"
  },
  {
    "path": "test/dummy/app/helpers/welcome_helper.rb",
    "content": "module WelcomeHelper\nend\n"
  },
  {
    "path": "test/dummy/app/jobs/bad_job.rb",
    "content": "class BadJob\n  include Sidekiq::Worker\n\n  sidekiq_options retry: false\n\n  def perform\n    raise ArgumentError, \"This is a bad job\"\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/jobs/simple_job.rb",
    "content": "class SimpleJob\n  include Sidekiq::Worker\n\n  def perform\n    \"SimpleJob is working\"\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/mailers/.gitkeep",
    "content": ""
  },
  {
    "path": "test/dummy/app/models/.gitkeep",
    "content": ""
  },
  {
    "path": "test/dummy/app/models/sample.rb",
    "content": "class Sample < ActiveRecord::Base\nend\n"
  },
  {
    "path": "test/dummy/app/views/articles/new.html.erb",
    "content": "<h1>New Article</h1>\n\n<%= form_for :article, url: articles_path do |f| %>\n    <p>\n      <%= f.label :title %><br>\n      <%= f.text_field :title %>\n    </p>\n\n    <p>\n      <%= f.label :text %><br>\n      <%= f.text_area :text %>\n    </p>\n\n    <p>\n      <%= f.submit %>\n    </p>\n<% end %>\n"
  },
  {
    "path": "test/dummy/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Dummy</title>\n  <%= stylesheet_link_tag    \"application\", :media => \"all\" %>\n  <%= javascript_include_tag \"application\" %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/dummy/app/views/welcome/index.html.erb",
    "content": "<h1>Welcome#index</h1>\n<p>Find me in app/views/welcome/index.html.erb</p>\n"
  },
  {
    "path": "test/dummy/bin/bundle",
    "content": "#!/usr/bin/env ruby\nENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../Gemfile\", __dir__)\nload Gem.bin_path(\"bundler\", \"bundle\")\n"
  },
  {
    "path": "test/dummy/bin/puma",
    "content": "#!/usr/bin/env ruby\n#\n# This file was generated by Bundler.\n#\n# The application 'puma' is installed as part of a gem, and\n# this file is here to facilitate running it.\n#\n\nrequire \"pathname\"\nENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../../Gemfile\", Pathname.new(__FILE__).realpath)\n\nrequire \"rubygems\"\nrequire \"bundler/setup\"\n\nload Gem.bin_path(\"puma\", \"puma\")\n"
  },
  {
    "path": "test/dummy/bin/rails",
    "content": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path(\"../config/application\", __dir__)\nrequire_relative \"../config/boot\"\nrequire \"rails/commands\"\n"
  },
  {
    "path": "test/dummy/bin/rake",
    "content": "#!/usr/bin/env ruby\nrequire_relative \"../config/boot\"\nrequire \"rake\"\nRake.application.run\n"
  },
  {
    "path": "test/dummy/bin/setup",
    "content": "#!/usr/bin/env ruby\nrequire \"pathname\"\n\n# path to your application root.\nAPP_ROOT = Pathname.new File.expand_path(\"..\", __dir__)\n\nDir.chdir APP_ROOT do\n  # This script is a starting point to setup your application.\n  # Add necessary setup steps to this file:\n\n  puts \"== Installing dependencies ==\"\n  system \"gem install bundler --conservative\"\n  system \"bundle check || bundle install\"\n\n  # puts \"\\n== Copying sample files ==\"\n  # unless File.exist?(\"config/database.yml\")\n  #   system \"cp config/database.yml.sample config/database.yml\"\n  # end\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system \"rm -f log/*\"\n  system \"rm -rf tmp/cache\"\n\n  puts \"\\n== Restarting application server ==\"\n  system \"touch tmp/restart.txt\"\nend\n"
  },
  {
    "path": "test/dummy/config/application.rb",
    "content": "require File.expand_path(\"boot\", __dir__)\n\nrequire \"rails/all\"\n\nBundler.require\n\nmodule Dummy\n  class Application < Rails::Application\n    config.load_defaults \"#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}\" if config.respond_to?(:load_defaults)\n\n    # Configure sensitive parameters which will be filtered from the log file.\n    config.filter_parameters += [:password]\n    config.active_record.sqlite3.represent_boolean_as_integer = true if config.active_record.sqlite3\n    config.active_record.async_query_executor = :global_thread_pool if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7\n\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n    # Run \"rake -D time\" for a list of tasks for finding time zone names. Default is UTC.\n    # config.time_zone = 'Central Time (US & Canada)'\n\n    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]\n    # config.i18n.default_locale = :de\n\n    # Do not swallow errors in after_commit/after_rollback callbacks.\n    # config.active_record.raise_in_transactional_callbacks = true\n\n    config.semantic_logger.default_level = :trace\n    # Warning: Set to :error or higher in production to avoid performance issues.\n    config.semantic_logger.backtrace_level = :trace\n\n    # Test out Amazing Print\n    config.rails_semantic_logger.ap_options = {multiline: false, ruby19_syntax: true}\n  end\nend\n"
  },
  {
    "path": "test/dummy/config/boot.rb",
    "content": "require \"rubygems\"\ngemfile = File.expand_path(\"../../../Gemfile\", __dir__)\n\nif File.exist?(gemfile)\n  ENV[\"BUNDLE_GEMFILE\"] = gemfile\n  require \"bundler\"\n  Bundler.setup\nend\n\n$LOAD_PATH.unshift File.expand_path(\"../../../lib\", __dir__)\n"
  },
  {
    "path": "test/dummy/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/dummy/config/environment.rb",
    "content": "# Load the rails application\nrequire File.expand_path(\"application\", __dir__)\n\n# Initialize the rails application\nDummy::Application.initialize!\n"
  },
  {
    "path": "test/dummy/config/environments/development.rb",
    "content": "Dummy::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes                     = false\n\n  # Do not eager load code on boot.\n  config.eager_load                        = false\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation        = :log\n\n  # Raise an error on page load if there are pending migrations.\n  # config.active_record.migration_error = :page_load\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  # This option may cause significant delays in view rendering with a large\n  # number of complex assets.\n  config.assets.debug                      = true\n\n  # Asset digests allow you to set far-future HTTP expiration dates on all assets,\n  # yet still be able to expire them through the digest params.\n  config.assets.digest                     = true\n\n  # Adds additional error checking when serving assets at runtime.\n  # Checks for improperly declared sprockets dependencies.\n  # Raises helpful error messages.\n  config.assets.raise_runtime_errors       = true\n\n  # Raises error for missing translations\n  # config.action_view.raise_on_missing_translations = true\n\n  config.rails_semantic_logger.quiet_assets         = true\n  config.rails_semantic_logger.ap_options           = {multiline: true}\nend\n"
  },
  {
    "path": "test/dummy/config/environments/production.rb",
    "content": "Dummy::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # Full error reports are disabled and caching is turned on\n  config.consider_all_requests_local = false\n  config.perform_caching             = true\n  config.cache_classes               = true\n  config.eager_load                  = true\n\n  # Disable Rails's static asset server (Apache or nginx could already do this)\n  config.serve_static_files          = true\n\n  # SSL is handled by the load balancer\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = false\n\n  # See everything in the log (default is :info)\n  config.log_level                   = :info\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation can not be found)\n  config.i18n.fallbacks              = true\n\n  # Send deprecation notices to registered listeners\n  config.active_support.deprecation  = :notify\n\n  # Disable colorized logging\n  # config.colorize_logging = false\nend\n"
  },
  {
    "path": "test/dummy/config/environments/test.rb",
    "content": "Dummy::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes                              = true\n\n  # Configure static asset server for tests with Cache-Control for performance\n  # config.serve_static_assets = true\n\n  # Log error messages when you accidentally call methods on nil\n  config.whiny_nils                                 = true\n  config.eager_load                                 = false\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local                = true\n  config.action_controller.perform_caching          = false\n\n  # Raise exceptions instead of rendering exception templates\n  config.action_dispatch.show_exceptions = Rails::VERSION::MAJOR >= 7 ? :none : false\n\n  # Disable request forgery protection in test environment\n  config.action_controller.allow_forgery_protection = false\n\n  # Print deprecation notices to the stderr\n  config.active_support.deprecation                 = :stderr\n\n  config.active_support.test_order                  = :random\nend\n"
  },
  {
    "path": "test/dummy/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/dummy/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format\n# (all these examples are active by default):\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n#\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/dummy/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/dummy/config/initializers/payload_collector.rb",
    "content": "ActiveSupport::Notifications.subscribe \"process_action.action_controller\" do |*args|\n  event = ActiveSupport::Notifications::Event.new(*args)\n  PayloadCollector.append(event.payload)\nend\n"
  },
  {
    "path": "test/dummy/config/initializers/secret_token.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\nDummy::Application.config.secret_token = \"afd0f2e89c2a660f839bfb8e982358b6748c7fcd7c61c98b1edfc164539fae5beafece2906858801a4c7f5bbcd2f2e606db2a766630e1b0b1468f50ad6025630\"\n"
  },
  {
    "path": "test/dummy/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.session_store :cookie_store, key: \"_dummy_session\"\n"
  },
  {
    "path": "test/dummy/config/initializers/sidekiq.rb",
    "content": "# In tests we force Sidekiq into thinking it is running as a server,\n# so it creates a stdout logger. Remove it here:\nRails.application.config.after_initialize do\n  SemanticLogger.appenders.delete_if { |appender| appender.is_a?(SemanticLogger::Appender::IO) } if Rails.env.test?\nend\n"
  },
  {
    "path": "test/dummy/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#  self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/dummy/config/locales/en.yml",
    "content": "# Sample localization file for English. Add more files in this directory for other locales.\n# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/dummy/config/routes.rb",
    "content": "Dummy::Application.routes.draw do\n  get \"welcome/index\"\n\n  resources :articles\n\n  resource :dashboard, controller: :dashboard\n\n  root \"welcome#index\"\nend\n"
  },
  {
    "path": "test/dummy/config/secrets.yml",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rake secret` to generate a secure secret key.\n\n# Make sure the secrets in this file are kept private\n# if you're sharing your code publicly.\n\ndevelopment:\n  secret_key_base: 72c4f52428cb36cd0afb2cf6e36a8a67fa39f8fd2396f533b85e42441feafdcdcac4f7629f3e1c5b846b1c452ad6e3ecfe368e913b71cd147d4536b35406b2fd\n\ntest:\n  secret_key_base: 29dc974bc32244b62fd69259cd4f9ff7223a4cfe9fe40ee948dd1e64a99cd6bbef4a5d75f1b106ef50fa750613542e52e21d7fd67695356cd529264f614c1c3d\n\n# Do not keep production secrets in the repository,\n# instead read values from the environment.\nproduction:\n  secret_key_base: <%= ENV[\"SECRET_KEY_BASE\"] %>\n"
  },
  {
    "path": "test/dummy/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire File.expand_path(\"config/environment\", __dir__)\nrun Dummy::Application\n"
  },
  {
    "path": "test/dummy/db/migrate/20170525020551_create_samples.rb",
    "content": "class CreateSamples < ActiveRecord::Migration\n  def change\n    create_table :samples do |t|\n      t.string :name\n      t.integer :age\n      t.date :joined\n\n      t.timestamps null: false\n    end\n  end\nend\n"
  },
  {
    "path": "test/dummy/db/schema.rb",
    "content": "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# Note that this schema.rb definition is the authoritative source for your\n# database schema. If you need to create the application database on another\n# system, you should be using db:schema:load, not running all the migrations\n# from scratch. The latter is a flawed and unsustainable approach (the more migrations\n# you'll amass, the slower it'll run and the greater likelihood for issues).\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema.define(version: 20_170_525_020_551) do\n  create_table \"samples\", force: :cascade do |t|\n    t.string   \"name\"\n    t.integer  \"age\"\n    t.date     \"joined\"\n    t.datetime \"created_at\", null: false\n    t.datetime \"updated_at\", null: false\n  end\nend\n"
  },
  {
    "path": "test/dummy/lib/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "test/dummy/log/.gitkeep",
    "content": ""
  },
  {
    "path": "test/dummy/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/dummy/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/dummy/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/dummy/script/rails",
    "content": "#!/usr/bin/env ruby\n# This command will automatically be run when you run \"rails\" with Rails 3 gems installed from the root of your application.\n\nAPP_PATH = File.expand_path(\"../config/application\", __dir__)\nrequire File.expand_path(\"../config/boot\", __dir__)\nrequire \"rails/commands\"\n"
  },
  {
    "path": "test/dummy/test/fixtures/samples.yml",
    "content": "# Read about fixtures at\n# http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\none:\n  name: MyString\n  age: 1\n  joined: 2017-05-24\n\ntwo:\n  name: MyString\n  age: 1\n  joined: 2017-05-24\n"
  },
  {
    "path": "test/payload_collector.rb",
    "content": "class PayloadCollector\n  class << self\n    def wrap\n      @store = true\n      yield\n    ensure\n      @store = false\n    end\n\n    def append(payload)\n      data.append(payload) if @store\n    end\n\n    def last\n      data.last\n    end\n\n    def flush\n      @data = []\n    end\n\n    private\n\n    def data\n      @data ||= []\n    end\n  end\nend\n"
  },
  {
    "path": "test/rails_test.rb",
    "content": "require_relative \"test_helper\"\n\nclass RailsTest < Minitest::Test\n  describe \"Rails\" do\n    describe \".logger\" do\n      it \"replaces the Rails logger\" do\n        assert_kind_of SemanticLogger::Logger, Rails.logger\n      end\n\n      it \"uses the colorized formatter\" do\n        assert_kind_of SemanticLogger::Formatters::Color, SemanticLogger.appenders.first.formatter\n      end\n\n      it \"is compatible with Rails logger\" do\n        assert_nil Rails.logger.formatter\n        Rails.logger.formatter = \"blah\"\n        assert_equal \"blah\", Rails.logger.formatter\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/sidekiq_test.rb",
    "content": "require_relative \"test_helper\"\n\nclass SidekiqTest < Minitest::Test\n  # Cannot use inline testing since it bypasses the Sidekiq logging calls.\n  describe Sidekiq::Worker do\n    let(:job) { SimpleJob }\n    let(:args) { [] }\n\n    describe \"#logger\" do\n      it \"has its own logger with the same name as the job\" do\n        assert_kind_of SemanticLogger::Logger, SimpleJob.logger\n        assert_kind_of SemanticLogger::Logger, job.logger\n        assert_equal job.logger.name, job.name\n        refute_same Sidekiq.logger, job.logger\n      end\n    end\n\n    describe \"#perform\" do\n      let(:config) { Sidekiq.default_configuration }\n      let(:msg) { Sidekiq.dump_json({\"class\" => job.to_s, \"args\" => args, \"enqueued_at\" => 1.minute.ago}) }\n      let(:uow) { Sidekiq::BasicFetch::UnitOfWork.new(\"queue:default\", msg) }\n      if Sidekiq::VERSION.to_i == 6 && Sidekiq::VERSION.to_f < 6.5\n        let(:processor) do\n          mgr          = Minitest::Mock.new\n          opts         = Sidekiq.options\n          opts[:fetch] = Sidekiq::BasicFetch.new(opts)\n          Sidekiq::Processor.new(mgr, opts)\n        end\n      elsif Sidekiq::VERSION.to_i == 6\n        let(:processor) do\n          config         = Sidekiq\n          config[:fetch] = Sidekiq::BasicFetch.new(config)\n          Sidekiq::Processor.new(config) { |*args| }\n        end\n      elsif Sidekiq::VERSION.to_i < 7\n        let(:processor) do\n          opts = Sidekiq.options\n          mgr  = Minitest::Mock.new\n          mgr.expect(:options, opts)\n          mgr.expect(:options, opts)\n          mgr.expect(:options, opts)\n          Sidekiq::Processor.new(mgr)\n        end\n      else\n        let(:processor) { Sidekiq::Processor.new(config.default_capsule) { |*args| } }\n      end\n\n      it \"a simple job\" do\n        # SimpleJob.perform_async\n        messages = semantic_logger_events do\n          processor.send(:process, uow)\n        end\n\n        assert_equal 2, messages.count, -> { messages.collect(&:to_h).ai }\n\n        assert_semantic_logger_event(\n          messages[0],\n          level:      :info,\n          name:       \"SimpleJob\",\n          message:    \"Start #perform\",\n          metric:     \"sidekiq.queue.latency\",\n          named_tags: {jid: nil, queue: \"default\"}\n        )\n        assert messages[0].metric_amount.is_a?(Float)\n\n        assert_semantic_logger_event(\n          messages[1],\n          level:      :info,\n          name:       \"SimpleJob\",\n          message:    \"Completed #perform\",\n          metric:     \"sidekiq.job.perform\",\n          named_tags: {jid: nil, queue: \"default\"}\n        )\n        assert messages[1].duration.is_a?(Float)\n      end\n\n      describe \"with Bad Job\" do\n        let(:job) { BadJob }\n\n        it \"a job that raises an exception\" do\n          # BadJob.perform_async\n          messages = semantic_logger_events do\n            assert_raises ArgumentError do\n              processor.send(:process, uow)\n            end\n          end\n\n          assert_equal 3, messages.count, -> { messages.collect(&:to_h).ai }\n\n          assert_semantic_logger_event(\n            messages[0],\n            level:      :info,\n            name:       \"BadJob\",\n            message:    \"Start #perform\",\n            metric:     \"sidekiq.queue.latency\",\n            named_tags: {jid: nil, queue: \"default\"},\n            exception:  :nil\n          )\n          assert messages[0].metric_amount.is_a?(Float)\n\n          assert_semantic_logger_event(\n            messages[1],\n            level:      :error,\n            name:       \"BadJob\",\n            message:    \"Completed #perform\",\n            metric:     \"sidekiq.job.perform\",\n            named_tags: {jid: nil, queue: \"default\"},\n            exception:  ArgumentError\n          )\n          assert messages[1].duration.is_a?(Float)\n\n          assert_semantic_logger_event(\n            messages[2],\n            level:            :warn,\n            name:             \"BadJob\",\n            message:          \"Job raised exception\",\n            payload_includes: {context: \"Job raised exception\"},\n            exception:        :nil\n          )\n          assert_equal messages[2].payload[:job][\"class\"], \"BadJob\"\n          assert_equal messages[2].payload[:job][\"args\"], []\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/test_helper.rb",
    "content": "ENV[\"RAILS_ENV\"] ||= \"test\"\nENV[\"DISABLE_DATABASE_ENVIRONMENT_CHECK\"] = \"1\"\n# Load first so Sidekiq thinks it is running as a server instance\nrequire \"sidekiq/cli\"\nif defined?(Sidekiq::DEFAULT_ERROR_HANDLER)\n  # Set by Sidekiq CLI at startup\n  Sidekiq.options[:error_handlers] << Sidekiq::DEFAULT_ERROR_HANDLER\nend\nrequire_relative \"dummy/config/environment\"\n\nrequire \"rails/test_help\"\nrequire \"minitest/autorun\"\nrequire \"minitest/rails\"\nrequire \"amazing_print\"\n\nrequire_relative \"payload_collector\"\n\n# Include the complete backtrace?\nMinitest.backtrace_filter = Minitest::BacktraceFilter.new if ENV[\"BACKTRACE\"].present?\nRails.backtrace_cleaner.remove_silencers!\n\n# Add Semantic Logger helpers for Minitest\nMinitest::Test.include SemanticLogger::Test::Minitest\n\nActionMailer::Base.delivery_method = :test\n\ndef filter_params_setting(value, user_defined_params, &block)\n  original_value = Rails.configuration.filter_parameters\n  Rails.configuration.filter_parameters = user_defined_params\n  block.call\nensure\n  Rails.configuration.filter_parameters = original_value\nend\n\ndef filter_params_regex_setting(value, user_defined_params, &block)\n  original_value = Rails.configuration.filter_parameters\n\n  Rails.configuration.filter_parameters += user_defined_params\n\n  filter_params_regex = Rails.configuration.filter_parameters.map do |key|\n    \"(?i:#{key})\"\n  end.join(\"|\")\n\n  Rails.configuration.filter_parameters = [/(?-mix:#{filter_params_regex})/]\n\n  block.call\nensure\n  Rails.configuration.filter_parameters = original_value\nend\n"
  }
]