bugsnag 5.5.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +33 -11
  3. data/CHANGELOG.md +23 -0
  4. data/Gemfile +20 -0
  5. data/Rakefile +19 -12
  6. data/UPGRADING.md +58 -0
  7. data/VERSION +1 -1
  8. data/bugsnag.gemspec +0 -9
  9. data/lib/bugsnag.rb +64 -86
  10. data/lib/bugsnag/configuration.rb +42 -26
  11. data/lib/bugsnag/delivery.rb +9 -0
  12. data/lib/bugsnag/delivery/synchronous.rb +3 -5
  13. data/lib/bugsnag/delivery/thread_queue.rb +4 -2
  14. data/lib/bugsnag/helpers.rb +5 -16
  15. data/lib/bugsnag/{delayed_job.rb → integrations/delayed_job.rb} +15 -7
  16. data/lib/bugsnag/{mailman.rb → integrations/mailman.rb} +13 -11
  17. data/lib/bugsnag/{que.rb → integrations/que.rb} +11 -11
  18. data/lib/bugsnag/{rack.rb → integrations/rack.rb} +22 -23
  19. data/lib/bugsnag/{rails → integrations/rails}/active_record_rescue.rb +9 -7
  20. data/lib/bugsnag/{rails → integrations/rails}/controller_methods.rb +0 -9
  21. data/lib/bugsnag/{railtie.rb → integrations/railtie.rb} +24 -21
  22. data/lib/bugsnag/{rake.rb → integrations/rake.rb} +12 -9
  23. data/lib/bugsnag/{resque.rb → integrations/resque.rb} +12 -9
  24. data/lib/bugsnag/integrations/shoryuken.rb +49 -0
  25. data/lib/bugsnag/{sidekiq.rb → integrations/sidekiq.rb} +13 -9
  26. data/lib/bugsnag/middleware/callbacks.rb +4 -8
  27. data/lib/bugsnag/middleware/classify_error.rb +7 -13
  28. data/lib/bugsnag/middleware/clearance_user.rb +8 -8
  29. data/lib/bugsnag/middleware/exception_meta_data.rb +34 -0
  30. data/lib/bugsnag/middleware/ignore_error_class.rb +21 -0
  31. data/lib/bugsnag/middleware/mailman.rb +4 -4
  32. data/lib/bugsnag/middleware/rack_request.rb +13 -13
  33. data/lib/bugsnag/middleware/rails3_request.rb +10 -10
  34. data/lib/bugsnag/middleware/rake.rb +5 -5
  35. data/lib/bugsnag/middleware/sidekiq.rb +5 -5
  36. data/lib/bugsnag/middleware/suggestion_data.rb +30 -0
  37. data/lib/bugsnag/middleware/warden_user.rb +6 -6
  38. data/lib/bugsnag/middleware_stack.rb +5 -5
  39. data/lib/bugsnag/report.rb +187 -0
  40. data/lib/bugsnag/stacktrace.rb +113 -0
  41. data/lib/bugsnag/tasks/bugsnag.rake +2 -70
  42. data/spec/cleaner_spec.rb +6 -0
  43. data/spec/configuration_spec.rb +1 -1
  44. data/spec/fixtures/middleware/internal_info_setter.rb +3 -3
  45. data/spec/fixtures/middleware/public_info_setter.rb +3 -3
  46. data/spec/fixtures/tasks/Rakefile +2 -3
  47. data/spec/integration_spec.rb +5 -20
  48. data/spec/{delayed_job_spec.rb → integrations/delayed_job_spec.rb} +0 -0
  49. data/spec/integrations/sidekiq_spec.rb +34 -0
  50. data/spec/middleware_spec.rb +108 -35
  51. data/spec/rack_spec.rb +1 -1
  52. data/spec/{notification_spec.rb → report_spec.rb} +226 -209
  53. data/spec/spec_helper.rb +18 -0
  54. data/spec/{code_spec.rb → stacktrace_spec.rb} +1 -1
  55. metadata +23 -139
  56. data/.document +0 -5
  57. data/lib/bugsnag/capistrano.rb +0 -7
  58. data/lib/bugsnag/capistrano2.rb +0 -32
  59. data/lib/bugsnag/delay/resque.rb +0 -21
  60. data/lib/bugsnag/deploy.rb +0 -35
  61. data/lib/bugsnag/middleware/rails2_request.rb +0 -52
  62. data/lib/bugsnag/notification.rb +0 -506
  63. data/lib/bugsnag/rails.rb +0 -70
  64. data/lib/bugsnag/rails/action_controller_rescue.rb +0 -74
  65. data/lib/bugsnag/shoryuken.rb +0 -41
  66. data/lib/bugsnag/tasks/bugsnag.cap +0 -48
  67. data/rails/init.rb +0 -7
