gitlab_exception_notification 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe39c86fd3708a6ad6b2115ab9cad7f2d8cb7bee
4
- data.tar.gz: 3043cdf0626015276cb2bb694b458d142b0044fd
3
+ metadata.gz: 5605432556fe4818136d9665283a4cdc2539df00
4
+ data.tar.gz: d6e6c3db460a7b0042426ed2effe0cd4e35d41d9
5
5
  SHA512:
6
- metadata.gz: 6347f9bd84b8acba6eef8def9248b995b02b04b41b23bf889317887468b35ee1da11adaf462aae12c0471bdd9b6d21d10f88bfc47b38423790edba2827e1415f
7
- data.tar.gz: b04eb97d33d36fbb7e3654cc103d3377b63e64ac7eef231934261fd087454f81bc9d5b4d399c5a5276eccde4c6f568e89cdfbfd30118abff1b2093e8ce08c49a
6
+ metadata.gz: e39f635df1ecf5bec0c02398b1b2ed7c2f8e1a4b65c6c47ab526df220db91d8fa433961a7c6bdbfc23846050e4aa67c94f0080f2b9ce8e88ed4e21ec32f75a43
7
+ data.tar.gz: c5455fef59681269060cd1867fe423e4dcdcc5348d0ae490e2f3d89d72e66a45e5de9b94f94a2c04e1d5fc8aec1b83fb657d53ae464b1b0dfd8997ec72fdf2b2
@@ -1,2 +1,16 @@
1
1
  module GitlabExceptionNotification
2
+
3
+ require "gitlab"
4
+ require 'digest'
5
+
6
+ REJECT_HEADERS = /HTTP_COOKIE|(rack.*)|(action_dispatch.*)/
7
+ SLINE = "
8
+ "
9
+ STAB = SLINE + " "
10
+
11
+ PER_PAGE = 40
12
+
13
+ require 'gitlab_exception_notification/issue'
14
+ require 'gitlab_exception_notification/gitlab_notifier'
15
+
2
16
  end
@@ -1,139 +1,24 @@
1
1
 
2
- require "gitlab"
3
-
4
- REJECT_HEADERS = /HTTP_COOKIE|(rack.*)|(action_dispatch.*)/
5
- SLINE = "
6
- "
7
- STAB = SLINE + " "
8
-
9
- PER_PAGE = 40
10
2
 
11
3
  module ExceptionNotifier
12
4
  class GitlabNotifier
13
5
  def initialize(options)
14
- p "Booting issue notifications"
15
- @client = Gitlab.client(endpoint: 'http://gitlab.42.fr/api/v3', private_token: options[:private_token])
16
- @project_id = @client.project_search(options[:project_name]).first.id
17
- @issues = get_all_issues
18
- end
19
-
20
- def issue_exists?(exception)
21
- @issues = get_all_issues
22
- rest = @issues.select do |i|
23
- i.title == issue_title(exception) and i.description and i.description[exception.backtrace.first]
24
- end
25
- (rest.count > 0 ? rest.first.id : false)
26
- end
27
-
28
- def get_all_issues
29
- page = 1
30
- i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
31
- @issues = i
32
- while i.count == PER_PAGE
33
- i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
34
- @issues += i
35
- page += 1
36
- end
37
- return @issues.flatten
38
- end
39
-
40
- def update_issue(id, exception)
41
- issue = @client.issue(@project_id, id)
42
- last = issue.updated_at.to_date
43
- if last < 1.hour.ago
44
- begin
45
- @client.edit_issue(@project_id, id, {state_event: "reopen"})
46
- iss = @client.edit_issue(@project_id, id, {title: "#{issue.title}"})
47
- rescue Exception => e
48
- p "An error occured: #{e.inspect}"
49
- end
50
- else
51
- body = ":fire: This issue occured again #{Time.current}.
52
- \n#### Summary:\n
53
- #{issue_summary(exception).map { |k, v| "- #{k}: #{v}"}.join(SLINE)}
54
- "
55
- @client.reopen_issue(@project_id, id)
56
- @client.create_issue_note @project_id, id, body
57
- end
58
- end
59
-
60
-
61
- # The issue title
62
- def issue_title exception
63
- title = []
64
- title << "#{@kontroller.controller_name}##{@kontroller.action_name}" if @kontroller
65
- title << "(#{exception.class})"
66
- title << (exception.message.length > 120 ? exception.message[0..120] + "..." : exception.message)
67
- title.join(' ')
68
- end
69
-
70
- def issue_summary exception
71
- {
72
- 'URL': @request.url,
73
- 'HTTP Method': @request.request_method,
74
- 'IP address': @request.remote_ip,
75
- 'Parameters': md_hash(@request.filtered_parameters, STAB),
76
- 'Timestamp': Time.current,
77
- 'Server': Socket.gethostname,
78
- 'Rails root': (defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : nil),
79
- 'Process': $$,
80
- 'session data': md_hash(@request.session.to_hash, STAB),
81
- }
6
+ @options = options
82
7
  end
