redmine_airbrake_backend 0.6.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/README.md +17 -16
- data/app/controllers/airbrake_controller.rb +113 -70
- data/app/controllers/airbrake_notice_controller.rb +22 -17
- data/app/controllers/airbrake_report_controller.rb +22 -8
- data/app/helpers/airbrake_helper.rb +18 -23
- data/app/views/airbrake/issue_description/default.erb +21 -27
- data/config/locales/de.yml +1 -1
- data/config/locales/en.yml +1 -1
- data/config/routes.rb +3 -4
- data/lib/redmine_airbrake_backend.rb +4 -0
- data/lib/redmine_airbrake_backend/backtrace_element.rb +37 -0
- data/lib/redmine_airbrake_backend/engine.rb +2 -1
- data/lib/redmine_airbrake_backend/error.rb +21 -45
- data/lib/redmine_airbrake_backend/ios_report.rb +87 -0
- data/lib/redmine_airbrake_backend/version.rb +1 -1
- data/redmine_airbrake_backend.gemspec +0 -1
- metadata +5 -21
- data/lib/redmine_airbrake_backend/report/ios.rb +0 -82
- data/lib/redmine_airbrake_backend/request.rb +0 -133
- data/lib/redmine_airbrake_backend/request/json.rb +0 -85
- data/lib/redmine_airbrake_backend/request/xml.rb +0 -122
@@ -1,50 +1,44 @@
|
|
1
|
-
<% if error.present? %>
|
2
1
|
h1. <%= error.message %>
|
3
2
|
|
4
|
-
<% if error.application.present? %>
|
5
|
-
h2. Application:
|
6
|
-
|
7
|
-
<%= format_list_item('Name', error.application[:name]) %>
|
8
|
-
<%= format_list_item('Version', error.application[:version]) %>
|
9
|
-
<% end %>
|
10
3
|
|
11
4
|
<% if error.backtrace.present? %>
|
12
5
|
h2. Stacktrace:
|
13
6
|
|
14
|
-
p((. <%= raw error.backtrace.
|
15
|
-
<% end %>
|
7
|
+
p((. <%= raw error.backtrace.map { |e| format_backtrace_element(e) }.join("\n") %>
|
16
8
|
<% end %>
|
17
9
|
|
18
|
-
<% if request.request.present? %>
|
19
|
-
h2. Request:
|
20
10
|
|
21
|
-
|
22
|
-
|
23
|
-
|
11
|
+
<% if error.application.present? %>
|
12
|
+
h2. Application:
|
13
|
+
|
14
|
+
<%= format_list_item('Name', error.application[:name]) %>
|
15
|
+
<%= format_list_item('Version', error.application[:version]) %>
|
16
|
+
<% end %>
|
17
|
+
|
24
18
|
|
25
|
-
<% if
|
19
|
+
<% if params[:params].present? %>
|
26
20
|
h2. Parameters:
|
27
21
|
|
28
|
-
<%= format_table(
|
22
|
+
<%= format_table(params[:params]) %>
|
29
23
|
<% end %>
|
30
24
|
|
31
|
-
<% if request.request[:cgi_data].present? %>
|
32
|
-
h2. Headers:
|
33
25
|
|
34
|
-
|
26
|
+
<% if params[:session].present? %>
|
27
|
+
h2. Session
|
28
|
+
|
29
|
+
<%= format_table(params[:session]) %>
|
35
30
|
<% end %>
|
36
31
|
|
37
|
-
<% if request.request[:session].present? %>
|
38
|
-
h2. Session:
|
39
32
|
|
40
|
-
|
41
|
-
|
33
|
+
<% if params[:context].present? %>
|
34
|
+
h2. Context
|
35
|
+
|
36
|
+
<%= format_table(params[:context].reject { |k, v| [:notifier].include?(k) }) %>
|
42
37
|
<% end %>
|
43
38
|
|
44
|
-
|
39
|
+
|
40
|
+
<% if params[:environment].present? %>
|
45
41
|
h2. Environment
|
46
42
|
|
47
|
-
<%=
|
48
|
-
<%= format_list_item('Directory', request.env[:project_root]) %>
|
49
|
-
<%= format_list_item('Hostname', request.env[:hostname]) %>
|
43
|
+
<%= format_table(params[:environment]) %>
|
50
44
|
<% end %>
|
data/config/locales/de.yml
CHANGED
@@ -4,7 +4,7 @@ de:
|
|
4
4
|
field_reopen_regexp: Regulärer Ausdruck für erneutes Öffnen
|
5
5
|
field_reopen_repeat_description: Neue Fehlerbeschreibung bei erneutem Öffnen anfügen
|
6
6
|
|
7
|
-
settings_airbrake_hash_field: "Benutzerdefiniertes Feld für
|
7
|
+
settings_airbrake_hash_field: "Benutzerdefiniertes Feld für Fehler-ID"
|
8
8
|
settings_airbrake_occurrences_field: "Benutzerdefiniertes Feld für # Auftreten"
|
9
9
|
|
10
10
|
project_module_airbrake: Airbrake
|
data/config/locales/en.yml
CHANGED
@@ -4,7 +4,7 @@ en:
|
|
4
4
|
field_reopen_regexp: Reopen regular expression
|
5
5
|
field_reopen_repeat_description: Add new description on reopen
|
6
6
|
|
7
|
-
settings_airbrake_hash_field: Notice
|
7
|
+
settings_airbrake_hash_field: Notice ID Custom Field
|
8
8
|
settings_airbrake_occurrences_field: Occurrences Custom Field
|
9
9
|
|
10
10
|
project_module_airbrake: Airbrake
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
|
-
#
|
3
|
-
post '/
|
4
|
-
post '/
|
5
|
-
post '/notifier_api/v3/reports/:type' => 'airbrake_report#report'
|
2
|
+
# api
|
3
|
+
post '/api/v3/projects/:project_id/notices' => 'airbrake_notice#notices'
|
4
|
+
post '/api/v3/projects/:project_id/ios-reports' => 'airbrake_report#ios_reports'
|
6
5
|
|
7
6
|
# settings
|
8
7
|
resources :projects do
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
|
4
|
+
module RedmineAirbrakeBackend
|
5
|
+
# Backtrace element received by airbrake
|
6
|
+
class BacktraceElement
|
7
|
+
attr_reader :file, :line, :function, :column
|
8
|
+
|
9
|
+
def initialize(data)
|
10
|
+
# File
|
11
|
+
@file = data[:file].presence
|
12
|
+
|
13
|
+
# Line
|
14
|
+
@line = data[:line].presence
|
15
|
+
|
16
|
+
# Function
|
17
|
+
@function = data[:function].presence
|
18
|
+
|
19
|
+
# Column
|
20
|
+
@column = data[:column].presence
|
21
|
+
end
|
22
|
+
|
23
|
+
def checksum
|
24
|
+
@_checksum ||= Digest::MD5.hexdigest("#{@file}|#{normalize_function_name(@function)}|#{@line}|#{@column}")
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def normalize_function_name(function_name)
|
30
|
+
name = @function
|
31
|
+
.downcase
|
32
|
+
.gsub(/_\d+_/, '') # ruby blocks
|
33
|
+
|
34
|
+
RedmineAirbrakeBackend.filter_hex_values(name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -36,10 +36,11 @@ module RedmineAirbrakeBackend
|
|
36
36
|
description 'Airbrake Backend for Redmine'
|
37
37
|
url 'https://github.com/ydkn/redmine_airbrake_backend'
|
38
38
|
version ::RedmineAirbrakeBackend::VERSION
|
39
|
-
requires_redmine version_or_higher: '3.
|
39
|
+
requires_redmine version_or_higher: '3.2.0'
|
40
40
|
directory RedmineAirbrakeBackend.directory
|
41
41
|
|
42
42
|
project_module :airbrake do
|
43
|
+
permission :airbrake, { airbrake_notice: [:notices], airbrake_report: [:ios_reports] }
|
43
44
|
permission :manage_airbrake, { airbrake: [:update] }
|
44
45
|
end
|
45
46
|
|
@@ -1,45 +1,43 @@
|
|
1
1
|
require 'digest/md5'
|
2
|
+
require 'redmine_airbrake_backend/backtrace_element'
|
3
|
+
|
2
4
|
|
3
5
|
module RedmineAirbrakeBackend
|
4
6
|
# Error received by airbrake
|
5
7
|
class Error
|
6
|
-
attr_accessor :request
|
7
8
|
attr_reader :type, :message, :backtrace
|
8
|
-
attr_reader :
|
9
|
-
|
10
|
-
def initialize(error_data)
|
11
|
-
# Data
|
12
|
-
@data = error_data
|
9
|
+
attr_reader :id, :subject, :application, :attachments
|
13
10
|
|
11
|
+
def initialize(data)
|
14
12
|
# Type
|
15
|
-
@type =
|
13
|
+
@type = data[:type]
|
16
14
|
|
17
15
|
# Message
|
18
|
-
@message =
|
16
|
+
@message = data[:message]
|
19
17
|
|
20
18
|
# Backtrace
|
21
|
-
@backtrace =
|
22
|
-
|
23
|
-
# Application
|
24
|
-
@application = @data[:application]
|
25
|
-
|
26
|
-
# Attachments
|
27
|
-
@attachments = @data[:attachments]
|
19
|
+
@backtrace = data[:backtrace].map { |b| BacktraceElement.new(b) }
|
28
20
|
|
29
|
-
#
|
30
|
-
@
|
21
|
+
# Error ID
|
22
|
+
@id = generate_id
|
31
23
|
|
32
24
|
# Subject
|
33
25
|
@subject = generate_subject
|
26
|
+
|
27
|
+
# Attachments
|
28
|
+
@attachments = data[:attachments].presence || []
|
29
|
+
|
30
|
+
# Application
|
31
|
+
@application = data[:application].presence
|
34
32
|
end
|
35
33
|
|
36
34
|
private
|
37
35
|
|
38
|
-
def
|
36
|
+
def generate_id
|
39
37
|
h = []
|
40
|
-
h << filter_hex_values(@type)
|
41
|
-
h << filter_hex_values(@message)
|
42
|
-
h +=
|
38
|
+
h << RedmineAirbrakeBackend.filter_hex_values(@type)
|
39
|
+
h << RedmineAirbrakeBackend.filter_hex_values(@message)
|
40
|
+
h += @backtrace.map(&:checksum).compact
|
43
41
|
|
44
42
|
Digest::MD5.hexdigest(h.compact.join("\n"))
|
45
43
|
end
|
@@ -48,34 +46,12 @@ module RedmineAirbrakeBackend
|
|
48
46
|
s = ''
|
49
47
|
|
50
48
|
if @type.blank? || @message.starts_with?("#{@type}:")
|
51
|
-
s = "[#{@
|
49
|
+
s = "[#{@id[0..7]}] #{@message}"
|
52
50
|
else
|
53
|
-
s = "[#{@
|
51
|
+
s = "[#{@id[0..7]}] #{@type}: #{@message}"
|
54
52
|
end
|
55
53
|
|
56
54
|
s[0..254].strip
|
57
55
|
end
|
58
|
-
|
59
|
-
def normalized_backtrace
|
60
|
-
if @backtrace.present?
|
61
|
-
@backtrace.map do |e|
|
62
|
-
"#{e[:file]}|#{normalize_method_name(e[:method])}|#{e[:number]}" rescue nil
|
63
|
-
end.compact
|
64
|
-
else
|
65
|
-
[]
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def normalize_method_name(method_name)
|
70
|
-
name = e[:method]
|
71
|
-
.downcase
|
72
|
-
.gsub(/_\d+_/, '') # ruby blocks
|
73
|
-
|
74
|
-
filter_hex_values(name)
|
75
|
-
end
|
76
|
-
|
77
|
-
def filter_hex_values(value)
|
78
|
-
value.gsub(/0x[0-9a-f]+/, '')
|
79
|
-
end
|
80
56
|
end
|
81
57
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'redmine_airbrake_backend/error'
|
2
|
+
|
3
|
+
|
4
|
+
module RedmineAirbrakeBackend
|
5
|
+
# iOS Report received by airbrake
|
6
|
+
class IosReport < Error
|
7
|
+
def initialize(data)
|
8
|
+
super(IosReport.parse(data))
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.parse(data)
|
14
|
+
error = {
|
15
|
+
application: {},
|
16
|
+
backtrace: [],
|
17
|
+
attachments: [],
|
18
|
+
}
|
19
|
+
|
20
|
+
header_finished = false
|
21
|
+
next_line_is_message = false
|
22
|
+
crashed_thread = false
|
23
|
+
indicent_identifier = nil
|
24
|
+
|
25
|
+
data.split("\n").each do |line|
|
26
|
+
header_finished = true if line =~ /^(Application Specific Information|Last Exception Backtrace|Thread \d+( Crashed)?):$/
|
27
|
+
|
28
|
+
unless header_finished
|
29
|
+
key, value = line.split(':', 2).map { |s| s.strip }
|
30
|
+
|
31
|
+
next if key.blank? || value.blank?
|
32
|
+
|
33
|
+
case key
|
34
|
+
when 'Exception Type'
|
35
|
+
error[:type] = value
|
36
|
+
when 'Exception Codes'
|
37
|
+
error[:message] = value
|
38
|
+
when 'Incident Identifier'
|
39
|
+
indicent_identifier = value
|
40
|
+
when 'Identifier'
|
41
|
+
error[:application][:name] = value
|
42
|
+
when 'Version'
|
43
|
+
error[:application][:version] = value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if next_line_is_message
|
48
|
+
next_line_is_message = false
|
49
|
+
|
50
|
+
error[:message] = line
|
51
|
+
|
52
|
+
if line =~ /^\*\*\* Terminating app due to uncaught exception '([^']+)', reason: '\*\*\* (.*)'$/
|
53
|
+
error[:type] = Regexp.last_match(1)
|
54
|
+
error[:message] = Regexp.last_match(2)
|
55
|
+
else
|
56
|
+
error[:message] = line
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
crashed_thread = false if line =~ /^Thread \d+:$/
|
61
|
+
|
62
|
+
if crashed_thread
|
63
|
+
if line =~ /^(\d+)\s+([^\s]+)\s+(0x[0-9a-f]+)\s+(.+) \+ (\d+)$/
|
64
|
+
error[:backtrace] << {
|
65
|
+
file: Regexp.last_match(2),
|
66
|
+
function: Regexp.last_match(4),
|
67
|
+
line: Regexp.last_match(5)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
crashed_thread = true if error[:backtrace].blank? && line =~ /^(Last Exception Backtrace|Thread \d+ Crashed):$/
|
73
|
+
|
74
|
+
next_line_is_message = true if line =~ /^Application Specific Information:$/
|
75
|
+
end
|
76
|
+
|
77
|
+
return nil if error.blank?
|
78
|
+
|
79
|
+
error[:attachments] << {
|
80
|
+
filename: "#{indicent_identifier}.crash",
|
81
|
+
data: data
|
82
|
+
} if indicent_identifier.present?
|
83
|
+
|
84
|
+
error
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redmine_airbrake_backend
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Florian Schwab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: nokogiri
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: bundler
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,17 +81,15 @@ files:
|
|
95
81
|
- db/migrate/20130708102357_create_airbrake_project_settings.rb
|
96
82
|
- db/migrate/20131003151228_add_reopen_repeat_description.rb
|
97
83
|
- lib/redmine_airbrake_backend.rb
|
84
|
+
- lib/redmine_airbrake_backend/backtrace_element.rb
|
98
85
|
- lib/redmine_airbrake_backend/engine.rb
|
99
86
|
- lib/redmine_airbrake_backend/error.rb
|
87
|
+
- lib/redmine_airbrake_backend/ios_report.rb
|
100
88
|
- lib/redmine_airbrake_backend/patches/issue_category.rb
|
101
89
|
- lib/redmine_airbrake_backend/patches/issue_priority.rb
|
102
90
|
- lib/redmine_airbrake_backend/patches/project.rb
|
103
91
|
- lib/redmine_airbrake_backend/patches/projects_helper.rb
|
104
92
|
- lib/redmine_airbrake_backend/patches/tracker.rb
|
105
|
-
- lib/redmine_airbrake_backend/report/ios.rb
|
106
|
-
- lib/redmine_airbrake_backend/request.rb
|
107
|
-
- lib/redmine_airbrake_backend/request/json.rb
|
108
|
-
- lib/redmine_airbrake_backend/request/xml.rb
|
109
93
|
- lib/redmine_airbrake_backend/version.rb
|
110
94
|
- lib/tasks/test.rake
|
111
95
|
- redmine_airbrake_backend.gemspec
|
@@ -131,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
115
|
version: '0'
|
132
116
|
requirements: []
|
133
117
|
rubyforge_project:
|
134
|
-
rubygems_version: 2.
|
118
|
+
rubygems_version: 2.5.1
|
135
119
|
signing_key:
|
136
120
|
specification_version: 4
|
137
121
|
summary: This plugin provides the necessary API to use Redmine as a Airbrake backend
|
@@ -1,82 +0,0 @@
|
|
1
|
-
module RedmineAirbrakeBackend
|
2
|
-
# Represents a report contained in a request
|
3
|
-
class Report
|
4
|
-
# iOS report
|
5
|
-
class Ios
|
6
|
-
# Parse an iOS crash log
|
7
|
-
def self.parse(data)
|
8
|
-
error = {
|
9
|
-
backtrace: [],
|
10
|
-
attachments: [],
|
11
|
-
application: {}
|
12
|
-
}
|
13
|
-
|
14
|
-
header_finished = false
|
15
|
-
next_line_is_message = false
|
16
|
-
crashed_thread = false
|
17
|
-
indicent_identifier = nil
|
18
|
-
|
19
|
-
data.split("\n").each do |line|
|
20
|
-
header_finished = true if line =~ /^(Application Specific Information|Last Exception Backtrace|Thread \d+( Crashed)?):$/
|
21
|
-
|
22
|
-
unless header_finished
|
23
|
-
key, value = line.split(':', 2).map { |s| s.strip }
|
24
|
-
|
25
|
-
next if key.blank? || value.blank?
|
26
|
-
|
27
|
-
case key
|
28
|
-
when 'Exception Type'
|
29
|
-
error[:type] = value
|
30
|
-
when 'Exception Codes'
|
31
|
-
error[:message] = value
|
32
|
-
when 'Incident Identifier'
|
33
|
-
indicent_identifier = value
|
34
|
-
when 'Identifier'
|
35
|
-
error[:application][:name] = value
|
36
|
-
when 'Version'
|
37
|
-
error[:application][:version] = value
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
if next_line_is_message
|
42
|
-
next_line_is_message = false
|
43
|
-
|
44
|
-
error[:message] = line
|
45
|
-
|
46
|
-
if line =~ /^\*\*\* Terminating app due to uncaught exception '([^']+)', reason: '\*\*\* (.*)'$/
|
47
|
-
error[:type] = Regexp.last_match(1)
|
48
|
-
error[:message] = Regexp.last_match(2)
|
49
|
-
else
|
50
|
-
error[:message] = line
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
crashed_thread = false if line =~ /^Thread \d+:$/
|
55
|
-
|
56
|
-
if crashed_thread
|
57
|
-
if line =~ /^(\d+)\s+([^\s]+)\s+(0x[0-9a-f]+)\s+(.+) \+ (\d+)$/
|
58
|
-
error[:backtrace] << {
|
59
|
-
file: Regexp.last_match(2),
|
60
|
-
method: Regexp.last_match(4),
|
61
|
-
number: Regexp.last_match(5),
|
62
|
-
}
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
crashed_thread = true if error[:backtrace].blank? && line =~ /^(Last Exception Backtrace|Thread \d+ Crashed):$/
|
67
|
-
|
68
|
-
next_line_is_message = true if line =~ /^Application Specific Information:$/
|
69
|
-
end
|
70
|
-
|
71
|
-
return nil if error.blank?
|
72
|
-
|
73
|
-
error[:attachments] << {
|
74
|
-
filename: "#{indicent_identifier}.crash",
|
75
|
-
data: data
|
76
|
-
} if indicent_identifier.present?
|
77
|
-
|
78
|
-
error
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|