diff --git a/README.md b/README.md index d50ebc8..463b629 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,13 @@ errors in a certain environment, just don't set the DSN in that environment! ```bash # Set your SENTRY_DSN environment variable. -export SENTRY_DSN=https://public:secret@example.com/project-id +export SENTRY_DSN=https://public@example.com/project-id ``` ```crystal # Or you can configure the client in the code (not recommended - keep your DSN secret!) Raven.configure do |config| - config.dsn = "https://public:secret@example.com/project-id" + config.dsn = "https://public@example.com/project-id" end ``` diff --git a/shard.yml b/shard.yml index 0df4777..f7256b4 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: raven -version: 1.1.0 +version: 1.2.0 authors: - Sijawusz Pur Rahnama @@ -7,14 +7,15 @@ authors: dependencies: any_hash: github: Sija/any_hash.cr - version: 0.2.2 + version: ~> 0.2.2 development_dependencies: timecop: - github: TobiasGSmollett/timecop.cr + github: crystal-community/timecop.cr + version: ~> 0.2.0 ameba: github: veelenga/ameba - version: 0.8.0 + version: ~> 0.9.0 targets: crash_handler: diff --git a/spec/raven/breadcrumb_buffer_spec.cr b/spec/raven/breadcrumb_buffer_spec.cr index bff6136..4b2a7a4 100644 --- a/spec/raven/breadcrumb_buffer_spec.cr +++ b/spec/raven/breadcrumb_buffer_spec.cr @@ -31,6 +31,20 @@ describe Raven::BreadcrumbBuffer do end end + it "records breadcrumbs w/a splat" do + with_breadcrumb_buffer do |breadcrumbs| + breadcrumbs.record(message: "test") + breadcrumbs.members.first.message.should eq("test") + end + end + + it "records breadcrumbs w/a NamedTuple" do + with_breadcrumb_buffer do |breadcrumbs| + breadcrumbs.record({message: "test"}) + breadcrumbs.members.first.message.should eq("test") + end + end + it "allows peeking" do with_breadcrumb_buffer do |breadcrumbs| breadcrumbs.peek.should be_nil diff --git a/spec/raven/instance_spec.cr b/spec/raven/instance_spec.cr index 4b848ec..a604ac0 100644 --- a/spec/raven/instance_spec.cr +++ b/spec/raven/instance_spec.cr @@ -15,7 +15,7 @@ end private class InstanceTest < Raven::Instance getter last_sent_event : Raven::Event? - def send_event(event) + def send_event(event, hint = nil) super.tap do @last_sent_event = event end diff --git a/src/crash_handler.cr b/src/crash_handler.cr index b07c6c4..7a78dd6 100644 --- a/src/crash_handler.cr +++ b/src/crash_handler.cr @@ -148,7 +148,7 @@ module Raven {output.to_s.chomp, error.to_s.chomp} end - def run : Void + def run : Nil configure! @started_at = Time.monotonic diff --git a/src/raven/breadcrumb.cr b/src/raven/breadcrumb.cr index 60eaa79..921715b 100644 --- a/src/raven/breadcrumb.cr +++ b/src/raven/breadcrumb.cr @@ -1,5 +1,7 @@ module Raven class Breadcrumb + include Mixin::InitializeWith + # The type of breadcrumb. The default type is `Type::DEFAULT` which indicates # no specific handling. Other types are currently: # - `Type::HTTP` for HTTP requests and @@ -58,8 +60,9 @@ module Raven # are unsupported by the type are rendered as a key/value table. any_json_property :data - def initialize + def initialize(**options) @timestamp = Time.now + initialize_with **options end def to_hash @@ -74,5 +77,3 @@ module Raven end end end - -require "./breadcrumbs/*" diff --git a/src/raven/breadcrumb_buffer.cr b/src/raven/breadcrumb_buffer.cr index ceaeb70..572ab75 100644 --- a/src/raven/breadcrumb_buffer.cr +++ b/src/raven/breadcrumb_buffer.cr @@ -23,15 +23,23 @@ module Raven @buffer = Array(Breadcrumb?).new(size, nil) end - def record(crumb : Breadcrumb) : Void + def record(crumb : Breadcrumb) : Nil @buffer.shift @buffer << crumb end - def record(crumb : Breadcrumb? = nil) : Void - crumb ||= Breadcrumb.new + def record(**opts) : Nil + crumb = Breadcrumb.new(**opts) yield crumb - self.record crumb + record crumb + end + + def record(**opts) : Nil + record(**opts) { } + end + + def record(opts : NamedTuple) : Nil + record(**opts) end def members : Array(Breadcrumb) diff --git a/src/raven/client.cr b/src/raven/client.cr index 9f4e6a3..3b96189 100644 --- a/src/raven/client.cr +++ b/src/raven/client.cr @@ -38,7 +38,16 @@ module Raven transport.send_feedback(event_id, data) end - def send_event(event : Event | Event::HashType) + def send_event(event : Event | Event::HashType, hint : Event::Hint? = nil) + if event.is_a?(Event) + configuration.before_send.try do |before_send| + event = before_send.call(event, hint) + unless event + logger.info "Discarded event because before_send returned nil" + return + end + end + end event = event.is_a?(Event) ? event.to_hash : event unless @state.should_try? failed_send nil, event diff --git a/src/raven/client_state.cr b/src/raven/client_state.cr index 6ac2960..c900b40 100644 --- a/src/raven/client_state.cr +++ b/src/raven/client_state.cr @@ -27,18 +27,18 @@ module Raven false end - def failure(retry_after = nil) : Void + def failure(retry_after = nil) : Nil @status = Status::ERROR @retry_number += 1 @last_check = Time.now @retry_after = retry_after end - def success : Void + def success : Nil reset end - def reset : Void + def reset : Nil @status = Status::ONLINE @retry_number = 0 @last_check = nil diff --git a/src/raven/configuration.cr b/src/raven/configuration.cr index d289c18..8fb8e3d 100644 --- a/src/raven/configuration.cr +++ b/src/raven/configuration.cr @@ -50,7 +50,7 @@ module Raven property async : Proc(Event, Nil)? # ditto - def async=(block : Proc(Event, _)) + def async=(block : Event -> _) @async = ->(event : Event) { block.call(event) nil @@ -237,13 +237,37 @@ module Raven property transport_failure_callback : Proc(Event::HashType, Nil)? # ditto - def transport_failure_callback=(block : Proc(Event::HashType, _)) + def transport_failure_callback=(block : Event::HashType -> _) @transport_failure_callback = ->(event : Event::HashType) { block.call(event) nil } end + # Optional `Proc`, called before sending an event to the server: + # + # ``` + # ->(event : Raven::Event, hint : Raven::Event::Hint?) { + # if hint.try(&.exception).try(&.message) =~ /database unavailable/i + # event.fingerprint << "database-unavailable" + # end + # event + # } + # ``` + def before_send=(block : Event, Event::Hint? -> _) + @before_send = ->(event : Event, hint : Event::Hint?) { + block.call(event, hint).as(Event?) + } + end + + # ditto + def before_send(&block : Event, Event::Hint? -> _) + self.before_send = block + end + + # ditto + property before_send : Proc(Event, Event::Hint?, Event?)? + # Errors object - an `Array` containing error messages. getter errors = [] of String @@ -257,14 +281,14 @@ module Raven @release = detect_release @server_name = server_name_from_env - # try runtime ENV variable first - if dsn = ENV["SENTRY_DSN"]? - self.dsn = dsn - end - # then try compile-time ENV variable - # overwrites runtime if set + # try compile-time ENV variable {% if dsn = env("SENTRY_DSN") %} self.dsn = {{dsn}} + {% else %} + # try runtime ENV variable + if dsn = ENV["SENTRY_DSN"]? + self.dsn = dsn + end {% end %} end @@ -314,11 +338,10 @@ module Raven File.directory?("/etc/heroku") end - private def heroku_dyno_metadata_message + private HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. " \ "For Sentry's release detection to work correctly, please run " \ "`heroku labs:enable runtime-dyno-metadata`" - end private def detect_release_from_heroku return unless running_on_heroku? @@ -326,7 +349,7 @@ module Raven if commit = ENV["HEROKU_SLUG_COMMIT"]? return commit end - logger.warn(heroku_dyno_metadata_message) + logger.warn(HEROKU_DYNO_METADATA_MESSAGE) nil end diff --git a/src/raven/event.cr b/src/raven/event.cr index 801a3c1..6522d94 100644 --- a/src/raven/event.cr +++ b/src/raven/event.cr @@ -13,6 +13,9 @@ module Raven FATAL end + # Structure passed to `Configuration#before_send` callback. + record Hint, exception : Exception?, message : String? + # See Sentry server default limits at # https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8 diff --git a/src/raven/instance.cr b/src/raven/instance.cr index 61c75e1..794169b 100644 --- a/src/raven/instance.cr +++ b/src/raven/instance.cr @@ -103,8 +103,8 @@ module Raven # event = Raven::Event.new(message: "An error") # Raven.send_event(event) # ``` - def send_event(event) - client.send_event(event) + def send_event(event, hint = nil) + client.send_event(event, hint) end @last_event_id_mutex = Mutex.new @@ -131,16 +131,22 @@ module Raven end Event.from(obj, configuration: configuration, context: context).tap do |event| event.initialize_with **options - yield event + hint = + if obj.is_a?(String) + Event::Hint.new(exception: nil, message: obj) + else + Event::Hint.new(exception: obj, message: nil) + end + yield event, hint if async = configuration.async begin async.call(event) rescue ex logger.error "Async event sending failed: #{ex.message}" - send_event(event) + send_event(event, hint) end else - send_event(event) + send_event(event, hint) end @last_event_id_mutex.synchronize do @last_event_id = event.id diff --git a/src/raven/breadcrumbs/logger.cr b/src/raven/integrations/kernel/logger.cr similarity index 100% rename from src/raven/breadcrumbs/logger.cr rename to src/raven/integrations/kernel/logger.cr diff --git a/src/raven/integrations/kernel/spawn.cr b/src/raven/integrations/kernel/spawn.cr index e16aca5..ebda53d 100644 --- a/src/raven/integrations/kernel/spawn.cr +++ b/src/raven/integrations/kernel/spawn.cr @@ -1,5 +1,4 @@ def spawn(*, name : String? = nil, &block) - # ameba:disable Style/RedundantBegin wrapped_block = ->{ begin block.call diff --git a/src/raven/transports/dummy.cr b/src/raven/transports/dummy.cr index a3ee4d8..409c4c7 100644 --- a/src/raven/transports/dummy.cr +++ b/src/raven/transports/dummy.cr @@ -6,7 +6,7 @@ module Raven def send_event(auth_header, data, **options) events << { auth_header: auth_header, - data: data, + data: data.to_s, options: options, }.to_any_json end diff --git a/src/raven/version.cr b/src/raven/version.cr index b74f44f..8f01e62 100644 --- a/src/raven/version.cr +++ b/src/raven/version.cr @@ -1,3 +1,3 @@ module Raven - VERSION = "1.1.0" + VERSION = "1.2.0" end