83
8
 
84
- def issue_description exception
85
-
86
- # Get a 'mardowned' backtrace
87
- m_backtrace = "```#{SLINE} #{exception.backtrace.join(SLINE)}#{SLINE}```"
88
-
89
- # Get the concerned file
90
- file = exception.backtrace.first
91
-
92
- description = ["#{exception.message} #{@kontroller ? 'in controller ' + @kontroller.controller_name + '#' + @kontroller.action_name : ''}"]
93
- description << "File: #{file}"
94
- {
95
- 'Summary': issue_summary(exception).map { |k, v| "- #{k}: #{v}"}.join(SLINE),
96
- 'session id': @request.ssl? ? "[FILTERED]" : (@request.session['session_id'] || (@request.env["rack.session.options"] and @request.env["rack.session.options"][:id])).inspect,
97
- 'data': @data,
98
- 'backtrace': m_backtrace,
99
- 'request headers': md_hash(@request.headers),
100
- 'environment': md_hash(@env.reject{|k, v| (REJECT_HEADERS =~ k).nil? })
101
- }.reject{|k, v| v.nil? or v.blank?}.each do |k, v|
102
- description << "--------------------------------"
103
- description << "#### #{k.to_s.humanize}: "
104
- description << v.to_s
105
- end
106
- description.join("\n\n")
107
- end
108
-
109
- def md_hash hash, pre = ""
110
- hash.map { |k, v| "#{pre}- **#{k}**: `#{v}`"}.join(SLINE)
111
- end
112
-
113
- def create_issue(exception)
114
- @client.create_issue(@project_id, issue_title(exception), {description: issue_description(exception)})
115
- end
116
9
 
117
10
  def exception_notification(env, exception, options={})
118
- @env = env
119
- @exception = exception
120
- @options = options.reverse_merge(env['exception_notifier.options'] || {})
121
- @kontroller = env['action_controller.instance'] || MissingController.new
122
- @request = ActionDispatch::Request.new(env)
123
- @sections = @options[:sections]
124
- @data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
125
- @sections = @sections + %w(data) unless @data.empty?
126
-
127
- if issue_id = issue_exists?(exception)
128
- update_issue(issue_id, exception)
11
+ issue = GitlabExceptionNotification::Issue.new(env, exception, options)
12
+ if issue_id = issue.exists?
13
+ issue.update(issue_id)
129
14
  else
130
- create_issue(exception)
15
+ issue.create
131
16
  end
132
17
  end
133
18
 
134
19
  def call(exception, options={})
135
20
  env = options[:env] || {}
136
- exception_notification(env, exception, options)
21
+ exception_notification(env, exception, @options.merge(options))
137
22
  end
138
23
  end
139
24
  end