@@ -1,10 +1,19 @@
1
1
  module Bugsnag
2
2
  module Delivery
3
3
  class << self
4
+ # Add a delivery method to the list of supported methods. Any registered
5
+ # method can then be used by name in Configuration.
6
+ #
7
+ # require 'bugsnag'
8
+ # Bugsnag::Delivery.register(:my_delivery_queue, MyDeliveryQueue)
9
+ # Bugsnag.configure do |config|
10
+ # config.delivery_method = :my_delivery_queue
11
+ # end
4
12
  def register(name, delivery_method)
5
13
  delivery_methods[name.to_sym] = delivery_method
6
14
  end
7
15
 
16
+ # Reference a delivery method by name
8
17
  def [](name)
9
18
  delivery_methods[name.to_sym]
10
19
  end
@@ -10,13 +10,13 @@ module Bugsnag
10
10
  def deliver(url, body, configuration)
11
11
  begin
12
12
  response = request(url, body, configuration)
13
- Bugsnag.debug("Notification to #{url} finished, response was #{response.code}, payload was #{body}")
13
+ configuration.debug("Notification to #{url} finished, response was #{response.code}, payload was #{body}")
14
14
  rescue StandardError => e
15
15
  # KLUDGE: Since we don't re-raise http exceptions, this breaks rspec
16
16
  raise if e.class.to_s == "RSpec::Expectations::ExpectationNotMetError"
17
17
 
18
- Bugsnag.warn("Notification to #{url} failed, #{e.inspect}")
19
- Bugsnag.warn(e.backtrace)
18
+ configuration.warn("Notification to #{url} failed, #{e.inspect}")
19
+ configuration.warn(e.backtrace)
20
20
  end
21
21
  end
22
22
 
@@ -36,8 +36,6 @@ module Bugsnag
36
36
 
37
37
  if uri.scheme == "https"
38
38
  http.use_ssl = true
39
- # the default in 1.9+, but required for 1.8
40
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
41
39
  http.ca_file = configuration.ca_file if configuration.ca_file
42
40
  end
43
41
 
@@ -9,10 +9,12 @@ module Bugsnag
9
9
 
10
10
  class << self
11
11
  def deliver(url, body, configuration)
12
+ @configuration = configuration
13
+
12
14
  start_once!
13
15
 
14
16
  if @queue.length > MAX_OUTSTANDING_REQUESTS
15
- Bugsnag.warn("Dropping notification, #{@queue.length} outstanding requests")
17
+ @configuration.warn("Dropping notification, #{@queue.length} outstanding requests")
16
18
  return
17
19
  end
18
20
 
@@ -38,7 +40,7 @@ module Bugsnag
38
40
  end
39
41
 
40
42
  at_exit do
41
- Bugsnag.warn("Waiting for #{@queue.length} outstanding request(s)") unless @queue.empty?
43
+ @configuration.warn("Waiting for #{@queue.length} outstanding request(s)") unless @queue.empty?
42
44
  @queue.push STOP
43
45
  worker_thread.join
44
46
  end
@@ -1,12 +1,12 @@
1
1
  require 'uri'
