bugloco 0.0.1 → 0.0.2

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: e01c60255d1c3940e8f6f58551df2d2df876c7d8
4
- data.tar.gz: c9cdb69fa65eb6afaf910538aa82771bf868dc2f
3
+ metadata.gz: 47458d1e986e645aa899ce0f91d9285b36e9c88f
4
+ data.tar.gz: 27fc2e6fa0eb66513114f8aa4c23ab31f795adcf
5
5
  SHA512:
6
- metadata.gz: 4ce74a4980b9e4578e4427a233d8cbf7662db256e06e9d3b6c2f7c1c347403e12da671a4f806d9df69f43a1e636fba59d48b72c2ad59c730ef387cbd2cec8052
7
- data.tar.gz: c1ba7c04fd26475ac0c0e14a665ecbdb0b365ebdcc0890da37b7823a8e280eeb3f0bdc2dd8fb94b5a7798b11e0454ad93817f641ef8ac1f851326e6eccee9ecc
6
+ metadata.gz: ac1236859a8495ce7f27485fb7169bf585f25ac6eae524d1574b942499e71243645ea32e9f9f5b2e528ab9c943fd375ab15121381a2b2174be4dadf6618f2396
7
+ data.tar.gz: 7b18d5dbf10f446744fc6793504ec248e082e2e265959290474ac5e42799dc5e8394ac15d56f176d627d7c8e95ed92183c3324a7ea19e8508bb6e5b6fed2001f
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ script: rake
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1
6
+ - ruby-head
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in bugloco.gemspec
4
4
  gemspec
5
+
6
+ gem "pry"
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Bugloco
1
+ # Bugloco [![Build Status](https://travis-ci.org/bugloco/bugloco-rb.svg?branch=master)](https://travis-ci.org/bugloco/bugloco-rb) [![Coverage Status](https://coveralls.io/repos/bugloco/bugloco-rb/badge.png)](https://coveralls.io/r/bugloco/bugloco-rb)
2
2
 
3
3
  TODO: Write a gem description
4
4
 
@@ -6,15 +6,21 @@ TODO: Write a gem description
6
6
 
7
7
  Add this line to your application's Gemfile:
8
8
 
9
- gem 'bugloco'
9
+ ```
10
+ gem 'bugloco'
11
+ ```
10
12
 
11
13
  And then execute:
12
14
 
13
- $ bundle
15
+ ```
16
+ $ bundle
17
+ ```
14
18
 
15
19
  Or install it yourself as:
16
20
 
17
- $ gem install bugloco
21
+ ```
22
+ $ gem install bugloco
23
+ ```
18
24
 
19
25
  ## Usage
20
26
 
data/bugloco.gemspec CHANGED
@@ -21,6 +21,9 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.6"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec", ">= 3.0.0"
24
+ spec.add_development_dependency "rack"
25
+ spec.add_development_dependency "coveralls"
26
+ spec.add_development_dependency "fakeweb"
24
27
 
25
- spec.add_dependency "ruby_protobuf"
28
+ spec.add_dependency "ruby_protobuf", "~> 0.4.11"
26
29
  end