@@ -0,0 +1,142 @@
1
+ module GitlabExceptionNotification
2
+
3
+ class Issue
4
+
5
+ def initialize(env, exception, options={})
6
+ @env = env
7
+ @exception = exception
8
+ @options = options.reverse_merge(env['exception_notifier.options'] || {})
9
+ @kontroller = env['action_controller.instance'] || MissingController.new
10
+ @request = ActionDispatch::Request.new(env)
11
+ @data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
12
+ @digest = digest
13
+ @client = Gitlab.client(endpoint: 'http://gitlab.42.fr/api/v3', private_token: options[:private_token])
14
+ @project_id = @client.project_search(options[:project_name]).first.id
15
+ @issues = self.all
16
+ end
17
+
18
+ def create
19
+ @client.create_issue(@project_id, title, {description: description})
20
+ end
21
+
22
+ def update(id)
23
+ issue = @client.issue(@project_id, id)
24
+ last = issue.updated_at.to_date
25
+ if last < 1.hour.ago
26
+ begin
27
+ @client.edit_issue(@project_id, id, {state_event: "reopen"})
28
+ iss = @client.edit_issue(@project_id, id, {title: increment_title(issue)})
29
+ rescue Exception => e
30
+ p "An error occured: #{e.inspect}"
31
+ end
32
+ else
33
+ body = ":fire: This issue occured again #{Time.current}.
34
+ \n#### Summary:\n
35
+ #{summary.map { |k, v| "- #{k}: #{v}"}.join(SLINE)}
36
+ "
37
+ @client.reopen_issue(@project_id, id)
38
+ @client.create_issue_note @project_id, id, body
39
+ end
40
+ end
41
+
42
+ def is_same_exception? issue
43
+ return false if issue.nil? or issue.description.nil?
44
+ issue.description.split(SLINE).last.strip == @digest
45
+ end
46
+
47
+ def exists?
48
+ # @issues = self.all
49
+ rest = @issues.select do |i|
50
+ is_same_exception?(i)
51
+ end
52
+ (rest.count > 0 ? rest.first.id : false)
53
+ end
54
+
55
+ def all
56
+ page = 1
57
+ i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
58
+ @issues = i
59
+ while i.count == PER_PAGE
60
+ i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
61
+ @issues += i
62
+ page += 1
63
+ end
64
+ return @issues.flatten
65
+ end
66
+
67
+ # =====================================================================================
68
+ # Formatters
69
+ # =====================================================================================
70
+
71
+
72
+ # The issue title
73
+ def title
74
+ t = []
75
+ t << "#{@kontroller.controller_name}##{@kontroller.action_name}" if @kontroller
76
+ t << "(#{@exception.class})"
77
+ t << (@exception.message.length > 120 ? @exception.message[0..120] + "..." : @exception.message)
78
+ t.join(' ')
79
+ end
80
+
81
+ def summary
82
+ {
83
+ 'URL': @request.url,
84
+ 'HTTP Method': @request.request_method,
85
+ 'IP address': @request.remote_ip,
86
+ 'Parameters': md_hash(@request.filtered_parameters, STAB),
87
+ 'Timestamp': Time.current,
88
+ 'Server': Socket.gethostname,
89
+ 'Rails root': (defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : nil),
90
+ 'Process': $$,
91
+ 'session data': md_hash(@request.session.to_hash, STAB),
92
+ }
93
+ end
94
+
95
+ def description
96
+ # Get a 'mardowned' backtrace
97
+ m_backtrace = "```#{SLINE} #{@exception.backtrace.join(SLINE)}#{SLINE}```"
98
+
99
+ # Get the concerned file
100
+ file = @exception.backtrace.first
101
+
102
+ d = ["#{@exception.message} #{@kontroller ? 'in controller ' + @kontroller.controller_name + '#' + @kontroller.action_name : ''}"]
103
+ d << "File: #{file}"
104
+ {
105
+ 'Summary': summary.map { |k, v| "- #{k}: #{v}"}.join(SLINE),
106
+ 'session id': @request.ssl? ? "[FILTERED]" : (@request.session['session_id'] || (@request.env["rack.session.options"] and @request.env["rack.session.options"][:id])).inspect,
107
+ 'data': @data,
108
+ 'backtrace': m_backtrace,
109
+ 'request headers': md_hash(@request.headers),
110
+ 'environment': md_hash(@env.reject{|k, v| (REJECT_HEADERS =~ k).nil? })
111
+ }.reject{|k, v| v.nil? or v.blank?}.each do |k, v|
112
+ d << "--------------------------------"
113
+ d << "#### #{k.to_s.humanize}: "
114
+ d << v.to_s
115
+ end
116
+ d << @digest
117
+ d.join(SLINE)
118
+ end
119
+
120
+ # =====================================================================================
121
+ # Utilities
122
+ # =====================================================================================
123
+
124
+ protected
125
+
126
+ def increment_title issue
127
+ count = ((issue.title =~ /^\([0-9]+\).*/) == 0 ? issue.title.gsub(/^\(([0-9]*)\)(.*)/,'\1').to_i + 1 : 1)
128
+ new_title = ((issue.title =~ /^\([0-9]+\).*/) == 0 ? issue.title.gsub(/^\(([0-9]*)\)(.*)/, '(' + count.to_s + ')\2') : "(#{count.to_s}) #{issue.title}")
129
+ return new_title
130
+ end
131
+
132
+ def md_hash hash, pre = ""
133
+ hash.map { |k, v| "#{pre}- **#{k}**: `#{v}`"}.join(SLINE)
134
+ end
135
+
136
+ def digest
137
+ "EXC" + Digest::SHA256.hexdigest(@exception.to_s + @exception.backtrace.first.split(":in").first.to_s)
138
+ end
139
+
140
+
141
+ end
142
+ end
@@ -1,3 +1,3 @@
1
1
  module GitlabExceptionNotification
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab_exception_notification
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andre Aubin
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '4.2'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 4.2.4
22
+ version: 4.2.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,27 @@ dependencies:
29
29
  version: '4.2'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 4.2.4
32
+ version: 4.2.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: exception_notification
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 4.0.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '4.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 4.0.1
33
53
  - !ruby/object:Gem::Dependency
34
54
  name: gitlab
35
55
  requirement: !ruby/object:Gem::Requirement
@@ -62,6 +82,7 @@ files:
62
82
  - Rakefile
63
83
  - lib/gitlab_exception_notification.rb
64
84
  - lib/gitlab_exception_notification/gitlab_notifier.rb
85
+ - lib/gitlab_exception_notification/issue.rb
65
86
  - lib/gitlab_exception_notification/version.rb
66
87
  - test/dummy/README.rdoc
67
88
  - test/dummy/Rakefile