Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Stackdriver crash reporting (#9113)
- Loading branch information
Showing
12 changed files
with
415 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
module Fastlane | ||
module Actions | ||
class OptOutCrashReportingAction < Action | ||
def self.run(params) | ||
ENV['FASTLANE_OPT_OUT_CRASH_REPORTING'] = "YES" | ||
UI.message("Disabled crash reporting") | ||
end | ||
|
||
def self.description | ||
"This will prevent reports from being uploaded when _fastlane_ crashes" | ||
end | ||
|
||
def self.details | ||
[ | ||
"By default, fastlane will send a report when it crashes", | ||
"The stacktrace is sanitized so no personal information is sent.", | ||
"Learn more at https://github.com/fastlane/fastlane#crash-reporting", | ||
"Add `opt_out_crash_reporting` at the top of your Fastfile to disable crash reporting" | ||
].join(' ') | ||
end | ||
|
||
def self.authors | ||
['mpirri', 'ohayon'] | ||
end | ||
|
||
def self.is_supported?(platform) | ||
true | ||
end | ||
|
||
def self.example_code | ||
[ | ||
'opt_out_crash_reporting # add this to the top of your Fastfile' | ||
] | ||
end | ||
|
||
def self.category | ||
:misc | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
fastlane_core/lib/fastlane_core/crash_reporter/backtrace_sanitizer.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
module FastlaneCore | ||
class BacktraceSanitizer | ||
class << self | ||
def sanitize(type: :unknown, backtrace: nil) | ||
if type == :user_error || type == :crash | ||
# If the crash is from `UI` we only want to include the stack trace | ||
# up to the point where the crash was initiated. | ||
# The two stack frames we are dropping are `method_missing` and | ||
# the call to `crash!` or `user_error!`. | ||
stack = backtrace.drop(2) | ||
else | ||
stack = backtrace | ||
end | ||
|
||
stack = remove_fastlane_gem_path(backtrace: stack) | ||
stack = remove_gem_home_path(backtrace: stack) | ||
remove_home_dir_mentions(backtrace: stack) | ||
end | ||
|
||
def remove_fastlane_gem_path(backtrace: nil) | ||
fastlane_path = Gem.loaded_specs['fastlane'].full_gem_path | ||
return backtrace unless fastlane_path | ||
backtrace.map do |frame| | ||
frame.gsub(fastlane_path, '[fastlane_path]') | ||
end | ||
end | ||
|
||
def remove_gem_home_path(backtrace: nil) | ||
return backtrace unless Gem.dir | ||
backtrace.map do |frame| | ||
frame.gsub(Gem.dir, '[gem_home]') | ||
end | ||
end | ||
|
||
def remove_home_dir_mentions(backtrace: nil) | ||
backtrace.map do |frame| | ||
frame.gsub(Dir.home, '~') | ||
end | ||
end | ||
end | ||
end | ||
end |
49 changes: 49 additions & 0 deletions
49
fastlane_core/lib/fastlane_core/crash_reporter/crash_report_generator.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
module FastlaneCore | ||
class CrashReportGenerator | ||
class << self | ||
def types | ||
{ | ||
user_error: '[USER_ERROR]', | ||
crash: '[FASTLANE_CRASH]', | ||
exception: '[EXCEPTION]', | ||
connection_failure: '[CONNECTION_FAILURE]', | ||
system: '[SYSTEM_ERROR]', | ||
option_parser: '[OPTION_PARSER]', | ||
invalid_command: '[INVALID_COMMAND]', | ||
unknown: '[UNKNOWN]' | ||
} | ||
end | ||
|
||
def generate(type: :unknown, exception: nil) | ||
message = crash_report_message(type: type, exception: exception) | ||
crash_report_payload(message: message) | ||
end | ||
|
||
private | ||
|
||
def crash_report_message(type: :unknown, exception: nil) | ||
backtrace = FastlaneCore::BacktraceSanitizer.sanitize(type: type, backtrace: exception.backtrace).join("\n") | ||
message = types[type] | ||
if type == :user_error | ||
message += ': ' | ||
else | ||
message += ": #{exception.message}" | ||
end | ||
message = message[0..100] | ||
message += "\n" unless type == :user_error | ||
message + backtrace | ||
end | ||
|
||
def crash_report_payload(message: '') | ||
{ | ||
'eventTime' => Time.now.utc.to_datetime.rfc3339, | ||
'serviceContext' => { | ||
'service' => 'fastlane', | ||
'version' => Fastlane::VERSION | ||
}, | ||
'message' => message | ||
}.to_json | ||
end | ||
end | ||
end | ||
end |
58 changes: 58 additions & 0 deletions
58
fastlane_core/lib/fastlane_core/crash_reporter/crash_reporter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
require 'faraday' | ||
require 'json' | ||
|
||
module FastlaneCore | ||
class CrashReporter | ||
class << self | ||
def crash_report_path | ||
File.join(FastlaneCore.fastlane_user_dir, 'latest_crash.json') | ||
end | ||
|
||
def enabled? | ||
!FastlaneCore::Env.truthy?("FASTLANE_OPT_OUT_CRASH_REPORTING") | ||
end | ||
|
||
def report_crash(type: :unknown, exception: nil) | ||
return unless enabled? | ||
payload = CrashReportGenerator.generate(type: type, exception: exception) | ||
send_report(payload: payload) | ||
save_file(payload: payload) | ||
show_message unless did_show_message? | ||
end | ||
|
||
private | ||
|
||
def show_message | ||
UI.message("Sending crash report...") | ||
UI.message("The stacktrace is sanitized so no personal information is sent.") | ||
UI.message("To see what we are sending, look here: #{crash_report_path}") | ||
UI.message("Learn more at https://github.com/fastlane/fastlane#crash-reporting") | ||
UI.message("You can disable crash reporting by adding `opt_out_crash_reporting` at the top of your Fastfile") | ||
end | ||
|
||
def did_show_message? | ||
file_name = ".did_show_opt_out_crash_info" | ||
|
||
path = File.join(FastlaneCore.fastlane_user_dir, file_name) | ||
did_show = File.exist?(path) | ||
|
||
return did_show if did_show | ||
|
||
File.write(path, '1') | ||
false | ||
end | ||
|
||
def save_file(payload: "{}") | ||
File.write(crash_report_path, payload) | ||
end | ||
|
||
def send_report(payload: "{}") | ||
connection = Faraday.new(url: "https://clouderrorreporting.googleapis.com/v1beta1/projects/fastlane-166414/events:report?key=AIzaSyAMACPfuI-wi4grJWEZjcPvhfV2Rhmddwo") | ||
connection.post do |request| | ||
request.headers['Content-Type'] = 'application/json' | ||
request.body = payload | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
fastlane_core/spec/crash_reporter/backtrace_sanitizer_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
describe FastlaneCore::BacktraceSanitizer do | ||
context 'path sanitization' do | ||
let(:fastlane_spec) { double('Gem::Specification') } | ||
|
||
it 'returns a trace with fastlane path sanitized' do | ||
expect(Gem).to receive(:loaded_specs).and_return({ | ||
'fastlane' => fastlane_spec | ||
}) | ||
expect(fastlane_spec).to receive(:full_gem_path).at_least(1).and_return('/path/to/fastlane') | ||
expect(FastlaneCore::BacktraceSanitizer.sanitize( | ||
backtrace: ['/path/to/fastlane/source/file'] | ||
)).to eq(['[fastlane_path]/source/file']) | ||
end | ||
|
||
it 'returns a trace with gem home sanitized' do | ||
expect(Gem).to receive(:dir).at_least(1).and_return('/path/to/gem') | ||
expect(FastlaneCore::BacktraceSanitizer.sanitize( | ||
backtrace: ['/path/to/gem/source/file'] | ||
)).to eq(['[gem_home]/source/file']) | ||
end | ||
|
||
it 'returns a trace with home directory sanitized' do | ||
expect(Dir).to receive(:home).at_least(1).and_return('/path/to/home_dir') | ||
expect(FastlaneCore::BacktraceSanitizer.sanitize( | ||
backtrace: ['/path/to/home_dir/source/file'] | ||
)).to eq(['~/source/file']) | ||
end | ||
end | ||
|
||
context 'stack frame dropping' do | ||
it 'drops the first two frames from crashes' do | ||
expect(FastlaneCore::BacktraceSanitizer.sanitize( | ||
type: :crash, | ||
backtrace: ['frame0', 'frame1', 'frame2'] | ||
)).to eq(['frame2']) | ||
end | ||
|
||
it 'drops the first two frames from user errors' do | ||
expect(FastlaneCore::BacktraceSanitizer.sanitize( | ||
type: :user_error, | ||
backtrace: ['frame0', 'frame1', 'frame2'] | ||
)).to eq(['frame2']) | ||
end | ||
|
||
it 'drops no frames from other errors' do | ||
expect(FastlaneCore::BacktraceSanitizer.sanitize( | ||
backtrace: ['frame0', 'frame1', 'frame2'] | ||
)).to eq(['frame0', 'frame1', 'frame2']) | ||
end | ||
end | ||
end |
61 changes: 61 additions & 0 deletions
61
fastlane_core/spec/crash_reporter/crash_report_generator_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
describe FastlaneCore::CrashReportGenerator do | ||
context 'generate crash report' do | ||
let(:exception) do | ||
double( | ||
'Exception', | ||
backtrace: [ | ||
'path/to/fastlane/line/that/crashed', | ||
'path/to/fastlane/line/that/called/the/crash' | ||
], | ||
message: 'message goes here' | ||
) | ||
end | ||
|
||
let(:expected_body) do | ||
{ | ||
'eventTime' => '0000-01-01T00:00:00+00:00', | ||
'serviceContext' => { | ||
'service' => 'fastlane', | ||
'version' => '2.29.0' | ||
}, | ||
'message' => "" | ||
} | ||
end | ||
|
||
before do | ||
allow(Time).to receive(:now).and_return(Time.utc(0)) | ||
end | ||
|
||
it 'omits a message for type user_error' do | ||
setup_sanitizer_expectation(type: :user_error) | ||
setup_expected_body(type: :user_error, message_text: ": ") | ||
expect(FastlaneCore::CrashReportGenerator.generate(type: :user_error, exception: exception)).to eq(expected_body.to_json) | ||
end | ||
|
||
it 'includes a message for other types' do | ||
setup_sanitizer_expectation | ||
setup_expected_body(message_text: ": #{exception.message}\n") | ||
expect(FastlaneCore::CrashReportGenerator.generate(exception: exception)).to eq(expected_body.to_json) | ||
end | ||
|
||
it 'includes stack frames in message' do | ||
setup_sanitizer_expectation | ||
setup_expected_body(message_text: ": #{exception.message}\n") | ||
report = JSON.parse(FastlaneCore::CrashReportGenerator.generate(exception: exception)) | ||
expect(report['message']).to include(exception.backtrace.join("\n")) | ||
end | ||
end | ||
end | ||
|
||
def setup_sanitizer_expectation(type: :unknown) | ||
expect(FastlaneCore::BacktraceSanitizer).to receive(:sanitize).with( | ||
type: type, | ||
backtrace: exception.backtrace | ||
) do |args| | ||
args[:backtrace] | ||
end | ||
end | ||
|
||
def setup_expected_body(type: :unknown, message_text: "") | ||
expected_body['message'] = "#{FastlaneCore::CrashReportGenerator.types[type]}#{message_text}#{exception.backtrace.join("\n")}" | ||
end |
Oops, something went wrong.