eyeloupe 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +48 -10
  4. data/app/assets/builds/eyeloupe.css +1 -1
  5. data/app/assets/javascripts/eyeloupe/controllers/eyeloupe/ai_assistant_controller.js +27 -0
  6. data/app/assets/javascripts/eyeloupe/controllers/eyeloupe/refresh_controller.js +3 -1
  7. data/app/assets/stylesheets/application.tailwind.css +8 -0
  8. data/app/assets/stylesheets/eyeloupe/application.css +0 -1
  9. data/app/controllers/eyeloupe/ai_assistant_responses_controller.rb +30 -0
  10. data/app/controllers/eyeloupe/data_controller.rb +2 -0
  11. data/app/controllers/eyeloupe/exceptions_controller.rb +29 -0
  12. data/app/models/eyeloupe/exception.rb +6 -0
  13. data/app/models/eyeloupe/in_request.rb +1 -0
  14. data/app/models/eyeloupe/out_request.rb +1 -0
  15. data/app/views/eyeloupe/exceptions/_frame.html.erb +39 -0
  16. data/app/views/eyeloupe/exceptions/index.html.erb +13 -0
  17. data/app/views/eyeloupe/exceptions/show.html.erb +126 -0
  18. data/app/views/eyeloupe/in_requests/_frame.html.erb +1 -1
  19. data/app/views/eyeloupe/in_requests/index.html.erb +1 -1
  20. data/app/views/eyeloupe/in_requests/show.html.erb +17 -1
  21. data/app/views/eyeloupe/out_requests/_frame.html.erb +1 -1
  22. data/app/views/eyeloupe/out_requests/index.html.erb +1 -1
  23. data/app/views/eyeloupe/out_requests/show.html.erb +17 -1
  24. data/app/views/layouts/eyeloupe/application.html.erb +41 -5
  25. data/config/importmap.rb +2 -1
  26. data/config/routes.rb +2 -0
  27. data/db/migrate/20230604190442_create_eyeloupe_exceptions.rb +21 -0
  28. data/lib/eyeloupe/concerns/rescuable.rb +14 -0
  29. data/lib/eyeloupe/configuration.rb +7 -0
  30. data/lib/eyeloupe/engine.rb +46 -0
  31. data/lib/eyeloupe/processors/exception.rb +71 -0
  32. data/lib/eyeloupe/processors/in_request.rb +15 -4
  33. data/lib/eyeloupe/processors/out_request.rb +10 -20
  34. data/lib/eyeloupe/request_middleware.rb +13 -1
  35. data/lib/eyeloupe/version.rb +1 -1
  36. data/lib/eyeloupe.rb +4 -1
  37. metadata +28 -4
  38. data/lib/eyeloupe/http.rb +0 -19
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="px-4 sm:px-6 lg:px-8 bg-white rounded-md shadow-md py-5">
2
2
  <div class="px-4 sm:px-0">
3
3
  <h3 class="text-xl font-semibold leading-7 text-gray-900">Request details</h3>
4
4
  </div>
@@ -80,3 +80,19 @@
80
80
  </dl>
81
81
  </div>
82
82
  </div>
83
+
84
+ <% if @request.exception.present? %>
85
+ <div class="mt-6 bg-white rounded-md shadow-md py-5">
86
+ <h3 class="text-base font-semibold leading-6 text-gray-900 px-4 sm:px-6 lg:px-8 border-b border-gray-100 pb-3">Exceptions</h3>
87
+
88
+ <%= link_to exception_path(@request.exception), class: "text-gray-600 hover:text-gray-900 hover:bg-gray-100 block px-4 sm:px-6 lg:px-8", data: {"turbo_frame": "_top"} do %>
89
+ <div class="divide-y divide-gray-100 px-0 flex justify-between">
90
+ <div class="whitespace-nowrap py-4 pl-4 pr-3 text-base font-medium text-gray-900 sm:pl-0">
91
+ <%= @request.exception.kind %>
92
+ <p class="text-sm font-normal text-gray-500"><%= @request.exception.message %></p>
93
+ </div>
94
+ </div>
95
+ <% end %>
96
+
97
+ </div>
98
+ <% end %>
@@ -23,7 +23,7 @@
23
23
  <%= render "eyeloupe/shared/verb", verb: request.verb %>
