Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement code hunks support #258

Merged
merged 2 commits into from
Oct 11, 2017
Merged

Implement code hunks support #258

merged 2 commits into from
Oct 11, 2017

Conversation

kyrylo
Copy link
Contributor

@kyrylo kyrylo commented Oct 10, 2017

This feature is experimental. I am not documenting it in the README because
users shouldn't be using it yet.

@@ -0,0 +1 @@
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [204/90] (https://github.com/bbatsov/ruby-style-guide#80-character-limits)

@kyrylo kyrylo force-pushed the send-code-hunks branch 2 times, most recently from 8384f4a to 2b25010 Compare October 10, 2017 12:10
93 => ' begin',
94 => ' json = @payload.to_json',
95 => ' rescue *JSON_EXCEPTIONS => ex',
# rubocop:disable Metrics/LineLength,Lint/InterpolationCheck

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lint/UnneededDisable: Unnecessary disabling of Lint/InterpolationCheck (did you mean Lint/LiteralInCondition?).

# rubocop:enable Metrics/LineLength,Lint/InterpolationCheck
97 => ' else'
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unexpected token tRCURLY

93 => ' begin',
94 => ' json = @payload.to_json',
95 => ' rescue *JSON_EXCEPTIONS => ex',
# rubocop:disable Metrics/LineLength,Lint/InterpolationCheck

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lint/UnneededDisable: Unnecessary disabling of Lint/InterpolationCheck (did you mean Lint/LiteralInCondition?), Metrics/LineLength.

This feature is experimental. I am not documenting it in the README because
users shouldn't be using it yet.
return unless code_hunk

unless code_hunk.key?(0)
frame[:code_hunk] = code_hunk

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename to just code for brevity? Anyway in JSON it should be codeHunk, not code_hunk

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

code_hunk = Airbrake::CodeHunk.new(frame[:file], frame[:line]).to_h
return unless code_hunk

unless code_hunk.key?(0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see 2 unless that check for code_hunk. I also don't understand what code_hunk.key?(0) does so it would be nice if you refactor this code or make it more obvious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#to_h returns a hash of int => string. A file can never have a zero line. I thought the existence of it would mean there's an error.

First unless is when a file is empty. Second unless is when there's an error that we need to log.

Does that make sense?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense, but why not to throw an exception like any other Ruby code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be unfortunate to miss a notice just because we couldn't read a line of code. I think simply ignoring that error and moving on would be the best solution.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't suggest you throw that exception to users. You can catch and log it as usually.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


logger.error(
"#{LOG_LABEL} error while reading code hunk `#{file}:#{line}'. " \
"Reason: #{code_hunk[0]}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I don't understand why code_hunk[0] contains the reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Files can never have line 0.


##
# @return [Integer] how many lines should be read around the base line
INTERVAL = 3

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NLINES would be a better name. Also I suggest to lower this to 2 so we have 2 lines before and 2 lines after.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

subject { described_class.new(fixture_path('empty_file.rb'), 1).to_h }

it { is_expected.to eql({}) }
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For empty file I expect to see {1: ""}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

subject { described_class.new(fixture_path('banana.rb'), 1).to_h }

it { is_expected.to be_nil }
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not to throw an exception (return error)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would it buy us?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK that is how errors are returned in Ruby.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have examples, I'd look at them. I don't think that's how it works. Either way, it's not a big deal because in real world backtraces would unlikely point to non existing files.

end
end

context "when a file has less lines than a code hunk requests" do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if you follow previous context and say something like less than INTERVAL lines before and after

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

end
end

context "when a line location is in the middle of a file" do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this should has enough lines before and after

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@start_line = [line - interval, 1].max
@end_line = line + interval

@code_hash = {}
Copy link

@vmihailenco vmihailenco Oct 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code_hash -> lines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

99 => ' end',
100 => '',
101 => ' break if truncate == 0',
102 => ' end',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style/TrailingCommaInArguments: Avoid comma after the last parameter of a method call. (https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma)

@kyrylo
Copy link
Contributor Author

kyrylo commented Oct 10, 2017

Pending discussion in #258 (comment) and #258 (comment) but the rest is fixed.

PTAL

@kyrylo
Copy link
Contributor Author

kyrylo commented Oct 10, 2017

PTAL

@@ -188,6 +193,13 @@ def match_frame(regexp, stackframe)

Patterns::GENERIC.match(stackframe)
end

def read_code_hunk(config, frame)
code_hunk = Airbrake::CodeHunk.new(config, frame[:file], frame[:line]).to_h
Copy link

@vmihailenco vmihailenco Oct 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a good reason this method is called to_h? I see none.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read?

Copy link

@vmihailenco vmihailenco Oct 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect CodeHunk to be shared and code look like this:

def constructor
  @code_hunk = Airbrake::CodeHunk.new(config)

def populate_code(frame)
  @code = @code_hunk.get(file, line)
  if @code
    frame[:code] = @code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks ok to me, but the problem is that Backtrace doesn't have a constructor. We use Backtrace.parse, not Backtrace.new.parse.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No big deal, I can init at the same place.

function: match[:function]
}

read_code_hunk(config, frame) if config.code_hunks

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is supposed to read something, but it returns nil.


private

def fetch_code

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually reads somethings, but it is called fetch and returns nil too.

return unless File.exist?(@file)

begin
fetch_code

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I see is that you check lines, then for some reason call fetch_code and then check lines again. It would be much more obvious if code was:

check @lines
set @lines
check @lines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, checking lines on line 29 is not needed. It's just prevention of reading the same file twice (for the same results). I removed it for now.

return
end

@lines = { 1 => '' } if @lines.empty?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this is here and not in fetch_code?


begin
fetch_code
rescue StandardError => ex

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an example you asked for.

@vmihailenco
Copy link

With this change notifier reads every file in backtrace for every notice which is a bit wasteful. I think we need to add a fixed size cache (say last 100 entries) in separate PR to make situation better.

@kyrylo
Copy link
Contributor Author

kyrylo commented Oct 10, 2017

With this change notifier reads every file in backtrace for every notice which is a bit wasteful. I think we need to add a fixed size cache (say last 100 entries) in separate PR to make situation better.

I agree.


I simplified code.

PTAL

@kyrylo kyrylo merged commit d5b31f8 into master Oct 11, 2017
@kyrylo kyrylo deleted the send-code-hunks branch October 11, 2017 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants