Skip to content

Commit

Permalink
Document Event and interface classes (#1675)
Browse files Browse the repository at this point in the history
* Comment Event's interfaces

* Add documents to RequestInterface

* Remove obsolete Interface registration logic

* Add documents to ThreadsInterface

* Add documents to ExceptionInterface

* Add documents to StacktraceBuilder

* Add documents to StacktraceInterface
  • Loading branch information
st0012 authored Jan 7, 2022
1 parent 8b9fc0a commit 95b7db1
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 40 deletions.
7 changes: 4 additions & 3 deletions sentry-ruby/lib/sentry/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Configuration
# Rails.backtrace_cleaner.clean(backtrace)
# end
#
# @return [Proc]
# @return [Proc, nil]
attr_accessor :backtrace_cleanup_callback

# Optional Proc, called before adding the breadcrumb to the current scope
Expand Down Expand Up @@ -127,8 +127,9 @@ class Configuration
alias inspect_exception_causes_for_exclusion? inspect_exception_causes_for_exclusion

# You may provide your own LineCache for matching paths with source files.
# This may be useful if you need to get source code from places other than
# the disk. See Sentry::LineCache for the required interface you must implement.
# This may be useful if you need to get source code from places other than the disk.
# @see LineCache
# @return [LineCache]
attr_accessor :linecache

# Logger used by Sentry. In Rails, this is the Rails logger, otherwise
Expand Down
42 changes: 35 additions & 7 deletions sentry-ruby/lib/sentry/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

module Sentry
class Event
# These are readable attributes.
SERIALIZEABLE_ATTRIBUTES = %i(
event_id level timestamp
release environment server_name modules
Expand All @@ -18,6 +19,7 @@ class Event
platform sdk type
)

# These are writable attributes.
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp level)

MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
Expand All @@ -29,8 +31,18 @@ class Event
attr_writer(*WRITER_ATTRIBUTES)
attr_reader(*SERIALIZEABLE_ATTRIBUTES)

attr_reader :request, :exception, :threads
# @return [RequestInterface]
attr_reader :request

# @return [ExceptionInterface]
attr_reader :exception

# @return [ThreadsInterface]
attr_reader :threads

# @param configuration [Configuration]
# @param integration_meta [Hash, nil]
# @param message [String, nil]
def initialize(configuration:, integration_meta: nil, message: nil)
# Set some simple default values
@event_id = SecureRandom.uuid.delete("-")
Expand Down Expand Up @@ -63,6 +75,7 @@ def initialize(configuration:, integration_meta: nil, message: nil)
end

class << self
# @!visibility private
def get_log_message(event_hash)
message = event_hash[:message] || event_hash['message']

Expand All @@ -79,6 +92,7 @@ def get_log_message(event_hash)
'<no message value>'
end

# @!visibility private
def get_message_from_exception(event_hash)
if exception = event_hash.dig(:exception, :values, 0)
"#{exception[:type]}: #{exception[:value]}"
Expand All @@ -94,14 +108,24 @@ def configuration
Sentry.configuration
end

# Sets the event's timestamp.
# @param time [Time, Float]
# @return [void]
def timestamp=(time)
@timestamp = time.is_a?(Time) ? time.to_f : time
end

def level=(new_level) # needed to meet the Sentry spec
@level = new_level.to_s == "warn" ? :warning : new_level
# Sets the event's level.
# @param level [String, Symbol]
# @return [void]
def level=(level) # needed to meet the Sentry spec
@level = level.to_s == "warn" ? :warning : level
end

# Sets the event's request environment data with RequestInterface.
# @see RequestInterface
# @param env [Hash]
# @return [void]
def rack_env=(env)
unless request || env.empty?
add_request_interface(env)
Expand All @@ -116,6 +140,7 @@ def rack_env=(env)
end
end

# @return [Hash]
def to_hash
data = serialize_attributes
data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
Expand All @@ -126,14 +151,12 @@ def to_hash
data
end

# @return [Hash]
def to_json_compatible
JSON.parse(JSON.generate(to_hash))
end

def add_request_interface(env)
@request = Sentry::RequestInterface.new(env: env, send_default_pii: @send_default_pii, rack_env_whitelist: @rack_env_whitelist)
end

# @!visibility private
def add_threads_interface(backtrace: nil, **options)
@threads = ThreadsInterface.build(
backtrace: backtrace,
Expand All @@ -142,6 +165,7 @@ def add_threads_interface(backtrace: nil, **options)
)
end

# @!visibility private
def add_exception_interface(exception)
if exception.respond_to?(:sentry_context)
@extra.merge!(exception.sentry_context)
Expand All @@ -152,6 +176,10 @@ def add_exception_interface(exception)

private

def add_request_interface(env)
@request = Sentry::RequestInterface.new(env: env, send_default_pii: @send_default_pii, rack_env_whitelist: @rack_env_whitelist)
end

def serialize_attributes
self.class::SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |att, memo|
if value = public_send(att)
Expand Down
11 changes: 1 addition & 10 deletions sentry-ruby/lib/sentry/interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@

module Sentry
class Interface
def self.inherited(klass)
name = klass.name.split("::").last.downcase.gsub("interface", "")
registered[name.to_sym] = klass
super
end

def self.registered
@@registered ||= {} # rubocop:disable Style/ClassVars
end

# @return [Hash]
def to_hash
Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
end
Expand Down
14 changes: 11 additions & 3 deletions sentry-ruby/lib/sentry/interfaces/exception.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,24 @@

module Sentry
class ExceptionInterface < Interface
def initialize(values:)
@values = values
# @param exceptions [Array<SingleExceptionInterface>]
def initialize(exceptions:)
@values = exceptions
end

# @return [Hash]
def to_hash
data = super
data[:values] = data[:values].map(&:to_hash) if data[:values]
data
end

