bugsnag / bugsnag-ruby

BugSnag error monitoring & reporting software for rails, sinatra, rack and ruby
https://docs.bugsnag.com/platforms/ruby
MIT License
245 stars 173 forks source link

Add the ability to store metadata globally #699

Closed imjoehaines closed 2 years ago

imjoehaines commented 2 years ago

Goal

This PR adds the ability to store metadata globally, which will then be attached to any future events. The API matches the new event metadata API added in https://github.com/bugsnag/bugsnag-ruby/pull/694, for example:

Bugsnag.configure do |config|
  config.add_metadata(:abc, { x: 1, y: 2, z: 3 })
  config.add_metadata(:abc, :a, 9)

  config.clear_metadata(:abc, :x)

  # metadata is: { abc: { y: 2, z: 3, a: 9 } }
end

Metadata can also be added/cleared outside of a configure block with Bugsnag.add_metadata and Bugsnag.clear_metadata

Global metadata is deeply cloned when attached to an event, which means mutating the event's metadata won't affect the global metadata:

Bugsnag.add_metadata(:abc, { x: 1, y: 2, z: 3 })

Bugsnag.notify(RuntimeError.new("example")) do |event|
  event.add_metadata(:abc, :x, "changed")
  event.clear_metadata(:abc, :z)

  # event metadata is: { x: "changed", y: 2 }
  # global metadata is: { x: 1, y: 2, z: 3 }
end

Design

The deep cloning is handled by a new Bugsnag::Utility::Duplicator class. The existing Gems I found were either unmaintained, monkey patched core objects or didn't support all of our supported Ruby versions

While this should be pretty thorough, it's possible for some values not to be duplicated. I think this is unlikely to be a problem given that types of values that will realistically be used in metadata, since it has to serialise to JSON in the end

The alternative to adding this class is using the Marshal.load(Marshal.dump(object)) trick, but this has a few drawbacks: