<commit_before>require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/blank'
require 'logger'
require 'active_support/logger'

module RooOnRails
  # Wraps any standard logger to provide context, similar to `ActiveSupport::TaggedLogging`
  # but with key/value pairs that are appended to the end of the text.
  #
  #   logger = RooOnRails::ContextLogging.new(Logger.new($stdout))
  #   logger.context(a: 1, b: 2) { logger.info 'foo' }                     # Logs "foo -- a=1 b=2"
  #   logger.context(a: 1) { logger.context(b: 2) { logger.info('foo') } } # Logs "foo -- a=1 b=2"
  #
  # You can also use `push_context` and `pop_context` in before/after blocks.
  #
  #   class ApplicationController < ActionController::Base
  #     before_action { logger.push_context(user_id: current_user.id) }
  #     after_action { logger.pop_context }
  #   end
  module ContextLogging
    module Formatter
      def call(severity, timestamp, progname, msg)
        super(severity, timestamp, progname, "#{msg}#{context_text}")
      end

      def context(**context)
        push_context(**context)
        yield self
      ensure
        pop_context
      end

      def push_context(**context)
        current_context.push(context)
      end

      def pop_context
        current_context.pop
      end

      def clear_context!
        current_context.clear
      end

      def current_context
        # We use our object ID here to avoid conflicting with other instances
        thread_key = @thread_key ||= "roo_on_rails:logging_context:#{object_id}".freeze
        Thread.current[thread_key] ||= []
      end

      private

      def context_text
        context = current_context
        return nil unless context.any?

        merged_context = context.each_with_object({}) { |ctx, acc| acc.merge!(ctx) }
        ' -- ' + merged_context.map { |k, v| "#{k}=#{v}" }.join(' ')
      end
    end

    def self.new(logger)
      # Ensure we set a default formatter so we aren't extending nil!
      logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
      logger.formatter.extend(Formatter)
      logger.extend(self)
    end

    delegate :push_context, :pop_context, :clear_context!, to: :formatter

    def context(**context)
      formatter.context(**context) { yield self }
    end

    def flush<commit_msg>Rename context method, allow immediate log method chaining<commit_after>require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/blank'
require 'logger'
require 'active_support/logger'

module RooOnRails
  # Wraps any standard logger to provide context, similar to `ActiveSupport::TaggedLogging`
  # but with key/value pairs that are appended to the end of the text.
  #
  #   logger = RooOnRails::ContextLogging.new(Logger.new($stdout))
  #   logger.with(a: 1, b: 2) { logger.info 'Stuff' }                  # Logs "Stuff -- a=1 b=2"
  #   logger.with(a: 1) { logger.with(b: 2) { logger.info('Stuff') } } # Logs "Stuff -- a=1 b=2"
  #
  # The above methods persist the context in thread-local storage so it will be attached to
  # anything done within the scope of the block, even in different methods. If you just need
  # to log something immediately you can use the short version.
  #
  #   logger.with(a: 1, b: 2).info('Stuff')                            # Logs "Stuff -- a=1 b=2"
  module ContextLogging
    module Formatter
      def call(severity, timestamp, progname, msg)
        super(severity, timestamp, progname, "#{msg}#{context_text}")
      end

      def with(**context)
        current_context.push(context)
        yield self
      ensure
        current_context.pop
      end

      def clear_context!
        current_context.clear
      end

      def current_context
        # We use our object ID here to avoid conflicting with other instances
        thread_key = @thread_key ||= "roo_on_rails:logging_context:#{object_id}".freeze
        Thread.current[thread_key] ||= []
      end

      private

      def context_text
        context = current_context
        return nil unless context.any?

        merged_context = context.each_with_object({}) { |ctx, acc| acc.merge!(ctx) }
        ' -- ' + merged_context.map { |k, v| "#{k}=#{v}" }.join(' ')
      end
    end

    class LoggerProxy
      def initialize(logger, context)
        @logger = logger
        @context = context
      end

      %i[debug info warn error fatal unknown].each do |severity|
        define_method severity do |message|
          @logger.with(**@context) { @logger.public_send(severity, message) }
        end
      end
    end

    def self.new(logger)
      # Ensure we set a default formatter so we aren't extending nil!
      logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
      logger.formatter.extend(Formatter)
      logger.extend(self)
    end

    delegate :clear_context!, to: :formatter

    def with(**context)
      if block_given?
        formatter.with(**context) { yield self }
      else
        LoggerProxy.new(self, context)
      end
    end

    def flush
{'fraction_non_alphanumeric': 0.1039905641832121, 'fraction_numerical': 0.003931590328287792, 'mean_word_length': 2.6869565217391305, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5377457', 'n_tokens_mistral': 1573, 'n_tokens_neox': 1506, 'n_words': 418}