5
5
Bundler . require
6
6
7
7
ENV [ "RAILS_ENV" ] = "development"
8
+ ENV [ "DATABASE_URL" ] = "sqlite3::memory:"
8
9
9
- require "action_controller"
10
+ require "action_controller/railtie"
11
+ require "active_record/railtie"
12
+ require "active_job/railtie"
10
13
11
14
class RailsMiniApp < Rails ::Application
12
15
config . hosts = nil
@@ -17,6 +20,12 @@ class RailsMiniApp < Rails::Application
17
20
config . api_only = true
18
21
config . force_ssl = false
19
22
23
+ config . active_record . logger = Logger . new ( $stdout)
24
+ config . active_record . migration_error = :page_load
25
+
26
+ config . active_job . queue_adapter = :inline
27
+ config . active_job . logger = Logger . new ( $stdout)
28
+
20
29
initializer :configure_sentry do
21
30
Sentry . init do |config |
22
31
config . dsn = ENV [ "SENTRY_DSN" ]
@@ -30,9 +39,67 @@ class RailsMiniApp < Rails::Application
30
39
config . release = "sentry-ruby-rails-mini-#{ Time . now . utc } "
31
40
32
41
config . transport . transport_class = Sentry ::DebugTransport
33
- config . sdk_debug_transport_log_file = "/workspace/sentry/log/sentry_debug_events.log"
34
42
config . background_worker_threads = 0
43
+
44
+ config . enable_logs = true
45
+ config . structured_logger_class = Sentry ::DebugStructuredLogger
46
+ config . rails . structured_logging . enabled = true
47
+ config . rails . structured_logging . attach_to = [ :active_record , :action_controller , :active_job ]
48
+ end
49
+ end
50
+ end
51
+
52
+ class Post < ActiveRecord ::Base
53
+ end
54
+
55
+ class User < ActiveRecord ::Base
56
+ end
57
+
58
+ class ApplicationJob < ActiveJob ::Base
59
+ retry_on ActiveRecord ::Deadlocked
60
+
61
+ discard_on ActiveJob ::DeserializationError
62
+ end
63
+
64
+ class SampleJob < ApplicationJob
65
+ queue_as :default
66
+
67
+ def perform ( message = "Hello from ActiveJob!" )
68
+ Rails . logger . info ( "SampleJob executed with message: #{ message } " )
69
+
70
+ Post . count
71
+ User . count
72
+
73
+ message
74
+ end
75
+ end
76
+
77
+ class DatabaseJob < ApplicationJob
78
+ queue_as :default
79
+
80
+ def perform ( post_title = "Test Post" )
81
+ Rails . logger . info ( "DatabaseJob creating post: #{ post_title } " )
82
+
83
+ post = Post . create! ( title : post_title , content : "Content for #{ post_title } " )
84
+ found_post = Post . find ( post . id )
85
+
86
+ Rails . logger . info ( "DatabaseJob found post: #{ found_post . title } " )
87
+
88
+ found_post
89
+ end
90
+ end
91
+
92
+ class FailingJob < ApplicationJob
93
+ queue_as :default
94
+
95
+ def perform ( should_fail = true )
96
+ Rails . logger . info ( "FailingJob started" )
97
+
98
+ if should_fail
99
+ raise StandardError , "Intentional job failure for testing"
35
100
end
101
+
102
+ "Job completed successfully"
36
103
end
37
104
end
38
105
@@ -61,7 +128,8 @@ def health
61
128
status : "ok" ,
62
129
timestamp : Time . now . utc . iso8601 ,
63
130
sentry_initialized : Sentry . initialized? ,
64
- log_file_writable : check_log_file_writable
131
+ log_file_writable : check_log_file_writable ,
132
+ structured_log_file_writable : check_structured_log_file_writable
65
133
}
66
134
end
67
135
@@ -70,6 +138,24 @@ def trace_headers
70
138
render json : { headers : headers }
71
139
end
72
140
141
+ def logged_events
142
+ if Sentry . logger . is_a? ( Sentry ::DebugStructuredLogger )
143
+ events = Sentry . logger . logged_events
144
+ render json : { events : events , count : events . length }
145
+ else
146
+ render json : { events : [ ] , count : 0 }
147
+ end
148
+ end
149
+
150
+ def clear_logged_events
151
+ if Sentry . logger . is_a? ( Sentry ::DebugStructuredLogger )
152
+ Sentry . logger . clear
153
+ render json : { status : "cleared" }
154
+ else
155
+ render json : { status : "no_debug_logger" }
156
+ end
157
+ end
158
+
73
159
private
74
160
75
161
def check_log_file_writable
@@ -80,6 +166,127 @@ def check_log_file_writable
80
166
false
81
167
end
82
168
169
+ def check_structured_log_file_writable
170
+ if Sentry . logger . is_a? ( Sentry ::DebugStructuredLogger )
171
+ log_file_path = Sentry . logger . log_file
172
+ File . writable? ( File . dirname ( log_file_path ) ) &&
173
+ ( !File . exist? ( log_file_path ) || File . writable? ( log_file_path ) )
174
+ else
175
+ false
176
+ end
177
+ rescue
178
+ false
179
+ end
180
+
181
+ def set_cors_headers
182
+ response . headers [ 'Access-Control-Allow-Origin' ] = '*'
183
+ response . headers [ 'Access-Control-Allow-Methods' ] = 'GET, POST, PUT, DELETE, OPTIONS'
184
+ response . headers [ 'Access-Control-Allow-Headers' ] = 'Content-Type, Authorization, sentry-trace, baggage'
185
+ end
186
+ end
187
+
188
+ class PostsController < ActionController ::Base
189
+ before_action :set_cors_headers
190
+ before_action :ensure_database_setup
191
+
192
+ def index
193
+ posts = Post . all . to_a
194
+
195
+ Sentry . logger . info ( "Posts index accessed" , posts_count : posts . length )
196
+
197
+ render json : {
198
+ posts : posts . map { |p | { id : p . id , title : p . title , content : p . content } }
199
+ }
200
+ end
201
+
202
+ def create
203
+ post = Post . create! ( post_params )
204
+
205
+ Sentry . logger . info ( "Post created" , post_id : post . id , title : post . title )
206
+
207
+ render json : { post : { id : post . id , title : post . title , content : post . content } } , status : :created
208
+ rescue ActiveRecord ::RecordInvalid => e
209
+ render json : { error : e . message } , status : :unprocessable_entity
210
+ end
211
+
212
+ def show
213
+ post = Post . find ( params [ :id ] )
214
+ render json : { post : { id : post . id , title : post . title , content : post . content } }
215
+ rescue ActiveRecord ::RecordNotFound
216
+ render json : { error : "Post not found" } , status : :not_found
217
+ end
218
+
219
+ private
220
+
221
+ def post_params
222
+ params . require ( :post ) . permit ( :title , :content )
223
+ end
224
+
225
+ def ensure_database_setup
226
+ unless ActiveRecord ::Base . connection . table_exists? ( 'posts' )
227
+ setup_database
228
+ end
229
+ end
230
+
231
+ def set_cors_headers
232
+ response . headers [ 'Access-Control-Allow-Origin' ] = '*'
233
+ response . headers [ 'Access-Control-Allow-Methods' ] = 'GET, POST, PUT, DELETE, OPTIONS'
234
+ response . headers [ 'Access-Control-Allow-Headers' ] = 'Content-Type, Authorization, sentry-trace, baggage'
235
+ end
236
+ end
237
+
238
+ class JobsController < ActionController ::Base
239
+ before_action :set_cors_headers
240
+ before_action :ensure_database_setup
241
+
242
+ def sample_job
243
+ job = SampleJob . perform_later ( "Hello from Rails mini app!" )
244
+
245
+ Sentry . logger . info ( "SampleJob enqueued" , job_id : job . job_id )
246
+
247
+ render json : {
248
+ message : "SampleJob enqueued successfully" ,
249
+ job_id : job . job_id ,
250
+ job_class : job . class . name
251
+ }
252
+ end
253
+
254
+ def database_job
255
+ title = params [ :title ] || "Test Post from Job"
256
+ job = DatabaseJob . perform_later ( title )
257
+
258
+ Sentry . logger . info ( "DatabaseJob enqueued" , job_id : job . job_id , post_title : title )
259
+
260
+ render json : {
261
+ message : "DatabaseJob enqueued successfully" ,
262
+ job_id : job . job_id ,
263
+ job_class : job . class . name ,
264
+ post_title : title
265
+ }
266
+ end
267
+
268
+ def failing_job
269
+ should_fail = params [ :should_fail ] != "false"
270
+ job = FailingJob . perform_later ( should_fail )
271
+
272
+ Sentry . logger . info ( "FailingJob enqueued" , job_id : job . job_id , should_fail : should_fail )
273
+
274
+ render json : {
275
+ message : "FailingJob enqueued successfully" ,
276
+ job_id : job . job_id ,
277
+ job_class : job . class . name ,
278
+ should_fail : should_fail
279
+ }
280
+ end
281
+
282
+ private
283
+
284
+ def ensure_database_setup
285
+ unless ActiveRecord ::Base . connection . table_exists? ( 'posts' )
286
+ setup_database
287
+ end
288
+ end
289
+
83
290
def set_cors_headers
84
291
response . headers [ 'Access-Control-Allow-Origin' ] = '*'
85
292
response . headers [ 'Access-Control-Allow-Methods' ] = 'GET, POST, PUT, DELETE, OPTIONS'
@@ -89,12 +296,43 @@ def set_cors_headers
89
296
90
297
RailsMiniApp . initialize!
91
298
299
+ def setup_database
300
+ ActiveRecord ::Schema . define do
301
+ create_table :posts , force : true do |t |
302
+ t . string :title , null : false
303
+ t . text :content
304
+ t . timestamps
305
+ end
306
+
307
+ create_table :users , force : true do |t |
308
+ t . string :name , null : false
309
+ t . string :email
310
+ t . timestamps
311
+ end
312
+ end
313
+
314
+ Post . create! ( title : "Welcome Post" , content : "Welcome to the Rails mini app!" )
315
+ Post . create! ( title : "Sample Post" , content : "This is a sample post for testing." )
316
+ User . create! ( name :
"Test User" , email :
"[email protected] " )
317
+ end
318
+
319
+ setup_database
320
+
92
321
RailsMiniApp . routes . draw do
93
322
get '/health' , to : 'events#health'
94
323
get '/error' , to : 'error#error'
95
324
get '/trace_headers' , to : 'events#trace_headers'
325
+ get '/logged_events' , to : 'events#logged_events'
326
+ post '/clear_logged_events' , to : 'events#clear_logged_events'
327
+
328
+ get '/posts' , to : 'posts#index'
329
+ post '/posts' , to : 'posts#create'
330
+ get '/posts/:id' , to : 'posts#show'
331
+
332
+ post '/jobs/sample' , to : 'jobs#sample_job'
333
+ post '/jobs/database' , to : 'jobs#database_job'
334
+ post '/jobs/failing' , to : 'jobs#failing_job'
96
335
97
- # Add CORS headers for cross-origin requests from JS app
98
336
match '*path' , to : proc { |env |
99
337
[ 200 , {
100
338
'Access-Control-Allow-Origin' => '*' ,
0 commit comments