2
- require 'set' unless defined?(Set)
3
- require 'json' unless defined?(JSON)
2
+ require 'set'
3
+ require 'json'
4
4
 
5
5
 
6
6
  module Bugsnag
7
7
  module Helpers
8
8
  MAX_STRING_LENGTH = 3072
9
- MAX_PAYLOAD_LENGTH = 128000
9
+ MAX_PAYLOAD_LENGTH = 256000
10
10
  MAX_ARRAY_LENGTH = 40
11
11
  RAW_DATA_TYPES = [Numeric, TrueClass, FalseClass]
12
12
 
@@ -23,16 +23,9 @@ module Bugsnag
23
23
  remove_metadata_from_events(reduced_value)
24
24
  end
25
25
 
26
- def self.flatten_meta_data(overrides)
27
- return nil unless overrides
26
+ private
28
27
 
29
- meta_data = overrides.delete(:meta_data)
30
- if meta_data.is_a?(Hash)
31
- overrides.merge(meta_data)
32
- else
33
- overrides
34
- end
35
- end
28
+ TRUNCATION_INFO = '[TRUNCATED]'
36
29
 
37
30
  # Check if a value is a raw type which should not be trimmed, truncated
38
31
  # or converted to a string
@@ -40,10 +33,6 @@ module Bugsnag
40
33
  RAW_DATA_TYPES.detect {|klass| value.is_a?(klass)} != nil
41
34
  end
42
35
 
43
- private
44
-
45
- TRUNCATION_INFO = '[TRUNCATED]'
46
-
47
36
  # Shorten array until it fits within the payload size limit when serialized
48
37
  def self.truncate_array(array)
49
38
  return [] unless array.respond_to?(:slice)
@@ -8,9 +8,12 @@ end
8
8
  unless defined? Delayed::Plugins::Bugsnag
9
9
  module Delayed
10
10
  module Plugins
11
+ class Bugsnag < Plugin
11
12
 
13
+ FRAMEWORK_ATTRIBUTES = {
14
+ :framework => "DelayedJob"
15
+ }
12
16
 
13
- class Bugsnag < Plugin
14
17
  module Notify
15
18
  def error(job, error)
16
19
  overrides = {
@@ -19,11 +22,9 @@ unless defined? Delayed::Plugins::Bugsnag
19
22
  :id => job.id,
20
23
  },
21
24
  :severity_reason => {
22
- :type => ::Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
23
- :attributes => {
24
- :framework => "DelayedJob"
25
- }
26
- }
25
+ :type => ::Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
26
+ :attributes => FRAMEWORK_ATTRIBUTES,
27
+ },
27
28
  }
28
29
  if job.respond_to?(:queue) && (queue = job.queue)
29
30
  overrides[:job][:queue] = queue
@@ -50,7 +51,14 @@ unless defined? Delayed::Plugins::Bugsnag
50
51
  overrides[:job][:payload] = p
51
52
  end
52
53
 
53
- ::Bugsnag.auto_notify(error, overrides)
54
+ ::Bugsnag.notify(error, true) do |report|
55
+ report.severity = "error"
56
+ report.severity_reason = {
57
+ :type => ::Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
58
+ :attributes => FRAMEWORK_ATTRIBUTES
59
+ }
60
+ report.meta_data.merge! overrides
61
+ end
54
62
 
55
63
  super if defined?(super)
56
64
  end
@@ -2,6 +2,11 @@ require 'mailman'
2
2
 
3
3
  module Bugsnag
4
4
  class Mailman
5
+
6
+ FRAMEWORK_ATTRIBUTES = {
7
+ :framework => "Mailman"
8
+ }
9
+
5
10
  def initialize
6
11
  Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Mailman)
7
12
  Bugsnag.configuration.app_type = "mailman"
@@ -9,23 +14,20 @@ module Bugsnag
9
14
 
10
15
  def call(mail)
11
16
  begin
12
-
13
- Bugsnag.set_request_data :mailman_msg, mail.to_s
14
-
17
+ Bugsnag.configuration.set_request_data :mailman_msg, mail.to_s
15
18
  yield
