Skip to content

Commit

Permalink
Merge pull request #72 from Sija/develop
Browse files Browse the repository at this point in the history
v1.8
  • Loading branch information
Sija authored Aug 5, 2020
2 parents d37386c + 27356a6 commit 6673db9
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 135 deletions.
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ from folks at [@getsentry](https://github.com/getsentry).

- [x] Processors (data scrubbers)
- [x] Interfaces (Message, Exception, Stacktrace, User, HTTP, ...)
- [x] Contexts (tags, extra, `os`, `runtime`)
- [x] Contexts (user, tags, extra, os, runtime)
- [x] Breadcrumbs
- [x] Integrations ([Kemal](https://github.com/kemalcr/kemal), [Amber](https://github.com/amberframework/amber), [Lucky](https://github.com/luckyframework/lucky), [Sidekiq.cr](https://github.com/mperham/sidekiq.cr))
- [x] Async support
- [x] User Feedback
- [x] Source code context for stack traces
- [x] Dedicated [`Log`](https://crystal-lang.org/api/Log.html) backend
- [x] Crash Handler

## Installation
Expand Down Expand Up @@ -201,6 +203,43 @@ Raven.extra_context happiness: "very"

For more information, see [Context](https://docs.sentry.io/clients/ruby/context/).

## `Log` backend

`Raven::LogBackend` allows for intercepting log entries, and takes following options:

- `record_breadcrumbs` - records every log entry as Breadcrumbs
- `capture_exceptions` - captures exceptions attached to the log entry
- `capture_all` - captures every log entry

Every captured `Exception` or a `Breadcrumb` will have corresponding fields mapped
directly from the original `Log::Entry`.

Metadata will be passed as `Event#tags` and `Breadcrumb#data`, respectively.

### Usage

```crystal
# append it to the existing bindings
Log.builder.bind "*", :info, Raven::LogBackend.new(
record_breadcrumbs: true,
capture_exceptions: false,
capture_all: false,
)
# or bind it within the `Log.setup` block
Log.setup do |c|
# bind the regular io-based backend
c.bind "*", :info, Log::IOBackend.new
# bind raven's backend
c.bind "*", :info, Raven::LogBackend.new(record_breadcrumbs: true)
c.bind "*", :warn, Raven::LogBackend.new(capture_exceptions: true)
c.bind "*", :fatal, Raven::LogBackend.new(capture_all: true)
end
```

See more in Crystal's `Log` [documentation](https://crystal-lang.org/api/Log.html#configure-logging-explicitly-in-the-code).

## Crash Handler

Since Crystal doesn't provide native handlers for unhandled exceptions
Expand Down
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: raven
version: 1.7.2
version: 1.8.0

authors:
- Sijawusz Pur Rahnama <sija@sija.pl>
Expand Down
148 changes: 148 additions & 0 deletions spec/raven/log_backend_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
require "log"
require "colorize"

private def build_logger(source = nil, **opts)
opts = {
record_breadcrumbs: false,
capture_exceptions: false,
capture_all: false,
}.merge(opts)

backend = Raven::LogBackend.new(**opts)

Log::Builder.new
.tap(&.bind("*", :trace, backend))
.for(source.to_s)
end

private def with_clean_configuration
prev_configuration = Raven.instance.configuration.dup
begin
Raven.instance.configuration = build_configuration
yield
ensure
Raven.instance.configuration = prev_configuration
end
end

describe Raven::LogBackend do
around_each do |example|
Raven::BreadcrumbBuffer.clear!
with_clean_configuration do
Raven.configuration.exclude_loggers.clear
example.run
end
Raven::BreadcrumbBuffer.clear!
end

context ":record_breadcrumbs" do
it "respects Raven.configuration.exclude_loggers setting" do
Raven.configuration.exclude_loggers = %w[spec.raven.*]

logger = build_logger("spec.raven.crumbs", record_breadcrumbs: true)
logger.trace { "foo" }

logger = build_logger("spec.raven", record_breadcrumbs: true)
logger.trace { "bar" }

Raven.breadcrumbs.should be_empty
end

context "with exception" do
it "records entries as breadcrumbs (true)" do
ex = build_exception

logger = build_logger("spec.raven", record_breadcrumbs: true)
logger.trace(exception: ex) { "foo".colorize(:green) }

crumbs = Raven.breadcrumbs
crumbs.size.should eq(1)

last = crumbs.peek.should_not be_nil
last.level.should eq(Raven::Breadcrumb::Severity::DEBUG)
last.category.should eq("spec.raven")
last.message.should eq("foo -- (%s): %s" % {ex.class, ex.message})
end
end

context "without exception" do
it "records entries as breadcrumbs (true)" do
logger = build_logger("spec.raven", record_breadcrumbs: true)
logger.trace { "foo".colorize(:green) }

crumbs = Raven.breadcrumbs
crumbs.size.should eq(1)

last = crumbs.peek.should_not be_nil
last.level.should eq(Raven::Breadcrumb::Severity::DEBUG)
last.category.should eq("spec.raven")
last.message.should eq("foo")
end
end

it "records entries as breadcrumbs (false)" do
logger = build_logger(record_breadcrumbs: false)
logger.trace { "boo!" }

Raven.breadcrumbs.should be_empty
end
end

context ":capture_exceptions" do
it "captures attached exception if present (true)" do
ex = build_exception

logger = build_logger(capture_exceptions: true)
logger.trace(exception: ex) { "boo!" }

Raven.captured_exception?(ex).should be_true
end

it "captures attached exception if present (false)" do
ex = build_exception

logger = build_logger(capture_exceptions: false)
logger.trace(exception: ex) { "boo!" }

Raven.captured_exception?(ex).should be_false
end
end

context ":capture_all" do
it "captures attached exception if present (true)" do
ex = build_exception

logger = build_logger(capture_all: true)
logger.trace(exception: ex) { "boo!" }

Raven.captured_exception?(ex).should be_true
end

it "captures attached exception if present (false)" do
ex = build_exception

logger = build_logger(capture_all: false)
logger.trace(exception: ex) { "boo!" }

Raven.captured_exception?(ex).should be_false
end

it "captures every entry (true)" do
prev_event_id = Raven.last_event_id

logger = build_logger(capture_all: true)
logger.trace { "boo!" }

Raven.last_event_id.should_not eq(prev_event_id)
end

it "captures every entry (false)" do
prev_event_id = Raven.last_event_id

logger = build_logger(capture_all: false)
logger.trace { "boo!" }

Raven.last_event_id.should eq(prev_event_id)
end
end
end
2 changes: 1 addition & 1 deletion src/raven.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Raven
module Delegators
delegate :context, :configuration, :client,
:report_status, :configure, :send_feedback, :send_event,
:capture, :last_event_id, :annotate_exception,
:capture, :last_event_id, :annotate_exception, :captured_exception?,
:user_context, :tags_context, :extra_context, :breadcrumbs,
to: :instance
end
Expand Down
4 changes: 2 additions & 2 deletions src/raven/backtrace.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ module Raven
filters = default_filters.dup
options[:filters]?.try { |f| filters.concat(f) }

filtered_lines = backtrace.map do |line|
filtered_lines = backtrace.compact_map do |line|
filters.reduce(line) do |nested_line, proc|
proc.call(nested_line) || break
end
end.compact
end

lines = filtered_lines.map &->Line.parse(String)
new(lines)
Expand Down
2 changes: 1 addition & 1 deletion src/raven/backtrace_line.cr
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ module Raven
new(file, number, column, method)
end

# ditto
# :ditto:
def self.parse(unparsed_line : String) : Line
parse?(unparsed_line) || \
raise ArgumentError.new("Error parsing line: #{unparsed_line.inspect}")
Expand Down
4 changes: 2 additions & 2 deletions src/raven/breadcrumb.cr
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module Raven
# - `:navigation` for navigation events.
property type : Type?

# ditto
# :ditto:
def type=(type : Symbol)
@type = Type.parse(type.to_s)
end
Expand All @@ -50,7 +50,7 @@ module Raven
# to `info` which is the middle level.
property level : Severity?

# ditto
# :ditto:
def level=(severity : Symbol)
@level = Severity.parse(severity.to_s)
end
Expand Down
6 changes: 2 additions & 4 deletions src/raven/client.cr
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,15 @@ module Raven
io.rewind

case configuration.encoding
when .gzip?
in .gzip?
io_gzipped = IO::Memory.new
Compress::Gzip::Writer.open(io_gzipped) do |gzip|
IO.copy(io, gzip)
end
io_gzipped.rewind
{"application/octet-stream", io_gzipped}
when .json?
in .json?
{"application/json", io}
else
raise "Invalid configuration encoding"
end
end

Expand Down
22 changes: 11 additions & 11 deletions src/raven/configuration.cr
Original file line number Diff line number Diff line change
Expand Up @@ -243,12 +243,12 @@ module Raven
}
end

# ditto
# :ditto:
def before_send(&block : Event, Event::Hint? -> _)
self.before_send = block
end

# ditto
# :ditto:
property before_send : Proc(Event, Event::Hint?, Event?)?

# Errors object - an `Array` containing error messages.
Expand Down Expand Up @@ -378,12 +378,12 @@ module Raven
sample_allowed?
end

def capture_allowed?(message_or_exc)
def capture_allowed?(message_or_ex)
@errors = [] of String
capture_allowed? &&
!raven_error?(message_or_exc) &&
!excluded_exception?(message_or_exc) &&
capture_allowed_by_callback?(message_or_exc)
!raven_error?(message_or_ex) &&
!excluded_exception?(message_or_ex) &&
capture_allowed_by_callback?(message_or_ex)
end

private def capture_in_current_environment?
Expand All @@ -392,8 +392,8 @@ module Raven
false
end

private def capture_allowed_by_callback?(message_or_exc)
return true if !should_capture || should_capture.try &.call(message_or_exc)
private def capture_allowed_by_callback?(message_or_ex)
return true if !should_capture || should_capture.try &.call(message_or_ex)
@errors << "#should_capture returned false"
false
end
Expand All @@ -405,9 +405,9 @@ module Raven
false
end

def raven_error?(message_or_exc)
return false unless message_or_exc.is_a?(Raven::Error)
@errors << "Refusing to capture Raven error: #{message_or_exc.inspect}"
def raven_error?(message_or_ex)
return false unless message_or_ex.is_a?(Raven::Error)
@errors << "Refusing to capture Raven error: #{message_or_ex.inspect}"
true
end

Expand Down
Loading

0 comments on commit 6673db9

Please sign in to comment.