@@ -0,0 +1,16 @@
1
+ require "ostruct"
2
+
3
+ module Bugloco
4
+ class Configuration < OpenStruct
5
+
6
+ def initialize(hash = {})
7
+ super
8
+
9
+ self.api_url = "https://bugloco.com/api/v1/notices"
10
+ end
11
+
12
+ def configured?
13
+ self.project_id && self.api_key
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,186 @@
1
+ require "bugloco/protobuf/bugloco.pb"
2
+ require "rack"
3
+ require "socket"
4
+
5
+ module Bugloco
6
+ class Notice
7
+
8
+ EXCEPTION_REGEX = /^(?<path>[^:]*):(?<line_number>[0-9]*):in `(?<function_name>[^']*)'$/
9
+
10
+ def initialize(exception, options = {})
11
+ @exception = exception
12
+ @options = options
13
+ @rack_env = @options[:rack_env] if @options[:rack_env]
14
+ @proto_message = nil
15
+ end
16
+
17
+ def to_pb
18
+ @pb ||= proto_message.serialize_to_string
19
+ end
20
+
21
+ def proto_message
22
+ return @proto_message unless @proto_message.nil?
23
+
24
+ @proto_message = Bugloco::Proto::Notice.new
25
+
26
+ load_company
27
+ load_project
28
+ load_exception_class_and_message
29
+ load_info_about_notifier
30
+ load_request
31
+ load_running_mode
32
+ load_server_info
33
+ load_backtrace
34
+ load_stack
35
+
36
+ @proto_message
37
+ end
38
+
39
+ private
40
+
41
+ def load_stack
42
+ @proto_message.stack = Bugloco.config.stack
43
+ end
44
+
45
+ def load_company
46
+ @proto_message.company = Bugloco::Proto::Company.new(
47
+ api_key: Bugloco.config.api_key)
48
+ end
49
+
50
+ def load_project
51
+ @proto_message.project = Bugloco::Proto::Project.new(
52
+ id: Bugloco.config.project_id)
53
+ end
54
+
55
+ def load_backtrace
56
+ return unless Bugloco.config.project_root
57
+
58
+ @exception.backtrace.each do |trace|
59
+ matches = trace.match(EXCEPTION_REGEX)
60
+ next unless matches
61
+ backtrace_entry = Bugloco::Proto::BacktraceEntry.new(
62
+ line_number: matches[:line_number].to_i,
63
+ function_name: matches[:function_name])
64
+
65
+ backtrace_entry.location_type = path_location_type(matches[:path])
66
+ backtrace_entry.full_path = matches[:path]
67
+ backtrace_entry.path = filter_path(matches[:path])
68
+
69
+ @proto_message.backtrace << backtrace_entry
70
+ end
71
+ end
72
+
73
+ def path_location_type(path)
74
+ type = nil
75
+
76
+ if path.start_with?(Bugloco.config.project_root.to_s)
77
+ type = Bugloco::Proto::LocationType::PROJECT
78
+ else
79
+ Gem.path.each do |gem_path|
80
+ if path.start_with?(gem_path)
81
+ type = Bugloco::Proto::LocationType::GEM
82
+ end
83
+ end
84
+
85
+ if type.nil?
86
+ type = Bugloco::Proto::LocationType::UNKNOWN
87
+ end
88
+ end
89
+
90
+ type
91
+ end
92
+
93
+ def filter_path(path)
94
+ if Bugloco.config.project_root
95
+ path.gsub!("#{Bugloco.config.project_root}/", "")
96
+ end
97
+
98
+ Gem.path.each do |gem_path|
99
+ path.gsub!("#{gem_path}/", "")
100
+ end
101
+
102
+ path
103
+ end
104
+
105
+
106
+ def load_server_info
107
+ @proto_message.server = Bugloco::Proto::Server.new(
108
+ hostname: Socket.gethostname,
109
+ os: RUBY_PLATFORM)
110
+ end
111
+
112
+ def load_running_mode
113
+ running_env = ENV["RAILS_ENV"] || ENV["RACK_ENV"]
114
+
115
+ running_mode = case running_env
116
+ when "test"
117
+ Bugloco::Proto::RunningMode::TEST
118
+ when "staging"
119
+ Bugloco::Proto::RunningMode::STAGING
120
+ when "production"
121
+ Bugloco::Proto::RunningMode::PRODUCTION
122
+ end
123
+
124
+ @proto_message.running_mode = running_mode
125
+ end
126
+
127
+ def load_exception_class_and_message
128
+ @proto_message.error_class = @exception.class.to_s
129
+ @proto_message.error_message = @exception.message
130
+ end
131
+
132
+ def load_info_about_notifier
133
+ @proto_message.notifier = Bugloco::Proto::Notifier.new(
134
+ name: "bugloco-rb",
135
+ version: Bugloco::VERSION
136
+ )
137
+ end
138
+
139
+ def load_request
140
+ @request = Bugloco::Proto::Request.new
141
+ @proto_message.request = @request
142
+
143
+ load_environment
144
+ load_rack_env unless @options[:rack_env].nil?
145
+ end
146
+
147
+ def load_environment
148
+ ENV.each do |k, v|
149
+ @request.environment_variables <<
150
+ Bugloco::Proto::Pair.new(key: k.to_s, value: v)
151
+ end
152
+ end
153
+
154
+ def load_rack_env
155
+ @rack_request = ::Rack::Request.new(@rack_env)
156
+
157
+ # load the parameters
158
+ request_parameters.each do |k, v|
159
+ @request.parameters << Bugloco::Proto::Pair.new(key: k.to_s, value: v.to_s)
160
+ end
161
+
162
+ # load the controller/action
163
+ @request.controller = request_parameters["controller"]
164
+ @request.action = request_parameters["action"]
165
+
166
+ load_user_from_rack_env
167
+
168
+ # Load the URL/Referer
169
+ @request.url = @rack_env["REQUEST_URI"]
170
+ @request.referer = @rack_env["HTTP_REFERER"]
171
+ end
172
+
173
+ def request_parameters
174
+ @rack_env["action_dispatch.request.parameters"] ||
175
+ @rack_request.params
176
+ end
177
+
178
+ def load_user_from_rack_env
179
+ @request.user = Bugloco::Proto::User.new(
180
+ remote_ip: @rack_env["REMOTE_IP"],
181
+ remote_host: @rack_env["REMOTE_HOST"],
182
+ user_agent: @rack_env["HTTP_USER_AGENT"]
183
+ )
184
+ end
185
+ end
186
+ end
@@ -1,13 +1,12 @@
1
1
  ### Generated by rprotoc. DO NOT EDIT!
2
2
  ### <proto file: /home/wmn/code/bug-loco/bugloco-rb/protobufs/bugloco.proto>
3
- # package bugloco.proto;
4
- #
5
3
  # syntax = "proto2";
6
- # option bugloco_proto_version = 1;
7
4
  #
8
- # enum FilelocationType {
9
- # PROJECT = 0;
10
- # LIBRARY = 1;
5
+ # package bugloco.proto;
6
+ #
7
+ # enum LocationType {
8
+ # UNKNOWN = 0;
9
+ # PROJECT = 1;
11
10
  # GEM = 2;
12
11
  # }
13
12
  #
@@ -17,21 +16,34 @@
17
16
  # PRODUCTION = 2;
18
17
  # }
19
18
  #
19
+ # enum Status {
20
+ # SUCCESS = 0;
21
+ # FAILURE = 1;
22
+ # }
23
+ #
24
+ # enum ErrorCode {
25
+ # API_KEY_INVALID = 0;
26
+ # PROJECT_NOT_FOUND = 1;
27
+ # UNKNOWN_ERROR = 2;
28
+ # }
29
+ #
20
30
  # message BacktraceEntry {
21
- # required bugloco.proto.FilelocationType location = 1;
31
+ # required bugloco.proto.LocationType location_type = 1;
22
32
  # required string full_path = 2;
23
33
  # required string path = 3;
24
- # required uint32 line_number = 4;
34
+ # required int32 line_number = 4;
25
35
  # required string function_name = 5;
26
36
  # }
27
37
  #
28
38
  # message Company {
29
- # required int32 id = 1;
30
- # required string key = 2;
39
+ # required string api_key = 1;
40
+ # optional int64 id = 2;
31
41
  # }
32
42
  #
33
43
  # message Project {
34
- # required int32 id = 1;
44
+ # required int64 id = 1;
45
+ # optional string framework = 2;
46
+ # optional string version = 3;
35
47
  # }
36
48
  #
37
49
  # message Pair {
@@ -42,22 +54,47 @@
42
54
  # message Server {
43
55
  # required string hostname = 1;
44
56
  # optional string os = 2;
45
- # optional string version = 3;
46
57
  # }
47
58
  #
48
- # message Exception {
59
+ # message Notifier {
60
+ # required string name = 1;
61
+ # required string version = 2;
62
+ # }
63
+ #
64
+ # message User {
65
+ # optional string remote_ip = 1;
66
+ # optional string remote_host = 2;
67
+ # optional string user_agent = 3;
68
+ # }
69
+ #
70
+ # message Request {
71
+ # repeated bugloco.proto.Pair environment_variables = 1;
72
+ # repeated bugloco.proto.Pair parameters = 2;
73
+ # optional string controller = 3;
74
+ # optional string action = 4;
75
+ # optional bugloco.proto.User user = 5;
76
+ # optional string url = 6;
77
+ # optional string referer = 7;
78
+ # }
79
+ #
80
+ # message Notice {
49
81
  # required bugloco.proto.Company company = 1;
50
82
  # required bugloco.proto.Project project = 2;
51
- # required string error_type = 3;
83
+ # required string error_class = 3;
52
84
  # required string error_message = 4;
53
- # repeated bugloco.proto.BacktraceEntry backtrace_entries = 5;
54
- # repeated bugloco.proto.Pair environment_variables = 6;
55
- # repeated bugloco.proto.Pair params = 7;
85
+ # required bugloco.proto.Notifier notifier = 5;
86
+ # required bugloco.proto.Request request = 6;
87
+ # repeated bugloco.proto.BacktraceEntry backtrace = 7;
56
88
  # optional string stack = 8;
57
89
  # optional bugloco.proto.RunningMode running_mode = 9;
58
90
  # optional bugloco.proto.Server server = 10;
59
- # optional string controller = 11;
60
- # optional string action = 12;
91
+ # }
92
+ #
93
+ # message Response {
94
+ # required bugloco.proto.Status status = 1;
95
+ # optional int64 notice_id = 2;
96
+ # optional bugloco.proto.ErrorCode error_code = 3;
97
+ # optional string error_message = 4;
61
98
  # }
62
99
 
63
100
  require 'protobuf/message/message'
@@ -67,11 +104,10 @@ require 'protobuf/message/extend'
67
104
 
68
105
  module Bugloco
69
106
  module Proto
70
- ::Protobuf::OPTIONS[:"bugloco_proto_version"] = 1
71
- class FilelocationType < ::Protobuf::Enum
107
+ class LocationType < ::Protobuf::Enum
72
108
  defined_in __FILE__
73
- PROJECT = value(:PROJECT, 0)
74
- LIBRARY = value(:LIBRARY, 1)
109
+ UNKNOWN = value(:UNKNOWN, 0)
110
+ PROJECT = value(:PROJECT, 1)
75
111
  GEM = value(:GEM, 2)
76
112
  end
77
113
  class RunningMode < ::Protobuf::Enum
@@ -80,22 +116,35 @@ module Bugloco
80
116
  STAGING = value(:STAGING, 1)
81
117
  PRODUCTION = value(:PRODUCTION, 2)
82
118
  end
119
+ class Status < ::Protobuf::Enum
120
+ defined_in __FILE__
121
+ SUCCESS = value(:SUCCESS, 0)
122
+ FAILURE = value(:FAILURE, 1)
123
+ end
124
+ class ErrorCode < ::Protobuf::Enum
125
+ defined_in __FILE__
126
+ API_KEY_INVALID = value(:API_KEY_INVALID, 0)
127
+ PROJECT_NOT_FOUND = value(:PROJECT_NOT_FOUND, 1)
128
+ UNKNOWN_ERROR = value(:UNKNOWN_ERROR, 2)
129
+ end
83
130
  class BacktraceEntry < ::Protobuf::Message
84
131
  defined_in __FILE__
85
- required :'bugloco::proto::FilelocationType', :location, 1
132
+ required :'bugloco::proto::LocationType', :location_type, 1
86
133
  required :string, :full_path, 2
87
134
  required :string, :path, 3
88
- required :uint32, :line_number, 4
135
+ required :int32, :line_number, 4
89
136
  required :string, :function_name, 5
90
137
  end
91
138
  class Company < ::Protobuf::Message
92
139
  defined_in __FILE__
93
- required :int32, :id, 1
94
- required :string, :key, 2
140
+ required :string, :api_key, 1
141
+ optional :int64, :id, 2
95
142
  end
96
143
  class Project < ::Protobuf::Message
97
144
  defined_in __FILE__
98
- required :int32, :id, 1
145
+ required :int64, :id, 1
146
+ optional :string, :framework, 2
147
+ optional :string, :version, 3
99
148
  end
100
149
  class Pair < ::Protobuf::Message
101
150
  defined_in __FILE__
@@ -106,22 +155,47 @@ module Bugloco
106
155
  defined_in __FILE__
107
156
  required :string, :hostname, 1
108
157
  optional :string, :os, 2
109
- optional :string, :version, 3
110
158
  end
111
- class Exception < ::Protobuf::Message
159
+ class Notifier < ::Protobuf::Message
160
+ defined_in __FILE__
161
+ required :string, :name, 1
162
+ required :string, :version, 2
163
+ end
164
+ class User < ::Protobuf::Message
165
+ defined_in __FILE__
166
+ optional :string, :remote_ip, 1
167
+ optional :string, :remote_host, 2
168
+ optional :string, :user_agent, 3
169
+ end
170
+ class Request < ::Protobuf::Message
171
+ defined_in __FILE__
172
+ repeated :'bugloco::proto::Pair', :environment_variables, 1
173
+ repeated :'bugloco::proto::Pair', :parameters, 2
174
+ optional :string, :controller, 3
175
+ optional :string, :action, 4
176
+ optional :'bugloco::proto::User', :user, 5
177
+ optional :string, :url, 6
178
+ optional :string, :referer, 7
179
+ end
180
+ class Notice < ::Protobuf::Message
112
181
  defined_in __FILE__
113
182
  required :'bugloco::proto::Company', :company, 1
114
183
  required :'bugloco::proto::Project', :project, 2
115
- required :string, :error_type, 3
184
+ required :string, :error_class, 3
116
185
  required :string, :error_message, 4
117
- repeated :'bugloco::proto::BacktraceEntry', :backtrace_entries, 5
118
- repeated :'bugloco::proto::Pair', :environment_variables, 6
119
- repeated :'bugloco::proto::Pair', :params, 7
186
+ required :'bugloco::proto::Notifier', :notifier, 5
187
+ required :'bugloco::proto::Request', :request, 6
188
+ repeated :'bugloco::proto::BacktraceEntry', :backtrace, 7
120
189
  optional :string, :stack, 8
121
190
  optional :'bugloco::proto::RunningMode', :running_mode, 9
122
191
  optional :'bugloco::proto::Server', :server, 10
123
- optional :string, :controller, 11
124
- optional :string, :action, 12
192
+ end
193
+ class Response < ::Protobuf::Message
194
+ defined_in __FILE__
195
+ required :'bugloco::proto::Status', :status, 1
196
+ optional :int64, :notice_id, 2
197
+ optional :'bugloco::proto::ErrorCode', :error_code, 3
198
+ optional :string, :error_message, 4
125
199
  end
126
200
  end
127
201
  end
@@ -0,0 +1,17 @@
1
+ module Bugloco
2
+ class Rack
3
+ def initialize(app)
4
+ @app = app
5
+ # TODO: Set the framework
6
+ end
7
+
8
+ def call(env)
9
+ response = @app.call(env)
10
+
11
+ response
12
+ rescue Exception => exception
13
+ env["bugloco.error_id"] = Bugloco.report_notice(exception, rack_env: env)
14
+ raise exception
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Bugloco
2
+ module Rails
3
+ class Middleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ response = @app.call(env)
10
+
11
+ if framework_exception = env["action_dispatch.exception"]
12
+ env["bugloco.error_id"] = Bugloco.report_notice(framework_exception, rack_env: env)
13
+ end
14
+
15
+ response
16
+ rescue Exception => exception
17
+ env["bugloco.error_id"] = Bugloco.report_notice(exception, rack_env: env)
18
+ raise exception
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ require "bugloco"
2
+ require "bugloco/rails/middleware"
3
+
4
+ module Bugloco
5
+ class Railtie < ::Rails::Railtie
6
+ initializer "bugloco.middleware" do |app|
7
+ app.config.middleware.insert_after("ActionDispatch::DebugExceptions",
8
+ "Bugloco::Rails::Middleware")
9
+ end
10
+
11
+ config.after_initialize do
12
+ Bugloco.config do |config|
13
+ config.framework = "Rails"
14
+ config.framework_version = ::Rails::VERSION::STRING
15
+ config.project_root = ::Rails.root
16
+ config.environment = ::Rails.env
17
+ config.logger = ::Rails.logger
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Bugloco
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/bugloco.rb CHANGED
@@ -1,31 +1,74 @@
1
1
  require "bugloco/version"
2
- require "bugloco/protobuf/bugloco.pb"
2
+ require "bugloco/rack"
3
+ require "bugloco/notice"
4
+ require "bugloco/configuration"
5
+ require "net/http"
6
+ require "net/https"
7
+
8
+ require 'bugloco/railtie' if defined?(Rails::Railtie)
3
9
 
4
10
  module Bugloco
5
- class Exception
6
- def initialize(exception)
7
- @exception = exception
8
- @proto_message = nil
11
+ class << self
12
+ HTTP_ERRORS = [
13
+ Timeout::Error,
14
+ Errno::EINVAL,
15
+ Errno::ECONNRESET,
16
+ EOFError,
17
+ Net::HTTPBadResponse,
18
+ Net::HTTPHeaderSyntaxError,
19
+ Net::ProtocolError,
20
+ Errno::ECONNREFUSED,
21
+ OpenSSL::SSL::SSLError].freeze
22
+
23
+ def report_notice(exception, options = {})
24
+ # TODO: We need to inform the user if api_key/project_id were not given
25
+ encoded_notice = Bugloco::Notice.new(exception, options).to_pb
26
+ encoded_response = send_notice(encoded_notice, options)
27
+
28
+ if encoded_response
29
+ response = Bugloco::Proto::Response.new.
30
+ parse_from_string(encoded_response)
31
+
32
+ response.notice_id if response.status == Bugloco::Proto::Status::SUCCESS
33
+ else
34
+ nil
35
+ end
36
+ end
37
+
38
+ def config
39
+ @configuration ||= Bugloco::Configuration.new
40
+ yield(@configuration) if block_given?
41
+ @configuration
9
42
  end
10
43
 
11
- def prepare_proto_message
12
- return @proto_message unless @proto_message.nil?
44
+ private
45
+
46
+ def send_notice(encoded_notice, options = {})
47
+ response = http_connection.post(api_url.path, encoded_notice, headers)
48
+ response.body
49
+ rescue *HTTP_ERRORS => e
50
+ # log e
51
+
52
+ nil
53
+ end
13
54
 
14
- @proto_message = Bugloco::Proto::Exception.new
55
+ def http_connection
56
+ http = Net::HTTP.new(api_url.host, api_url.port)
15
57
 
16
- load_environment
58
+ # do some SSL magic
17
59
 
18
- @proto_message
60
+ http
19
61
  end
20
62
 
21
- def load_environment
22
- ENV.each do |k, v|
23
- pair = Bugloco::Proto::Pair.new
24
- pair.key = k
25
- pair.value = v
63
+ def headers
64
+ {
65
+ "Content-type" => "application/octet-stream",
66
+ "Accept" => "application/octet-stream"
67
+ }.freeze
68
+ end
26
69
 
27
- @proto_message.environment_variables << pair
28
- end
70
+ def api_url
71
+ URI.parse(Bugloco.config.api_url)
29
72
  end
30
73
  end
31
74
  end
@@ -1,11 +1,10 @@
1
- package bugloco.proto;
2
-
3
1
  syntax = "proto2";
4
- option bugloco_proto_version = 1;
5
2
 
6
- enum FilelocationType {
7
- PROJECT = 0;
8
- LIBRARY = 1;
3
+ package bugloco.proto;
4
+
5
+ enum LocationType {
6
+ UNKNOWN = 0;
7
+ PROJECT = 1;
9
8
  GEM = 2;
10
9
  }
11
10
 
@@ -15,21 +14,34 @@ enum RunningMode {
15
14
  PRODUCTION = 2;
16
15
  }
17
16
 
17
+ enum Status {
18
+ SUCCESS = 0;
19
+ FAILURE = 1;
20
+ }
21
+
22
+ enum ErrorCode {
23
+ API_KEY_INVALID = 0;
24
+ PROJECT_NOT_FOUND = 1;
25
+ UNKNOWN_ERROR = 2;
26
+ }
27
+
18
28
  message BacktraceEntry {
19
- required bugloco.proto.FilelocationType location = 1;
29
+ required bugloco.proto.LocationType location_type = 1;
20
30
  required string full_path = 2;
21
31
  required string path = 3;
22
- required uint32 line_number = 4;
32
+ required int32 line_number = 4;
23
33
  required string function_name = 5;
24
34
  }
25
35
 
26
36
  message Company {
27
- required int32 id = 1;
28
- required string key = 2;
37
+ required string api_key = 1;
38
+ optional int64 id = 2;
29
39
  }
30
40
 
31
41
  message Project {
32
- required int32 id = 1;
42
+ required int64 id = 1;
43
+ optional string framework = 2;
44
+ optional string version = 3;
33
45
  }
34
46
 
35
47
  message Pair {
@@ -40,20 +52,45 @@ message Pair {
40
52
  message Server {
41
53
  required string hostname = 1;
42
54
  optional string os = 2;
43
- optional string version = 3;
44
55
  }
45
56
 
46
- message Exception {
57
+ message Notifier {
58
+ required string name = 1;
59
+ required string version = 2;
60
+ }
61
+
62
+ message User {
63
+ optional string remote_ip = 1;
64
+ optional string remote_host = 2;
65
+ optional string user_agent = 3;
66
+ }
67
+
68
+ message Request {
69
+ repeated bugloco.proto.Pair environment_variables = 1;
70
+ repeated bugloco.proto.Pair parameters = 2;
71
+ optional string controller = 3;
72
+ optional string action = 4;
73
+ optional bugloco.proto.User user = 5;
74
+ optional string url = 6;
75
+ optional string referer = 7;
76
+ }
77
+
78
+ message Notice {
47
79
  required bugloco.proto.Company company = 1;
48
80
  required bugloco.proto.Project project = 2;
49
- required string error_type = 3;
81
+ required string error_class = 3;
50
82
  required string error_message = 4;
51
- repeated bugloco.proto.BacktraceEntry backtrace_entries = 5;
52
- repeated bugloco.proto.Pair environment_variables = 6;
53
- repeated bugloco.proto.Pair params = 7;
83
+ required bugloco.proto.Notifier notifier = 5;
84
+ required bugloco.proto.Request request = 6;
85
+ repeated bugloco.proto.BacktraceEntry backtrace = 7;
54
86
  optional string stack = 8;
55
87
  optional bugloco.proto.RunningMode running_mode = 9;
56
88
  optional bugloco.proto.Server server = 10;
57
- optional string controller = 11;
58
- optional string action = 12;
89
+ }
90
+
91
+ message Response {
92
+ required bugloco.proto.Status status = 1;
93
+ optional int64 notice_id = 2;
94
+ optional bugloco.proto.ErrorCode error_code = 3;
95
+ optional string error_message = 4;
59
96
  }
@@ -0,0 +1,8 @@
1
+ describe Bugloco::Configuration do
2
+ context "configured?" do
3
+ it "should return true if all required config are there" do
4
+ @configuration = Bugloco::Configuration.new
5
+ expect(@configuration.configured?).to be_falsey
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,217 @@
1
+ describe Bugloco::Notice do
2
+ def run_rack_app(params_type = :query_string)
3
+ @extra_env.each {|k, v| ENV[k] = v}
4
+
5
+ @request_options = {
6
+ "action_dispatch.request.parameters" => @params,
7
+ }
8
+
9
+ if params_type == :action_dispatch
10
+ @rack_env = Rack::MockRequest.env_for(@url, @request_options)
11
+ else
12
+ @rack_env = Rack::MockRequest.env_for(@url)
13
+ end
14
+
15
+ @rack_env.merge! @rack_env_extra
16
+
17
+ @app = lambda do |env|
18
+ raise CustomBacktraceException, @error_message
19
+ end
20
+
21
+ begin
22
+ @stack = Bugloco::Rack.new(@app)
23
+ @stack.call(@rack_env)
24
+ rescue CustomBacktraceException => exception
25
+ @exception = mock_backtrace(exception)
26
+ @notice = Bugloco::Notice.new(@exception, rack_env: @rack_env)
27
+ @notice_proto_message = @notice.proto_message
28
+ end
29
+ end
30
+
31
+ before do
32
+ allow(Bugloco).to receive(:report_notice)
33
+
34
+ @extra_env = {}
35
+
36
+ @error_message = "Fake error needed for the builder"
37
+ @url = "http://www.google.com?name=John%20Doe"
38
+
39
+ @params = {
40
+ "name" => "John Doe",
41
+ "controller" => "home",
42
+ "action" => "greet",
43
+ }
44
+
45
+ @rack_env_extra = {}
46
+
47
+
48
+
49
+ end
50
+
51
+ after do
52
+ @extra_env.each {|k, _| ENV[k] = nil}
53
+ end
54
+
55
+ it "should include the company" do
56
+ @api_key = "secret_key"
57
+ Bugloco.config do |config|
58
+ config.api_key = @api_key
59
+ end
60
+ expected = Bugloco::Proto::Company.new(api_key: @api_key)
61
+
62
+ run_rack_app
63
+ expect(@notice_proto_message.company).to eq(expected)
64
+ end
65
+
66
+ it "should include the project" do
67
+ @project_id = 123
68
+ Bugloco.config do |config|
69
+ config.project_id = @project_id
70
+ end
71
+ expected = Bugloco::Proto::Project.new(id: @project_id)
72
+
73
+ run_rack_app
74
+ expect(@notice_proto_message.project).to eq(expected)
75
+ end
76
+
77
+ it "should include the stack" do
78
+ @stack_name = "sysadmin"
79
+ Bugloco.config do |config|
80
+ config.stack = @stack_name
81
+ end
82
+
83
+ run_rack_app
84
+ expect(@notice_proto_message.stack).to eq(@stack_name)
85
+ end
86
+
87
+ it "should collect the environment" do
88
+ @extra_env["BUGLOCO_KEY"] = "VAR"
89
+
90
+ run_rack_app
91
+ expected = Bugloco::Proto::Pair.new(key: "BUGLOCO_KEY", value: "VAR")
92
+ env_vars = @notice_proto_message.request.environment_variables
93
+ expect(env_vars).to include(expected)
94
+ end
95
+
96
+ it "should collect the class/message of the notice" do
97
+ run_rack_app
98
+ expect(@notice_proto_message.error_class).to eq("CustomBacktraceException")
99
+ expect(@notice_proto_message.error_message).to eq(@error_message)
100
+ end
101
+
102
+ it "should include information about the notifier" do
103
+ run_rack_app
104
+
105
+ expected = Bugloco::Proto::Notifier.new(
106
+ name: "Bugloco",
107
+ version: Bugloco::VERSION
108
+ )
109
+
110
+ expect(@notice_proto_message.notifier).to eq(expected)
111
+ end
112
+
113
+ it "should include information about the current server" do
114
+ run_rack_app
115
+ expected = Bugloco::Proto::Server.new(
116
+ hostname: `hostname`.chomp,
117
+ os: RUBY_PLATFORM)
118
+
119
+ expect(@notice_proto_message.server).to eq(expected)
120
+ end
121
+
122
+ context "rack" do
123
+ it "should extract the parameters from action dispatch" do
124
+ run_rack_app :action_dispatch
125
+ expected = Bugloco::Proto::Pair.new(key: "name", value: "John Doe")
126
+ expect(@notice_proto_message.request.parameters).to include(expected)
127
+ end
128
+
129
+ it "should extract the parameters from the query string" do
130
+ run_rack_app
131
+ expected = Bugloco::Proto::Pair.new(key: "name", value: "John Doe")
132
+ expect(@notice_proto_message.request.parameters).to include(expected)
133
+ end
134
+
135
+ it "should extract the controller/action from the rack_env" do
136
+ run_rack_app :action_dispatch
137
+ expect(@notice_proto_message.request.controller).to eq("home")
138
+ expect(@notice_proto_message.request.action).to eq("greet")
139
+ end
140
+
141
+ it "should collect info about the user (IP/user_agent)" do
142
+ @rack_env_extra.merge!({
143
+ "REMOTE_IP" => "8.8.8.8",
144
+ "REMOTE_HOST" => "google-public-dns-a.google.com",
145
+ "HTTP_USER_AGENT" => "Mozilla something :)",
146
+ })
147
+
148
+ run_rack_app
149
+ expected = Bugloco::Proto::User.new(
150
+ remote_ip: @rack_env_extra["REMOTE_IP"],
151
+ remote_host: @rack_env_extra["REMOTE_HOST"],
152
+ user_agent: @rack_env_extra["HTTP_USER_AGENT"]
153
+ )
154
+
155
+ expect(@notice_proto_message.request.user).to eq(expected)
156
+ end
157
+
158
+ it "should collect the URL" do
159
+ @rack_env_extra["REQUEST_URI"] = @url
160
+ run_rack_app
161
+ expect(@notice_proto_message.request.url).to eq(@rack_env_extra["REQUEST_URI"])
162
+ end
163
+
164
+ it "should collect the REFERER" do
165
+ @rack_env_extra["HTTP_REFERER"] = "http://www.google.com"
166
+ run_rack_app
167
+ expect(@notice_proto_message.request.referer).to eq(@rack_env_extra["HTTP_REFERER"])
168
+ end
169
+
170
+ it "should include information about RACK_ENV" do
171
+ @extra_env["RACK_ENV"] = "production"
172
+ run_rack_app
173
+ expected = Bugloco::Proto::RunningMode::PRODUCTION
174
+ expect(@notice_proto_message.running_mode).to eq(expected)
175
+ end
176
+ end
177
+
178
+ context "rails" do
179
+ before do
180
+ Bugloco.config do |config|
181
+ config.project_root = File.expand_path("../../../", __FILE__)
182
+ end
183
+ end
184
+
185
+ it "should record the backtrace" do
186
+ project_root = Bugloco.config.project_root
187
+ first = Bugloco::Proto::BacktraceEntry.new(
188
+ location_type: Bugloco::Proto::LocationType::PROJECT,
189
+ full_path: File.join(project_root, "spec/bugloco/notice_spec.rb"),
190
+ path: "spec/bugloco/notice_spec.rb",
191
+ line_number: 18,
192
+ function_name: "block in run_rack_app")
193
+ second = Bugloco::Proto::BacktraceEntry.new(
194
+ location_type: Bugloco::Proto::LocationType::PROJECT,
195
+ full_path: File.join(project_root, "lib/bugloco/rack.rb"),
196
+ path: "lib/bugloco/rack.rb",
197
+ line_number: 9,
198
+ function_name: "call")
199
+ third = Bugloco::Proto::BacktraceEntry.new(
200
+ location_type: Bugloco::Proto::LocationType::GEM,
201
+ full_path: "#{Gem.path.last}/gems/rspec-core-3.0.2/lib/rspec/core/example.rb",
202
+ path: "gems/rspec-core-3.0.2/lib/rspec/core/example.rb",
203
+ line_number: 148,
204
+ function_name: "instance_exec")
205
+
206
+ run_rack_app
207
+ expect(@notice_proto_message.backtrace).to eq([first, second, third])
208
+ end
209
+
210
+ it "should include information about RAILS_ENV" do
211
+ @extra_env["RAILS_ENV"] = "staging"
212
+ run_rack_app
213
+ expected = Bugloco::Proto::RunningMode::STAGING
214
+ expect(@notice_proto_message.running_mode).to eq(expected)
215
+ end
216
+ end
217
+ end
data/spec/bugloco_spec.rb CHANGED
@@ -1,26 +1,41 @@
1
- describe Bugloco::Exception do
2
- before do
3
- ENV["BUGLOCO_KEY"] = "VAR"
1
+ describe Bugloco do
2
+ context "#config" do
3
+ it "should be configurable" do
4
+ project_id = 123
5
+ api_key = "secret_key"
4
6
 
5
- begin
6
- raise RuntimeError, "Fake error needed for the builder"
7
- rescue RuntimeError => exception
8
- @exception = Bugloco::Exception.new(exception)
9
- @exception_proto_message = @exception.prepare_proto_message
7
+ Bugloco.config do |config|
8
+ config.project_id = project_id
9
+ config.api_key = api_key
10
+ end
11
+
12
+ expect(Bugloco.config.project_id).to eq(project_id)
13
+ expect(Bugloco.config.api_key).to eq(api_key)
10
14
  end
11
- end
12
15
 
13
- after do
14
- ENV["BUGLOCO_KEY"] = nil
16
+ it "should include the API url" do
17
+ expect(Bugloco.config.api_url).to eq("https://bugloco.com/api/v1/notices")
18
+ end
15
19
  end
16
20
 
17
- context "message_builder" do
18
- it "should collect the environment" do
19
- expected = Bugloco::Proto::Pair.new
20
- expected.key = "BUGLOCO_KEY"
21
- expected.value = "VAR"
21
+ context "#report_notice" do
22
+ it "should return success when the proto gets built and sent" do
23
+ pending "FakeWeb cannot receive binary, how to test this?"
24
+
25
+ response = Bugloco::Proto::Response.new(
26
+ status: Bugloco::Proto::Status::SUCCESS,
27
+ notice_id: 123)
28
+ FakeWeb.register_uri(:post, Bugloco.config.api_url,
29
+ body: response.serialize_to_string)
30
+
31
+ begin
32
+ raise CustomBacktraceException, "Fake web"
33
+ rescue CustomBacktraceException => exception
34
+ exception = mock_backtrace(exception)
35
+ api_response = Bugloco.report_notice(exception)
36
+ end
22
37
 
23
- expect(@exception_proto_message.environment_variables).to include(expected)
38
+ expect(api_response).to eq(response)
24
39
  end
25
40
  end
26
41
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,32 @@
1
+ if ENV["CI"]
2
+ require "coveralls"
3
+ Coveralls.wear!
4
+ end
5
+
6
+ require "fakeweb"
1
7
  require "bugloco"
2
8
 
9
+ def mock_backtrace(exception)
10
+ project_root = File.expand_path("../../", __FILE__)
11
+ backtrace = [
12
+ "#{project_root}/spec/bugloco/notice_spec.rb:18:in `block in run_rack_app'",
13
+ "#{project_root}/lib/bugloco/rack.rb:9:in `call'",
14
+ "#{Gem.path.last}/gems/rspec-core-3.0.2/lib/rspec/core/example.rb:148:in `instance_exec'"
15
+ ]
16
+
17
+ exception.backtrace = backtrace
18
+
19
+ exception
20
+ end
21
+
22
+ class CustomBacktraceException < Exception
23
+ attr_accessor :backtrace
24
+ end
25
+
3
26
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
4
27
  RSpec.configure do |config|