16
19
  rescue Exception => ex
17
20
  raise ex if [Interrupt, SystemExit, SignalException].include? ex.class
18
- Bugsnag.auto_notify(ex, {
19
- :severity_reason => {
20
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
21
- :attributes => {
22
- :framework => "Mailman"
23
- }
21
+ Bugsnag.notify(ex, true) do |report|
22
+ report.severity = "error"
23
+ report.severity_reason = {
24
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
25
+ :attributes => FRAMEWORK_ATTRIBUTES
24
26
  }
25
- })
27
+ end
26
28
  raise
27
29
  ensure
28
- Bugsnag.clear_request_data
30
+ Bugsnag.configuration.clear_request_data
29
31
  end
30
32
  end
31
33
  end
@@ -3,14 +3,7 @@ if defined?(::Que)
3
3
  begin
4
4
  job = job.dup # Make sure the original job object is not mutated.
5
5
 
6
- Bugsnag.auto_notify(error, {
7
- :severity_reason => {
8
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
9
- :attributes => {
10
- :framework => "Que"
11
- }
12
- }
13
- }) do |notification|
6
+ Bugsnag.notify(error, true) do |report|
14
7
  job[:error_count] += 1
15
8
 
16
9
  # If the job was scheduled using ActiveJob then unwrap the job details for clarity:
@@ -25,11 +18,18 @@ if defined?(::Que)
25
18
  job.merge!(wrapper_job_class: job[:job_class], wrapper_job_id: job[:job_id]).merge!(wrapped_job)
26
19
  end
27
20
 
28
- notification.add_tab(:job, job)
21
+ report.add_tab(:job, job)
22
+ report.severity = 'error'
23
+ report.severity_reason = {
24
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
25
+ :attributes => {
26
+ :framework => 'Que'
27
+ }
28
+ }
29
29
  end
30
30
  rescue => e
31
31
  # Que supresses errors raised by its error handler to avoid killing the worker. Log them somewhere:
32
- Bugsnag.warn("Failed to notify Bugsnag of error in Que job (#{e.class}): #{e.message} \n#{e.backtrace[0..9].join("\n")}")
32
+ Bugsnag.configuration.warn("Failed to notify Bugsnag of error in Que job (#{e.class}): #{e.message} \n#{e.backtrace[0..9].join("\n")}")
33
33
  raise
34
34
  end
35
35
  end
@@ -41,4 +41,4 @@ if defined?(::Que)
41
41
  Bugsnag.configuration.app_type ||= "que"
42
42
  Que.error_handler = handler
43
43
  end
44
- end
44
+ end
@@ -1,11 +1,8 @@
1
1
  module Bugsnag
2
2
  class Rack
3
3
 
4
- SEVERITY_REASON = {
5
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
6
- :attributes => {
7
- :framework => "Rack"
8
- }
4
+ FRAMEWORK_ATTRIBUTES = {
5
+ :framework => "Rack"
9
6
  }
10
7
 
11
8
  def initialize(app)
@@ -14,37 +11,41 @@ module Bugsnag
14
11
  # Configure bugsnag rack defaults
15
12
  Bugsnag.configure do |config|
16
13
  # Try to set the release_stage automatically if it hasn't already been set
17
- config.release_stage ||= release_stage
14
+ config.release_stage ||= ENV["RACK_ENV"] if ENV["RACK_ENV"]
18
15
 
19
16
  # Try to set the project_root if it hasn't already been set, or show a warning if we can't
20
17
  unless config.project_root && !config.project_root.to_s.empty?
21
18
  if defined?(settings)
22
19
  config.project_root = settings.root
23
20
  else
24
- Bugsnag.warn("You should set your app's project_root (see https://bugsnag.com/docs/notifiers/ruby#project_root).")
21
+ config.warn("You should set your app's project_root (see https://bugsnag.com/docs/notifiers/ruby#project_root).")
25
22
  end
26
23
  end
27
24
 
28
25
  # Hook up rack-based notification middlewares
29
26
  config.middleware.insert_before([Bugsnag::Middleware::Rails3Request,Bugsnag::Middleware::Callbacks], Bugsnag::Middleware::RackRequest) if defined?(::Rack)
30
27
  config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::WardenUser) if defined?(Warden)
31
- config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::ClearanceUser) if defined?(Clearance)
28
+ config.middleware.insert_before(Bugsnag::Middleware::Callbkacs, Bugsnag::Middleware::ClearanceUser) if defined?(Clearance)
32
29
 