# Builds ExceptionInterface with given exception and stacktrace_builder.
# @param exception [Exception]
# @param stacktrace_builder [StacktraceBuilder]
# @see SingleExceptionInterface#build_with_stacktrace
# @see SingleExceptionInterface#initialize
# @return [ExceptionInterface]
def self.build(exception:, stacktrace_builder:)
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exception).reverse
processed_backtrace_ids = Set.new
Expand All @@ -25,7 +33,7 @@ def self.build(exception:, stacktrace_builder:)
end
end

new(values: exceptions)
new(exceptions: exceptions)
end
end
end
26 changes: 25 additions & 1 deletion sentry-ruby/lib/sentry/interfaces/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,32 @@ class RequestInterface < Interface
# https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
MAX_BODY_LIMIT = 4096 * 4

attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
# @return [String]
attr_accessor :url

# @return [String]
attr_accessor :method

# @return [Hash]
attr_accessor :data

# @return [String]
attr_accessor :query_string

# @return [String]
attr_accessor :cookies

# @return [Hash]
attr_accessor :headers

# @return [Hash]
attr_accessor :env

# @param env [Hash]
# @param send_default_pii [Boolean]
# @param rack_env_whitelist [Array]
# @see Configuration#send_default_pii
# @see Configuration#rack_env_whitelist
def initialize(env:, send_default_pii:, rack_env_whitelist:)
env = env.dup

Expand Down
4 changes: 4 additions & 0 deletions sentry-ruby/lib/sentry/interfaces/stacktrace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@

module Sentry
class StacktraceInterface
# @return [<Array[Frame]>]
attr_reader :frames

# @param frames [<Array[Frame]>]
def initialize(frames:)
@frames = frames
end

# @return [Hash]
def to_hash
{ frames: @frames.map(&:to_hash) }
end

# @return [String]
def inspect
@frames.map(&:to_s)
end
Expand Down
47 changes: 37 additions & 10 deletions sentry-ruby/lib/sentry/interfaces/stacktrace_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,31 @@

module Sentry
class StacktraceBuilder
attr_reader :project_root, :app_dirs_pattern, :linecache, :context_lines, :backtrace_cleanup_callback
# @return [String]
attr_reader :project_root

# @return [Regexp, nil]
attr_reader :app_dirs_pattern

# @return [LineCache]
attr_reader :linecache

# @return [Integer, nil]
attr_reader :context_lines

# @return [Proc, nil]
attr_reader :backtrace_cleanup_callback

# @param project_root [String]
# @param app_dirs_pattern [Regexp, nil]
# @param linecache [LineCache]
# @param context_lines [Integer, nil]
# @param backtrace_cleanup_callback [Proc, nil]
# @see Configuration#project_root
# @see Configuration#app_dirs_pattern
# @see Configuration#linecache
# @see Configuration#context_lines
# @see Configuration#backtrace_cleanup_callback
def initialize(project_root:, app_dirs_pattern:, linecache:, context_lines:, backtrace_cleanup_callback: nil)
@project_root = project_root
@app_dirs_pattern = app_dirs_pattern
Expand All @@ -12,17 +35,21 @@ def initialize(project_root:, app_dirs_pattern:, linecache:, context_lines:, bac
@backtrace_cleanup_callback = backtrace_cleanup_callback
end

# you can pass a block to customize/exclude frames:
# Generates a StacktraceInterface with the given backtrace.
# You can pass a block to customize/exclude frames:
#
# ```ruby
# builder.build(backtrace) do |frame|
# if frame.module.match?(/a_gem/)
# nil
# else
# frame
# @example
# builder.build(backtrace) do |frame|
# if frame.module.match?(/a_gem/)
# nil
# else
# frame
# end
# end
# end
# ```
# @param backtrace [Array<String>]
# @param frame_callback [Proc]
# @yieldparam frame [StacktraceInterface::Frame]
# @return [StacktraceInterface]
def build(backtrace:, &frame_callback)
parsed_lines = parse_backtrace_lines(backtrace).select(&:file)

Expand Down
12 changes: 10 additions & 2 deletions sentry-ruby/lib/sentry/interfaces/threads.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

module Sentry
class ThreadsInterface
# @param crashed [Boolean]
# @param stacktrace [Array]
def initialize(crashed: false, stacktrace: nil)
@id = Thread.current.object_id
@name = Thread.current.name
Expand All @@ -10,6 +12,7 @@ def initialize(crashed: false, stacktrace: nil)
@stacktrace = stacktrace
end

# @return [Hash]
def to_hash
{
values: [
Expand All @@ -24,8 +27,13 @@ def to_hash
}
end

# patch this method if you want to change a threads interface's stacktrace frames
# also see `StacktraceBuilder.build`.
# Builds the ThreadsInterface with given backtrace and stacktrace_builder.
# Patch this method if you want to change a threads interface's stacktrace frames.
# @see StacktraceBuilder.build
# @param backtrace [Array]
# @param stacktrace_builder [StacktraceBuilder]
# @param crashed [Hash]
# @return [ThreadsInterface]
def self.build(backtrace:, stacktrace_builder:, **options)
stacktrace = stacktrace_builder.build(backtrace: backtrace) if backtrace
new(**options, stacktrace: stacktrace)
Expand Down
4 changes: 0 additions & 4 deletions sentry-ruby/spec/sentry/interface_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ class TestInterface < Sentry::Interface
end

RSpec.describe Sentry::Interface do
it "should register an interface when a new class is defined" do
expect(Sentry::Interface.registered[:test]).to eq(TestInterface)
end

it "serializes to a Hash" do
interface = TestInterface.new
interface.some_attr = "test"
Expand Down

0 comments on commit 95b7db1

Please sign in to comment.