24
24
  </td>
25
25
  <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500"><%= request.hostname %></td>
26
- <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500"><%= request.path.truncate(50) %></td>
26
+ <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500"><%= request.path.truncate(100) %></td>
27
27
  <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500">
28
28
  <%= render "eyeloupe/shared/status_code", code: request.status %>
29
29
  </td>
@@ -1,4 +1,4 @@
1
- <div data-controller="eyeloupe--search">
1
+ <div data-controller="eyeloupe--search" class="px-4 sm:px-6 lg:px-8 bg-white rounded-md shadow-md py-5">
2
2
  <div class="sm:flex sm:items-center">
3
3
  <div class="sm:flex-auto">
4
4
  <h1 class="text-xl font-semibold leading-6 text-gray-900">HTTP Client</h1>
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="px-4 sm:px-6 lg:px-8 bg-white rounded-md shadow-md py-5">
2
2
  <div class="px-4 sm:px-0">
3
3
  <h3 class="text-xl font-semibold leading-7 text-gray-900">HTTP Client Request Details</h3>
4
4
  </div>
@@ -67,3 +67,19 @@
67
67
  </dl>
68
68
  </div>
69
69
  </div>
70
+
71
+ <% if @request.exception.present? %>
72
+ <div class="mt-6 bg-white rounded-md shadow-md py-5">
73
+ <h3 class="text-base font-semibold leading-6 text-gray-900 px-4 sm:px-6 lg:px-8 border-b border-gray-100 pb-3">Exceptions</h3>
74
+
75
+ <%= link_to exception_path(@request.exception), class: "text-gray-600 hover:text-gray-900 hover:bg-gray-100 block px-4 sm:px-6 lg:px-8", data: {"turbo_frame": "_top"} do %>
76
+ <div class="divide-y divide-gray-100 px-0 flex justify-between">
77
+ <div class="whitespace-nowrap py-4 pl-4 pr-3 text-base font-medium text-gray-900 sm:pl-0">
78
+ <%= @request.exception.kind %>
79
+ <p class="text-sm font-normal text-gray-500"><%= @request.exception.message %></p>
80
+ </div>
81
+ </div>
82
+ <% end %>
83
+
84
+ </div>
85
+ <% end %>
@@ -67,6 +67,23 @@
67
67
  HTTP Client
68
68
  <% end %>
69
69
  </li>
70
+ <li>
71
+ <%= link_to exceptions_path, class:"#{request.path.include?('/exceptions') ? 'bg-gray-200 text-red-500' : ''} hover:bg-gray-200 text-gray-500 group flex gap-x-3 rounded-md p-2 text-base leading-6 font-medium" do %>
72
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
73
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
74
+ <path d="M9 9v-1a3 3 0 0 1 6 0v1"></path>
75
+ <path d="M8 9h8a6 6 0 0 1 1 3v3a5 5 0 0 1 -10 0v-3a6 6 0 0 1 1 -3"></path>
76
+ <path d="M3 13l4 0"></path>
77
+ <path d="M17 13l4 0"></path>
78
+ <path d="M12 20l0 -6"></path>
79
+ <path d="M4 19l3.35 -2"></path>
80
+ <path d="M20 19l-3.35 -2"></path>
81
+ <path d="M4 7l3.75 2.4"></path>
82
+ <path d="M20 7l-3.75 2.4"></path>
83
+ </svg>
84
+ Exceptions
85
+ <% end %>
86
+ </li>
70
87
  </ul>
71
88
  </li>
72
89
  </ul>
@@ -84,7 +101,7 @@
84
101
  <%= image_tag "eyeloupe/logo.png", class: "h-12 w-auto" %>
85
102
  <h1 class="ml-2 text-2xl font-semibold text-gray-700">Eyeloupe</h1>
86
103
  <% end %>
87
- <nav class="flex flex-1 flex-col">
104
+ <nav class="flex flex-1 flex-col mt-4">
88
105
  <ul role="list" class="flex flex-1 flex-col gap-y-7">
89
106
  <li>