33
- Bugsnag.configuration.app_type ||= "rack"
30
+ config.app_type ||= "rack"
34
31
  end
35
32
  end
36
33
 
37
34
  def call(env)
38
35
  # Set the request data for bugsnag middleware to use
39
- Bugsnag.set_request_data(:rack_env, env)
36
+ Bugsnag.configuration.set_request_data(:rack_env, env)
40
37
 
41
38
  begin
42
39
  response = @app.call(env)
43
40
  rescue Exception => raised
44
41
  # Notify bugsnag of rack exceptions
45
- Bugsnag.auto_notify(raised, {
46
- :severity_reason => SEVERITY_REASON
47
- })
42
+ Bugsnag.notify(raised, true) do |report|
43
+ report.severity = "error"
44
+ report.severity_reason = {
45
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
46
+ :attributes => Bugsnag::Rack::FRAMEWORK_ATTRIBUTES
47
+ }
48
+ end
48
49
 
49
50
  # Re-raise the exception
50
51
  raise
@@ -52,21 +53,19 @@ module Bugsnag
52
53
 
53
54
  # Notify bugsnag of rack exceptions
54
55
  if env["rack.exception"]
55
- Bugsnag.auto_notify(env["rack.exception"], {
56
- :severity_reason => SEVERITY_REASON
57
- })
56
+ Bugsnag.notify(env["rack.exception"], true) do |report|
57
+ report.severity = "error"
58
+ report.severity_reason = {
59
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
60
+ :attributes => FRAMEWORK_ATTRIBUTES
61
+ }
62
+ end
58
63
  end
59
64
 
60
65
  response
61
66
  ensure
62
67
  # Clear per-request data after processing the each request
63
- Bugsnag.clear_request_data
64
- end
65
-
66
- private
67
-
68
- def release_stage
69
- ENV["BUGSNAG_RELEASE_STAGE"] || ENV["RACK_ENV"]
68
+ Bugsnag.configuration.clear_request_data
70
69
  end
71
70
  end
72
71
  end
@@ -1,6 +1,9 @@
1
1
  module Bugsnag::Rails
2
2
  module ActiveRecordRescue
3
3
  KINDS = [:commit, :rollback].freeze
4
+ FRAMEWORK_ATTRIBUTES = {
5
+ :framework => "Rails"
6
+ }
4
7
 
5
8
  def run_callbacks(kind, *args, &block)
6
9
  if KINDS.include?(kind)
@@ -8,14 +11,13 @@ module Bugsnag::Rails
8
11
  super
9
12
  rescue StandardError => exception
10
13
  # This exception will NOT be escalated, so notify it here.