28
+ config.before(:each) do
29
+ ENV["RACK_ENV"] = nil
30
+ ENV["RAILS_ENV"] = nil
31
+ end
5
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugloco
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wael M. Nasreddine
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-18 00:00:00.000000000 Z
11
+ date: 2014-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,19 +53,61 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: 3.0.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: ruby_protobuf
56
+ name: rack
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
- type: :runtime
62
+ type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: fakeweb
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: ruby_protobuf
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.4.11
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.4.11
69
111
  description: Bugloco ruby gem for sending exceptions to Bugloco
70
112
  email:
71
113
  - wael.nasreddine@gmail.com
@@ -75,15 +117,23 @@ extra_rdoc_files: []
75
117
  files:
76
118
  - ".gitignore"
77
119
  - ".rspec"
120
+ - ".travis.yml"
78
121
  - Gemfile
79
122
  - LICENSE.txt
80
123
  - README.md
81
124
  - Rakefile
82
125
  - bugloco.gemspec
83
126
  - lib/bugloco.rb
127
+ - lib/bugloco/configuration.rb
128
+ - lib/bugloco/notice.rb
84
129
  - lib/bugloco/protobuf/bugloco.pb.rb
130
+ - lib/bugloco/rack.rb
131
+ - lib/bugloco/rails/middleware.rb
132
+ - lib/bugloco/railtie.rb
85
133
  - lib/bugloco/version.rb
86
134
  - protobufs/bugloco.proto
135
+ - spec/bugloco/configuration_spec.rb
136
+ - spec/bugloco/notice_spec.rb
87
137
  - spec/bugloco_spec.rb
88
138
  - spec/spec_helper.rb
89
139
  homepage: https://bugloco.com
@@ -111,5 +161,7 @@ signing_key:
111
161
  specification_version: 4
112
162
  summary: Bugloco ruby gem for sending exceptions to Bugloco
113
163
  test_files:
164
+ - spec/bugloco/configuration_spec.rb
165
+ - spec/bugloco/notice_spec.rb
114
166
  - spec/bugloco_spec.rb
115
167
  - spec/spec_helper.rb