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.
@@ -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.collect { |e| format_backtrace_element(e) }.join("\n") %>
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
- <%= format_list_item('URL', request.request[:url]) %>
22
- <%= format_list_item('Component', request.request[:component]) %>
23
- <%= format_list_item('Action', request.request[:action]) %>
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 request.request[:params].present? %>
19
+ <% if params[:params].present? %>
26
20
  h2. Parameters:
27
21
 
28
- <%= format_table(request.request[:params]) %>
22
+ <%= format_table(params[:params]) %>
29
23
  <% end %>
30
24
 
31
- <% if request.request[:cgi_data].present? %>
32
- h2. Headers:
33
25
 
34
- <%= format_table(request.request[:cgi_data]) %>
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
- <%= format_table(request.request[:session]) %>
41
- <% end %>
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
- <% if request.env.present? %>
39
+
40
+ <% if params[:environment].present? %>
45
41
  h2. Environment
46
42
 
47
- <%= format_list_item('Name', request.environment_name) %>
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 %>
@@ -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 Fehlernummer"
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
@@ -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 Hash Custom Field
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
@@ -1,8 +1,7 @@
1
1
  Rails.application.routes.draw do
2
- # notifier api
3
- post '/notifier_api/v2/notices' => 'airbrake_notice#notice_xml'
4
- post '/notifier_api/v3/notices' => 'airbrake_notice#notice_json'
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
@@ -4,4 +4,8 @@ module RedmineAirbrakeBackend
4
4
  def self.directory
5
5
  File.expand_path(File.join(File.dirname(__FILE__), '..'))
6
6
  end
7
+
8
+ def self.filter_hex_values(value)
9
+ value.gsub(/0x[0-9a-f]+/, '')
10
+ end
7
11
  end
@@ -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.1.0'
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 :application, :airbrake_hash, :subject, :attachments
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 = @data[:type]
13
+ @type = data[:type]
16
14
 
17
15
  # Message
18
- @message = @data[:message]
16
+ @message = data[:message]
19
17
 
20
18
  # Backtrace
21
- @backtrace = @data[: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
- # Hash
30
- @airbrake_hash = generate_hash
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 generate_hash
36
+ def generate_id
39
37
  h = []
40
- h << filter_hex_values(@type)
41
- h << filter_hex_values(@message)
42
- h += normalized_backtrace
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 = "[#{@airbrake_hash[0..7]}] #{@message}"
49
+ s = "[#{@id[0..7]}] #{@message}"
52
50
  else
53
- s = "[#{@airbrake_hash[0..7]}] #{@type}: #{@message}"
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
@@ -1,4 +1,4 @@
1
1
  module RedmineAirbrakeBackend
2
2
  # Version of this gem
3
- VERSION = '0.6.2'
3
+ VERSION = '1.0.0'
4
4
  end
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ['lib']
19
19
 
20
20
  spec.add_dependency 'rails'
21
- spec.add_dependency 'nokogiri'
22
21
 
23
22
  spec.add_development_dependency 'bundler'
24
23
  spec.add_development_dependency 'rake'
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.6.2
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: 2015-10-15 00:00:00.000000000 Z
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.4.8
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