90
107
  <ul role="list" class="-mx-2 space-y-1">
@@ -114,6 +131,23 @@
114
131
  HTTP Client
115
132
  <% end %>
116
133
  </li>
134
+ <li>
135
+ <%= link_to exceptions_path, class:"#{request.path.include?('/exceptions') ? 'bg-gray-200 text-red-500' : ''} hover:bg-gray-200 text-gray-500 group flex gap-x-3 rounded-md p-2 text-base leading-6 font-medium" do %>
136
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
137
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
138
+ <path d="M9 9v-1a3 3 0 0 1 6 0v1"></path>
139
+ <path d="M8 9h8a6 6 0 0 1 1 3v3a5 5 0 0 1 -10 0v-3a6 6 0 0 1 1 -3"></path>
140
+ <path d="M3 13l4 0"></path>
141
+ <path d="M17 13l4 0"></path>
142
+ <path d="M12 20l0 -6"></path>
143
+ <path d="M4 19l3.35 -2"></path>
144
+ <path d="M20 19l-3.35 -2"></path>
145
+ <path d="M4 7l3.75 2.4"></path>
146
+ <path d="M20 7l-3.75 2.4"></path>
147
+ </svg>
148
+ Exceptions
149
+ <% end %>
150
+ </li>
117
151
  </ul>
118
152
  </li>
119
153
  </ul>
@@ -191,11 +225,13 @@
191
225
  </div>
192
226
 
193
227
  <main class="py-10 lg:pl-72 px-5 mx-auto">
194
-
195
- <div class="px-4 sm:px-6 lg:px-8 bg-white rounded-md shadow-md py-5">
196
- <%= yield %>
197
- </div>
228
+ <%= yield %>
198
229
  </main>
230
+
231
+ <footer class="text-gray-500 py-1 lg:pl-72 px-5 mx-auto text-sm">
232
+ Eyeloupe <%= Eyeloupe::VERSION %> - <a class="underline" href="https://github.com/alxlion/eyeloupe">Github</a>
233
+ </footer>
234
+
199
235
  </div>
200
236
  </div>
201
237
 
data/config/importmap.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  pin_all_from File.expand_path("../app/assets/javascripts", __dir__)
2
2
  pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
3
3
  pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.0.1/dist/stimulus.js"
4
- pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
4
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
5
+ pin "showdown", to: "https://ga.jspm.io/npm:showdown@2.1.0/dist/showdown.js"
data/config/routes.rb CHANGED
@@ -4,6 +4,8 @@ Eyeloupe::Engine.routes.draw do
4
4
 
5
5
  resources :in_requests, only: [:index, :show]
6
6
  resources :out_requests, only: [:index, :show]
7
+ resources :exceptions, only: [:index, :show]
8
+ resources :ai_assistant_responses, only: [:show]
7
9
 
8
10
  resource :data, only: [:destroy]
9
11
 
@@ -0,0 +1,21 @@
1
+ class CreateEyeloupeExceptions < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :eyeloupe_exceptions do |t|
4
+ t.string :hostname
5
+ t.string :kind
6
+ t.string :location
7
+ t.string :file
8
+ t.integer :line
9
+ t.text :stacktrace
10
+ t.string :message
11
+ t.integer :count, default: 1
12
+ t.text :full_message
13
+ t.references :in_request, null: true, foreign_key: { to_table: :eyeloupe_in_requests }
14
+ t.references :out_request, null: true, foreign_key: { to_table: :eyeloupe_out_requests }
15
+
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :eyeloupe_exceptions, [:kind, :file, :line]
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module Eyeloupe
2
+ module Concerns
3
+ module Rescuable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ rescue_from(StandardError) do |exception|
8
+ Eyeloupe::Processors::Exception.instance.process(nil, exception)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -11,9 +11,16 @@ module Eyeloupe
11
11
  # @return [Boolean]
12
12
  attr_accessor :capture
13
13
 
14
+ # @return [String]
15
+ attr_accessor :openai_access_key
16
+
17
+ # @return [String]
18
+ attr_accessor :openai_model
19
+
14
20
  def initialize
15
21
  @excluded_paths = %w[]
16
22
  @capture = true