11
- Bugsnag.auto_notify(exception, {
12
- :severity_reason => {
13
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
14
- :attributes => {
15
- :framework => "Rails"
16
- }
14
+ Bugsnag.notify(exception, true) do |report|
15
+ report.severity = "error"
16
+ report.severity_reason = {
17
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
18
+ :attributes => FRAMEWORK_ATTRIBUTES
17
19
  }
18
- })
20
+ end
19
21
  raise
20
22
  end
21
23
  else
@@ -10,10 +10,6 @@ module Bugsnag::Rails
10
10
  _add_bugsnag_notify_callback(:before_callbacks, *methods, &block)
11
11
  end
12
12
 
13
- def after_bugsnag_notify(*methods, &block)
14
- _add_bugsnag_notify_callback(:after_callbacks, *methods, &block)
15
- end
16
-
17
13
  def _add_bugsnag_notify_callback(callback_key, *methods, &block)
18
14
  options = methods.last.is_a?(Hash) ? methods.pop : {}
19
15
 
@@ -40,10 +36,5 @@ module Bugsnag::Rails
40
36
  end
41
37
  end
42
38
  end
43
-
44
- private
45
- def notify_bugsnag(exception, custom_data=nil)
46
- Bugsnag.notify(exception, custom_data)
47
- end
48
39
  end
49
40
  end
@@ -7,8 +7,13 @@ require "bugsnag/middleware/rack_request"
7
7
 
8
8
  module Bugsnag
9
9
  class Railtie < Rails::Railtie
10
+
11
+ FRAMEWORK_ATTRIBUTES = {
12
+ :framework => "Rails"
13
+ }
14
+
10
15
  rake_tasks do
11
- require "bugsnag/rake"
16
+ require "bugsnag/integrations/rake"
12
17
  load "bugsnag/tasks/bugsnag.rake"
13
18
  end
14
19
 
@@ -17,14 +22,13 @@ module Bugsnag
17
22
  runner do
18
23
  at_exit do
19
24
  if $!
20
- Bugsnag.auto_notify($!, {
21
- :severity_reason => {
22
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
23
- :attributes => {
24
- :framework => "Rails"
25
- }
25
+ Bugsnag.notify($!, true) do |report|
26
+ report.severity = "error"
27
+ report.severity_reason = {
28
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
29
+ :attributes => FRAMEWORK_ATTRIBUTES
26
30
  }
27
- })
31
+ end
28
32
  end
29
33
  end
30
34
  end
@@ -39,28 +43,27 @@ module Bugsnag
39
43
  config.middleware.insert_before Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::Rails3Request
40
44
  end
41
45
 
42
- # Auto-load configuration settings from config/bugsnag.yml if it exists
43
- config_file = ::Rails.root.join("config", "bugsnag.yml")
44
- config = YAML.load_file(config_file) if File.exist?(config_file)
45
- Bugsnag.configure(config[::Rails.env] ? config[::Rails.env] : config) if config
46
-
47
- ActiveSupport.on_load(:action_controller) do
48
- require "bugsnag/rails/controller_methods"
49
- include Bugsnag::Rails::ControllerMethods
46
+ if defined?(::ActionController::Base)
47
+ require "bugsnag/integrations/rails/controller_methods"
48
+ ::ActionController::Base.send(:include, Bugsnag::Rails::ControllerMethods)
49
+ end
50
+ if defined?(ActionController::API)
51
+ require "bugsnag/integrations/rails/controller_methods"
52
+ ActionController::API.send(:include, Bugsnag::Rails::ControllerMethods)
50
53
  end
51
- ActiveSupport.on_load(:active_record) do
52
- require "bugsnag/rails/active_record_rescue"
53
- include Bugsnag::Rails::ActiveRecordRescue
54
+ if defined?(ActiveRecord::Base)
55
+ require "bugsnag/integrations/rails/active_record_rescue"
56
+ ActiveRecord::Base.send(:include, Bugsnag::Rails::ActiveRecordRescue)
54
57
  end
55
58
 
56
59
  Bugsnag.configuration.app_type = "rails"
57
60
  end
58
61
 
59
- # Configure params_filters after initialization, so that rails initializers
62
+ # Configure meta_data_filters after initialization, so that rails initializers
60
63
  # may set filter_parameters which will be picked up by Bugsnag.
61
64
  config.after_initialize do
62
65
  Bugsnag.configure do |config|
63
- config.params_filters += ::Rails.configuration.filter_parameters.map do |filter|
66
+ config.meta_data_filters += ::Rails.configuration.filter_parameters.map do |filter|
64
67
  case filter
65
68
  when String, Symbol
66
69
  /\A#{filter}\z/