bugloco 0.0.1 → 0.0.2

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 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