23
+ @openai_model = "gpt-3.5-turbo"
17
24
  end
18
25
  end
19
26
 
@@ -12,6 +12,52 @@ module Eyeloupe
12
12
  app.config.middleware.insert(0, Eyeloupe::RequestMiddleware)
13
13
  end
14
14
 
15
+ initializer 'eyeloupe.active_job' do
16
+ ActiveSupport.on_load(:active_job) do
17
+ include Eyeloupe::Concerns::Rescuable
18
+ end
19
+ end
20
+
21
+ initializer "eyeloupe.configure_openai" do
22
+ OpenAI.configure do |config|
23
+ config.access_token = Eyeloupe::configuration.openai_access_key
24
+ end
25
+ end
26
+
27
+ initializer "eyeloupe.configure_sidekiq" do
28
+ if defined?(Sidekiq)
29
+ Sidekiq.configure_server do |config|
30
+ config.error_handlers << proc {|ex,ctx_hash| Eyeloupe::Processors::Exception.instance.process(nil, ex) }
31
+ end
32
+ end
33
+ end
34
+
35
+ initializer 'eyeloupe.override_net_http_request' do
36
+ require 'net/http'
37
+ Net::HTTP.class_eval do
38
+ alias original_request request
39
+ def request(req, body = nil, &block)
40
+ res, ex = nil
41
+ exception_processor = Eyeloupe::Processors::Exception.instance
42
+ out_request_processor = Eyeloupe::Processors::OutRequest.instance
43
+
44
+ if Eyeloupe.configuration.capture
45
+ begin
46
+ out_request_processor.init(req, body)
47
+ res = original_request(req, body, &block)
48
+ rescue => e
49
+ ex = exception_processor.process(nil, e)
50
+ ensure
51
+ out_request_processor.process(res, ex)
52
+ end
53
+ else
54
+ res = original_request(req, body, &block)
55
+ end
56
+ res
57
+ end
58
+ end
59
+ end
60
+
15
61
  initializer "eyeloupe.importmap", :before => "importmap" do |app|
16
62
  app.config.importmap.paths << root.join("config/importmap.rb")
17
63
  # https://github.com/rails/importmap-rails#sweeping-the-cache-in-development-and-test
@@ -0,0 +1,71 @@
1
+ require 'socket'
2
+ module Eyeloupe
3
+ module Processors
4
+ class Exception
5
+ include Singleton
6
+
7
+ # @param [Hash, nil] env Rack environment
8
+ # @param [Exception] exception The exception object
9
+ # @return [Eyeloupe::Exception] The exception model
10
+ def process(env, exception)
11
+ if env && env['action_dispatch.backtrace_cleaner'].present?
12
+ backtrace = env['action_dispatch.backtrace_cleaner'].filter(exception.backtrace)
13
+ backtrace = exception.backtrace if backtrace.blank?
14
+ else
15
+ backtrace = exception.backtrace
16
+ end
17
+
18
+ file = backtrace ? backtrace[0].split(":")[0] : ""
19
+ line = backtrace ? backtrace[0].split(":")[1].to_i : 0
20
+
21
+ create_or_update_exception(exception.class.name || "", file, line, backtrace, exception.message, exception.full_message)
22
+ end
23
+
24
+ protected
25
+
26
+ # @param [Array] trace The backtrace
27
+ # @return [Array] The source code lines
28
+ def read_file(trace)
29
+ file = trace.size > 0 ? trace[0].split(":")[0] : ""
30
+ line = trace.size > 0 ? trace[0].split(":")[1].to_i : 0
31
+
32
+ if File.exist?(file)
33
+ lines = File.readlines(file)
34
+ start = line - 5
35
+ start = 0 if start < 0
36
+ lines[start..line+5] || []
37
+ else
38
+ []
39
+ end
40
+ end
41
+
42
+ # @param [String] kind The exception class name
43
+ # @param [String] file The file path
44
+ # @param [Integer] line The line number
45
+ # @param [Array] backtrace The backtrace
46
+ # @param [String] message The exception message
47
+ # @param [String] full_message The full exception message
48
+ # @return [Eyeloupe::Exception] The exception model
49
+ def create_or_update_exception(kind, file, line, backtrace, message, full_message)
50
+ obj = Eyeloupe::Exception.find_by(kind: kind, file: file, line: line)
51
+
52
+ if obj
53
+ obj.update(count: obj.count + 1, updated_at: Time.now)
54
+ else
55
+ obj = Eyeloupe::Exception.create(
56
+ hostname: Socket.gethostname,
57
+ kind: kind,
58
+ message: message,
59
+ full_message: full_message,
60
+ location: read_file(backtrace || []).to_json,
61
+ file: file,
62
+ line: line,
63
+ stacktrace: (backtrace || []).to_json,
64
+ )
65
+ end
66
+
67
+ obj
68
+ end
69
+ end
70
+ end
71
+ end
@@ -31,6 +31,9 @@ module Eyeloupe
31
31
  # @return [Array]
