diff --git a/.travis.yml b/.travis.yml index 44c9d45..7042f27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ language: ruby cache: bundler env: - - CI=true + global: + - CI=true + - JRUBY_OPTS=--debug before_install: - gem install bundler rvm: @@ -11,6 +13,7 @@ rvm: - 2.2.3 - 2.3.3 - 2.4.0 + - jruby-9.1.11.0 gemfile: - gemfiles/Gemfile.rails-3.2.x - gemfiles/Gemfile.rails-4.0.x @@ -21,6 +24,8 @@ matrix: include: - rvm: 2.3.3 gemfile: gemfiles/Gemfile.rails-HEAD + - rvm: jruby-9.1.11.0 + gemfile: gemfiles/Gemfile.rails-HEAD exclude: - rvm: 2.0.0 @@ -35,6 +40,14 @@ matrix: gemfile: gemfiles/Gemfile.rails-4.1.x - rvm: 2.4.0 gemfile: gemfiles/Gemfile.rails-4.2.x + - rvm: jruby-9.1.11.0 + gemfile: gemfiles/Gemfile.rails-3.2.x + - rvm: jruby-9.1.11.0 + gemfile: gemfiles/Gemfile.rails-4.0.x + - rvm: jruby-9.1.11.0 + gemfile: gemfiles/Gemfile.rails-4.1.x + - rvm: jruby-9.1.11.0 + gemfile: gemfiles/Gemfile.rails-4.2.x notifications: email: false diff --git a/lib/opbeat/error_message/stacktrace.rb b/lib/opbeat/error_message/stacktrace.rb index 6133f6f..0bb8704 100644 --- a/lib/opbeat/error_message/stacktrace.rb +++ b/lib/opbeat/error_message/stacktrace.rb @@ -27,9 +27,19 @@ class Frame < Struct.new(:filename, :lineno, :abs_path, :function, :vars, BACKTRACE_REGEX = /^(.+?):(\d+)(?::in `(.+?)')?$/.freeze + # regexp (optional leading X: on windows, or JRuby9000 class-prefix) + RUBY_INPUT_FORMAT = / + ^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>): + (\d+) + (?: :in \s `([^']+)')?$ + /x + + # org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170) + JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/ + class << self def from_line config, line - _, abs_path, lineno, function = line.match(BACKTRACE_REGEX).to_a + abs_path, lineno, function, _module_name = parse_line(line) lineno = lineno.to_i filename = strip_load_path(abs_path) @@ -44,7 +54,21 @@ def from_line config, line private + def parse_line(unparsed_line) + ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT) + if ruby_match + _, file, number, method = ruby_match.to_a + file.sub!(/\.class$/, '.rb') + module_name = nil + else + java_match = unparsed_line.match(JAVA_INPUT_FORMAT) + _, module_name, method, file, number = java_match.to_a + end + [file, number, method, module_name] + end + def strip_load_path path + return '' unless path prefix = $: .map(&:to_s) .select { |s| path.start_with?(s) } diff --git a/opbeat.gemspec b/opbeat.gemspec index 65d2516..ef6e202 100644 --- a/opbeat.gemspec +++ b/opbeat.gemspec @@ -11,6 +11,7 @@ Gem::Specification.new do |gem| gem.summary = "The official Opbeat Ruby client library" gem.homepage = "https://github.com/opbeat/opbeat-ruby" gem.license = "BSD-3-Clause" + gem.required_ruby_version = ">= 2.0.0" gem.files = `git ls-files -z`.split("\x0") gem.require_paths = ["lib"] diff --git a/spec/opbeat/error_message/stacktrace_spec.rb b/spec/opbeat/error_message/stacktrace_spec.rb index 8862010..1c15c9d 100644 --- a/spec/opbeat/error_message/stacktrace_spec.rb +++ b/spec/opbeat/error_message/stacktrace_spec.rb @@ -2,37 +2,58 @@ module Opbeat RSpec.describe ErrorMessage::Stacktrace do - def real_exception 1 / 0 rescue => e e end + def java_exception + require 'java' + java_import 'java.lang.ClassNotFoundException' + java.lang::Class.forName('foo.Bar') + rescue ClassNotFoundException => e + e + end + let(:config) { Configuration.new } let(:exception) { real_exception } - describe ".from" do - it "initializes from an exception" do - stacktrace = ErrorMessage::Stacktrace.from config, exception - expect(stacktrace.frames).to_not be_empty - - # so meta - last_frame = stacktrace.frames.last - expect(last_frame.filename).to eq "opbeat/error_message/stacktrace_spec.rb" - expect(last_frame.lineno).to be 7 - expect(last_frame.abs_path).to_not be_nil - expect(last_frame.function).to eq "/" - expect(last_frame.vars).to be_nil - - expect(last_frame.pre_context.last).to match(/def real_exception/) - expect(last_frame.context_line).to match(/1 \/ 0/) - expect(last_frame.post_context.first).to match(/rescue/) + describe '.from' do + context 'when on JRuby', if: RSpec::Support::Ruby.jruby? do + it 'initializes from a Java exception' do + stacktrace = ErrorMessage::Stacktrace.from config, java_exception + expect(stacktrace.frames).to_not be_empty + end + + it 'initializes from an exception' do + stacktrace = ErrorMessage::Stacktrace.from config, exception + expect(stacktrace.frames).to_not be_empty + end end - context "when context lines are off" do + context 'when on MRI', unless: RSpec::Support::Ruby.jruby? do + it 'initializes from an exception' do + stacktrace = ErrorMessage::Stacktrace.from config, exception + expect(stacktrace.frames).to_not be_empty + + # so meta + last_frame = stacktrace.frames.last + expect(last_frame.filename).to eq 'opbeat/error_message/stacktrace_spec.rb' + expect(last_frame.lineno).to be 6 + expect(last_frame.abs_path).to_not be_nil + expect(last_frame.function).to eq '/' + expect(last_frame.vars).to be_nil + + expect(last_frame.pre_context.last).to match(/def real_exception/) + expect(last_frame.context_line).to match(/1 \/ 0/) + expect(last_frame.post_context.first).to match(/rescue/) + end + end + + context 'when context lines are off' do let(:config) { Configuration.new context_lines: nil } - it "initializes too" do + it 'initializes too' do stacktrace = ErrorMessage::Stacktrace.from config, exception expect(stacktrace.frames).to_not be_empty @@ -44,13 +65,12 @@ def real_exception end end - describe "#to_h" do - it "is a hash" do + describe '#to_h' do + it 'is a hash' do hsh = ErrorMessage::Stacktrace.from(config, exception).to_h expect(hsh).to be_a Hash expect(hsh.keys).to eq [:frames] end end - end end diff --git a/spec/opbeat/injections/sequel_spec.rb b/spec/opbeat/injections/sequel_spec.rb index 983aa06..36166f0 100644 --- a/spec/opbeat/injections/sequel_spec.rb +++ b/spec/opbeat/injections/sequel_spec.rb @@ -2,7 +2,7 @@ require 'sequel' module Opbeat - RSpec.describe Injections::Sequel do + RSpec.describe Injections::Sequel, unless: RSpec::Support::Ruby.jruby? do it "is installed" do reg = Opbeat::Injections.installed['Sequel']