You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
3.5 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. module Semantic
  2. # use the Zeitwerk autoloader to reattach_appender for development autoreloading feature
  3. class DevLoader
  4. def initialize(session_key)
  5. @session_key = session_key
  6. force_preload_module
  7. once_and_reload do
  8. append_ansi_formatter
  9. register_log_subscriber
  10. end
  11. # FIXME: proper unsubscribe!!!
  12. # RailsSemanticLogger::ActionController::LogSubscriber.logger.level = :fatal
  13. #
  14. # RailsSemanticLogger.swap_subscriber(
  15. # RailsSemanticLogger::ActionController::LogSubscriber,
  16. # @log_subscriber, # attach missing!!!
  17. # :action_controller
  18. # )
  19. # ActiveSupport::LogSubscriber.subscribers.each do |sub|
  20. # puts "subscriber #{sub.pattern}"
  21. # end
  22. # RailsSemanticLogger.swap_subscriber(RailsSemanticLogger::ActionController::LogSubscriber,
  23. # @log_subscriber, :action_controller)
  24. end
  25. private
  26. def once_and_reload(&)
  27. yield
  28. Rails.autoloaders.main.on_load('ApplicationController', &)
  29. end
  30. def force_preload_module
  31. self.class.module_parent.constants.each { |const| self.class.module_parent.const_get(const) }
  32. end
  33. def append_ansi_formatter
  34. SemanticLogger.clear_appenders!
  35. formatter = Semantic::AnsiFormatter.new
  36. SemanticLogger.add_appender(io: $stdout,
  37. formatter:,
  38. filter: ->(log) { !formatter.reject(log) })
  39. end
  40. def register_log_subscriber
  41. reset_subscribers
  42. register_to_action_controller(:start_processing)
  43. register_to_action_controller(:process_action, :finish_processing)
  44. register_to_action_controller(:redirect_to)
  45. %i[send_file send_data halted_callback unpermitted_parameters send_stream write_fragment
  46. read_fragment expire_fragment exist_fragment?].each do |hook|
  47. register_to_action_controller(hook, :any_hook)
  48. end
  49. end
  50. def register_to_action_controller(hook, method = hook)
  51. @subscribers << ActiveSupport::Notifications.subscribe("#{hook}.action_controller") do |event|
  52. @log_subscriber.send(method, event)
  53. end
  54. end
  55. def subscriber_patterns(subscriber)
  56. subscriber.patterns.respond_to?(:keys) ? subscriber.patterns.keys : subscriber.patterns
  57. end
  58. def unattach(subscriber, pattern)
  59. subscriber_patterns(subscriber).each do |sub_pattern|
  60. ActiveSupport::Notifications.notifier.listeners_for(sub_pattern).each do |sub|
  61. next unless sub.instance_variable_get(:@delegate) == subscriber
  62. next unless pattern.match(sub_pattern)
  63. # puts "FOUND subscriber=#{subscriber} for sub_pattern=#{sub_pattern} with logger #{subscriber.logger.name}"
  64. # puts 'remove notification then log_subscriber'
  65. ActiveSupport::Notifications.unsubscribe(sub)
  66. ActiveSupport::LogSubscriber.subscribers.delete(subscriber)
  67. end
  68. end
  69. end
  70. # pattern could be either a string 'start_processing.action_controller' or a regex /\.action_controller$/
  71. def clear_subscribers(pattern)
  72. ActiveSupport::LogSubscriber.subscribers.each { |sub| unattach(sub, pattern) }
  73. end
  74. def reset_subscribers
  75. clear_subscribers(/\.action_controller$/)
  76. if defined?(@subscribers)
  77. @subscribers.each { |sub| ActiveSupport::Notifications.unsubscribe(sub) }
  78. @subscribers.clear
  79. else
  80. @subscribers = []
  81. end
  82. @log_subscriber = Semantic::LogSubscriber.new(@session_key)
  83. end
  84. end
  85. end