32
32
  attr_accessor :subs
33
33
 
34
+ # @return [Eyeloupe::Exception, nil]
35
+ attr_accessor :ex
36
+
34
37
  def initialize
35
38
  @env = {}
36
39
  @request = ActionDispatch::Request.new(@env)
@@ -40,6 +43,7 @@ module Eyeloupe
40
43
  @timings = {}
41
44
  @started_at = nil
42
45
  @subs = []
46
+ @ex = nil
43
47
  end
44
48
 
45
49
  # @param [ActionDispatch::Request] request The request object
@@ -47,8 +51,9 @@ module Eyeloupe
47
51
  # @param [Integer, nil] status HTTP status code
48
52
  # @param [Hash, nil] headers HTTP headers
49
53
  # @param [String, nil] response HTTP response
54
+ # @param [Eyeloupe::Exception, nil] ex The exception object persisted in db
50
55
  # @return [Eyeloupe::Processors::InRequest]
51
- def init(request, env, status, headers, response)
56
+ def init(request, env, status, headers, response, ex)
52
57
  unsubscribe
53
58
 
54
59
  @request = request
@@ -56,6 +61,7 @@ module Eyeloupe
56
61
  @status = status
57
62
  @headers = headers
58
63
  @response = response
64
+ @ex = ex
59
65
 
60
66
  self
61
67
  end
@@ -72,7 +78,8 @@ module Eyeloupe
72
78
 
73
79
  # @return [Eyeloupe::InRequest]
74
80
  def process
75
- Eyeloupe::InRequest.create(
81
+
82
+ req = Eyeloupe::InRequest.create(
76
83
  verb: @request.request_method,
77
84
  hostname: @request.host,
78
85
  path: @env["REQUEST_URI"],
@@ -88,6 +95,10 @@ module Eyeloupe
88
95
  session: (@request.session || {}).to_json,
89
96
  response: get_response,
90
97
  )
98
+
99
+ @ex.update(in_request_id: req.id) if @ex.present? && @ex.in_request_id.blank?
100
+
101
+ req
91
102
  end
92
103
 
93
104
  protected
@@ -105,11 +116,11 @@ module Eyeloupe
105
116
 
106
117
  # @return [String, nil]
107
118
  def get_response
108
- if @request.format.to_s =~ /html/
119
+ if @request.format.to_s =~ /html/ || @headers&.to_json =~ /html/
109
120
  "HTML content"
110
121
  elsif @response.is_a?(ActionDispatch::Response)
111
122
  @response.body
112
- elsif @response.is_a?(Rack::BodyProxy)
123
+ elsif @response.is_a?(Rack::BodyProxy) && @response.respond_to?(:first)
113
124
  @response.first
114
125
  else
115
126
  @response
@@ -11,25 +11,13 @@ module Eyeloupe
11
11
  # @return [String]
12
12
  attr_accessor :body
13
13
 
14
- # @return [Hash]
15
- attr_accessor :req_headers
16
-
17
- # @return [Hash]
18
- attr_accessor :res_headers
19
-
20
- # @return [Net::HTTPResponse, nil]
21
- attr_accessor :response
22
-
23
14
  # @return [Time, nil]
24
15
  attr_accessor :started_at
25
16
 
26
17
  def initialize
27
18
  @request = nil
28
19
  @body = ""
29
- @req_headers = {}
30
- @res_headers = {}
31
20
  @started_at = nil
32
- @response = nil
33
21
  end
34
22
 
35
23
  # @param [Net::HTTPRequest] request The request object
@@ -41,22 +29,24 @@ module Eyeloupe
41
29
  end
42
30
 
43
31
  # @param [Net::HTTPResponse] response The response object
32
+ # @param [Eyeloupe::Exception, nil] ex The exception object persisted in db
44
33
  # @return [Net::HTTPResponse] The response object
45
- def process(response)
46
- @response = response
47
-
48
- Eyeloupe::OutRequest.create(
34
+ def process(response, ex)
35
+ req = Eyeloupe::OutRequest.create(
49
36
  verb: @request.method,
50
37
  hostname: @request['host'],
51
38
  path: @request.path,
52
- status: @response.code,
53
- format: @response.content_type,
39
+ status: response.code,
40
+ format: response.content_type,
54
41
  duration: (Time.now - @started_at) * 1000,
55
42
  payload: @request.body,
56
43
  req_headers: (get_headers(@request) || {}).to_json,
57
- res_headers: (get_headers(@response) || {}).to_json,
58
- response: @response.body,
44
+ res_headers: (get_headers(response) || {}).to_json,
45
+ response: response.body,
59
46
  )
47
+
48
+ ex.update(out_request_id: req.id) if ex.present? && ex.out_request_id.blank?
49
+
60
50
  response
61
51
  end
62
52
 
@@ -5,30 +5,42 @@ module Eyeloupe
5
5
  # @return [Eyeloupe::Processors::InRequest]
6
6
  attr_accessor :inrequest_processor
7
7
 
8
+ # @return [Eyeloupe::Processors::Exception]
9
+ attr_accessor :exception_processor
10
+
8
11
  def initialize(app)
9
12
  @app = app
10
13
  @inrequest_processor = Processors::InRequest.instance
14
+ @exception_processor = Processors::Exception.instance
11
15
  end
12
16
 
13
17
  # @param [Hash] env Rack environment
14
18
  def call(env)
15
19
 
16
20
  request = ActionDispatch::Request.new(env)
21
+ ex = nil
17
22
 
18
23
  if enabled?(request) && !skip_request?(request)
19
24
  @inrequest_processor.start_timer
20
25
 
21
26
  begin
22
27
  status, headers, response = @app.call(env)
28
+
29
+ framework_exception = env['action_dispatch.exception']
30
+ if framework_exception
31
+ ex = @exception_processor.process(env, framework_exception)
32
+ end
33
+
23
34
  [status, headers, response]
24
35
  rescue Exception => e
25
36
  exception = ActionDispatch::ExceptionWrapper.new(env, e)
26
37
  status = exception.status_code
27
38
  headers = {}
28
39
  response = e.message
40
+ ex = @exception_processor.process(env, e)
29
41
  raise
30
42
  ensure
31
- @inrequest_processor.init(request, env, status, headers, response).process
43
+ @inrequest_processor.init(request, env, status, headers, response, ex).process
32
44
  end
33
45
  else
34
46
  @app.call(env)
@@ -1,4 +1,4 @@
1
1
  module Eyeloupe
2
2
  # @return [String]
3
- VERSION = "0.1.0"
3
+ VERSION = "0.3.0"
4
4
  end
data/lib/eyeloupe.rb CHANGED
@@ -3,10 +3,13 @@ require "eyeloupe/engine"
3
3
 
4
4
  require 'eyeloupe/request_middleware'
5
5
  require 'eyeloupe/configuration'
6
- require 'eyeloupe/http'
7
6
  require 'eyeloupe/processors/in_request'
8
7
  require 'eyeloupe/processors/out_request'
8
+ require 'eyeloupe/processors/exception'
9
+ require 'eyeloupe/concerns/rescuable'
9
10
 
11
+ require 'pagy'
12
+ require "openai"
10
13
  module Eyeloupe
11
14
 
12
15
  # @return [Eyeloupe::Configuration]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eyeloupe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Lion
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-03 00:00:00.000000000 Z
11
+ date: 2023-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sprockets-rails
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '6.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby-openai
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 4.1.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 4.1.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: sqlite3
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +122,8 @@ dependencies:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
124
  version: '1.1'
111
- description: The All in one Rails monitoring tool
125
+ description: Eyeloupe is debug assistant for Rails. It provides a simple and elegant
126
+ way to debug your Rails application.
112
127
  email:
113
128
  - dev@alexandrelion.com
114
129
  executables: []
@@ -124,6 +139,7 @@ files:
124
139
  - app/assets/images/eyeloupe/logo.png
125
140
  - app/assets/javascripts/eyeloupe/application.js
126
141
  - app/assets/javascripts/eyeloupe/controllers/application.js
142
+ - app/assets/javascripts/eyeloupe/controllers/eyeloupe/ai_assistant_controller.js
127
143
  - app/assets/javascripts/eyeloupe/controllers/eyeloupe/nav_controller.js
128
144
  - app/assets/javascripts/eyeloupe/controllers/eyeloupe/pause_controller.js
129
145
  - app/assets/javascripts/eyeloupe/controllers/eyeloupe/refresh_controller.js
@@ -132,17 +148,23 @@ files:
132
148
  - app/assets/stylesheets/application.tailwind.css
133
149
  - app/assets/stylesheets/eyeloupe/application.css
134
150
  - app/controllers/concerns/eyeloupe/searchable.rb
151
+ - app/controllers/eyeloupe/ai_assistant_responses_controller.rb
135
152
  - app/controllers/eyeloupe/application_controller.rb
136
153
  - app/controllers/eyeloupe/configs_controller.rb
137
154
  - app/controllers/eyeloupe/data_controller.rb
155
+ - app/controllers/eyeloupe/exceptions_controller.rb
138
156
  - app/controllers/eyeloupe/in_requests_controller.rb
139
157
  - app/controllers/eyeloupe/out_requests_controller.rb
140
158
  - app/helpers/eyeloupe/application_helper.rb
141
159
  - app/jobs/eyeloupe/application_job.rb
142
160
  - app/mailers/eyeloupe/application_mailer.rb
143
161
  - app/models/eyeloupe/application_record.rb
162
+ - app/models/eyeloupe/exception.rb
144
163
  - app/models/eyeloupe/in_request.rb
145
164
  - app/models/eyeloupe/out_request.rb
165
+ - app/views/eyeloupe/exceptions/_frame.html.erb
166
+ - app/views/eyeloupe/exceptions/index.html.erb
167
+ - app/views/eyeloupe/exceptions/show.html.erb
146
168
  - app/views/eyeloupe/in_requests/_frame.html.erb
147
169
  - app/views/eyeloupe/in_requests/index.html.erb
148
170
  - app/views/eyeloupe/in_requests/show.html.erb
@@ -157,10 +179,12 @@ files:
157
179
  - config/tailwind.config.js
158
180
  - db/migrate/20230518175305_create_eyeloupe_in_requests.rb
159
181
  - db/migrate/20230525125352_create_eyeloupe_out_requests.rb
182
+ - db/migrate/20230604190442_create_eyeloupe_exceptions.rb
160
183
  - lib/eyeloupe.rb
184
+ - lib/eyeloupe/concerns/rescuable.rb
161
185
  - lib/eyeloupe/configuration.rb
162
186
  - lib/eyeloupe/engine.rb
163
- - lib/eyeloupe/http.rb
187
+ - lib/eyeloupe/processors/exception.rb
164
188
  - lib/eyeloupe/processors/in_request.rb
165
189
  - lib/eyeloupe/processors/out_request.rb
166
190
  - lib/eyeloupe/request_middleware.rb
data/lib/eyeloupe/http.rb DELETED
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'net/http'
3
- module Net
4
- class HTTP
5
- alias original_request request
6
-
7
- def request(req, body = nil, &block)
8
- if Eyeloupe.configuration.capture
9
- Eyeloupe::Processors::OutRequest.instance.init(req, body)
10
- res = original_request(req, body, &block)
11
- Eyeloupe::Processors::OutRequest.instance.process(res)
12
- else
13
- res = original_request(req, body, &block)
14
- end
15
- res
16
- end
17
-
18
- end
19
- end