railswatch_gem 0.1.2 → 0.1.4

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
  SHA256:
3
- metadata.gz: c3c2f8707dde0136f3ec790ae46558c384646a71d656b3659f46473732a77b5c
4
- data.tar.gz: edec55e909ad43675ba436fac4337ee23a228988b8348cba5a8b9e99322c2f20
3
+ metadata.gz: b46c253056a39e43ac5a606710eba79ec8685088111637d802580d3fa9d66388
4
+ data.tar.gz: 79146492b64604e69b19909338a0bf42892b13410b7ee49f9470715c4e8f5615
5
5
  SHA512:
6
- metadata.gz: 189aad8fdc06a51766bb19e4b4ded13f2b037839ae14dff7e2db632b8f1ae7a20b52d46dc81e60c95acf7603db804c46eae5240e31da6081b0d49e8786f77846
7
- data.tar.gz: 2d7c7ebeaf988bdffd24265bc5eb1cafe659f35b91e5bb5537725279e99de42db9ec646bd92f25b0623b32ca3924e8aa0d062d07d307a8fd0dab5c431ef99be3
6
+ metadata.gz: cc4617eb9907496f98e26384db7477fd0a10ffab8dd29462614436e9f284d50fed7c538b7dad3c1ac08cc94be9be952275e2c8dff6129c8ee03267fac45efe32
7
+ data.tar.gz: e29e08b277c75b8512290d734eb0cd9f3e3b16b112060019b12b01592dc8067d070387636e45b99e38feaaea9b13064e5e81d053f0524eaf7ad39644e8cb61a7
@@ -33,7 +33,7 @@ module RailswatchGem
33
33
 
34
34
  data = {
35
35
  event_type: "cache",
36
- timestamp: Time.at(event.end).utc.iso8601(6),
36
+ timestamp: Time.at(event.time).utc.iso8601(6),
37
37
  action: action,
38
38
  key: payload[:key],
39
39
 
@@ -37,7 +37,7 @@ module RailswatchGem
37
37
 
38
38
  data = {
39
39
  event_type: "command",
40
- timestamp: Time.at(event.end).utc.iso8601(6),
40
+ timestamp: Time.at(event.time).utc.iso8601(6),
41
41
 
42
42
  # Command Identity
43
43
  name: command_name,
@@ -23,7 +23,19 @@ module RailswatchGem
23
23
  # 1. Infinite Loop Protection
24
24
  return if error.class.name.start_with?("RailswatchGem::")
25
25
 
26
- # 2. Context Correlation
26
+ # 2. Capture Backtrace Smartly
27
+ # If error.backtrace is nil (un-raised exception), use the current caller.
28
+ raw_trace = error.backtrace || caller
29
+
30
+ # Clean the trace to remove framework noise (gems, rails internals)
31
+ # This ensures we see the User's code, not the Gem's code.
32
+ clean_trace = if defined?(::Rails.backtrace_cleaner)
33
+ ::Rails.backtrace_cleaner.clean(raw_trace)
34
+ else
35
+ raw_trace
36
+ end
37
+
38
+ # 3. Context Correlation
27
39
  request_id = context[:request_id] || Thread.current[:railswatch_request_id]
28
40
 
29
41
  data = {
@@ -33,10 +45,10 @@ module RailswatchGem
33
45
  # Exception Details
34
46
  class: error.class.name,
35
47
  message: error.message,
36
- # Limit backtrace to save bandwidth
37
- backtrace: (error.backtrace || []).first(25),
38
48
 
39
- # Rails Error Reporter Context
49
+ backtrace: clean_trace.first(25),
50
+
51
+ # Rails Context
40
52
  handled: handled,
41
53
  severity: severity,
42
54
  source: source,
@@ -38,7 +38,7 @@ module RailswatchGem
38
38
 
39
39
  data = {
40
40
  event_type: "job",
41
- timestamp: Time.at(event.end).utc.iso8601(6),
41
+ timestamp: Time.at(event.time).utc.iso8601(6),
42
42
 
43
43
  # Job Identity
44
44
  job_class: job.class.name,
@@ -13,72 +13,97 @@ module RailswatchGem
13
13
  def start
14
14
  return unless defined?(::Rails) && ::Rails.logger
15
15
 
16
- target_logger = ::Rails.logger
17
- return if target_logger.respond_to?(:_railswatch_instrumented?)
16
+ # The "Sidecar" Pattern:
17
+ # Create a dedicated logger for Railswatch and broadcast to it.
18
+ railswatch_logger = RemoteLogger.new(@client)
18
19
 
19
- interceptor = create_interceptor(@client)
20
- target_logger.extend(interceptor)
20
+ if defined?(::ActiveSupport::BroadcastLogger) && ::Rails.logger.is_a?(::ActiveSupport::BroadcastLogger)
21
+ # Rails 7.1+: Native Broadcast
22
+ ::Rails.logger.broadcast_to(railswatch_logger)
23
+ elsif defined?(::ActiveSupport::Logger) && ::ActiveSupport::Logger.respond_to?(:broadcast)
24
+ # Rails 6.0 - 7.0: ActiveSupport Broadcast
25
+ ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(railswatch_logger))
26
+ else
27
+ # Fallback: Manual Interceptor (This is what your spec hits)
28
+ attach_legacy_interceptor(::Rails.logger, railswatch_logger)
29
+ end
21
30
  end
22
31
 
23
32
  private
24
33
 
25
- def create_interceptor(client)
26
- Module.new do
27
- define_method(:_railswatch_client) { client }
28
- private :_railswatch_client
34
+ def attach_legacy_interceptor(target_logger, remote_logger)
35
+ return if target_logger.respond_to?(:_railswatch_instrumented?)
29
36
 
30
- def add(severity, message = nil, progname = nil, &block)
31
- if respond_to?(:level) && severity < level
32
- return super
37
+ interceptor = Module.new do
38
+ define_method(:add) do |severity, message = nil, progname = nil, &block|
39
+ # FIX: Check the main logger's level BEFORE forwarding.
40
+ # 'level' here refers to target_logger.level
41
+ if severity < level
42
+ return super(severity, message, progname, &block)
33
43
  end
34
44
 
35
- begin
36
- log_message = message
37
- if log_message.nil?
38
- if block_given?
39
- log_message = yield
40
- else
41
- log_message = progname
42
- end
43
- end
44
-
45
- # INFINITE LOOP PROTECTION
46
- unless log_message.to_s.include?("[Railswatch]")
47
- request_id = Thread.current[:railswatch_request_id]
48
-
49
- _railswatch_client.record({
50
- event_type: "log",
51
- timestamp: Time.now.utc.iso8601(6),
52
- severity: format_severity_level(severity),
53
- message: log_message.to_s,
54
- progname: progname,
55
- request_id: request_id
56
- })
57
- end
58
-
59
- super(severity, log_message, nil)
60
- rescue => e
61
- # Fail safe: don't crash the app if logging fails
62
- $stderr.puts "RailswatchGem: LogsInstrumenter error: #{e.message}"
63
- super
64
- end
65
- end
45
+ # Forward to our remote logger logic (safe, won't crash)
46
+ remote_logger.add(severity, message, progname, &block)
66
47
 
67
- def _railswatch_instrumented?
68
- true
48
+ # Proceed with original logging
49
+ super(severity, message, progname, &block)
69
50
  end
70
51
 
71
- private
52
+ def _railswatch_instrumented?; true; end
53
+ end
72
54
 
73
- def format_severity_level(severity)
74
- case severity
75
- when 0, ::Logger::DEBUG then "DEBUG"
76
- when 1, ::Logger::INFO then "INFO"
77
- when 2, ::Logger::WARN then "WARN"
78
- when 3, ::Logger::ERROR then "ERROR"
79
- when 4, ::Logger::FATAL then "FATAL"
80
- else "UNKNOWN"
81
- end
55
+ target_logger.extend(interceptor)
56
+ end
57
+
58
+ # --- Inner Class: The Actual Logger Logic ---
59
+ class RemoteLogger < ::Logger
60
+ def initialize(client)
61
+ @client = client
62
+ super(nil) # No IO output
63
+ self.level = DEBUG # Capture everything sent to us (filtering happens upstream)
64
+ end
65
+
66
+ def add(severity, message = nil, progname = nil, &block)
67
+ # 1. Message Resolution
68
+ content = message || (block&.call) || progname
69
+
70
+ # 2. Guard Clauses
71
+ return true if content.nil?
72
+
73
+ str_message = content.to_s.strip
74
+ return true if str_message.empty?
75
+ return true if str_message.include?("[Railswatch]")
76
+
77
+ # 3. Record
78
+ record_log(severity, str_message, progname)
79
+
80
+ true
81
+ rescue => e
82
+ $stderr.puts "RailswatchGem: Logs error: #{e.message}"
83
+ true
84
+ end
85
+
86
+ private
87
+
88
+ def record_log(severity, message, progname)
89
+ @client.record({
90
+ event_type: "log",
91
+ timestamp: Time.now.utc.iso8601(6),
92
+ severity: format_severity_level(severity),
93
+ message: message,
94
+ progname: progname,
95
+ request_id: Thread.current[:railswatch_request_id]
96
+ })
97
+ end
98
+
99
+ def format_severity_level(severity)
100
+ case severity
101
+ when 0, ::Logger::DEBUG then "DEBUG"
102
+ when 1, ::Logger::INFO then "INFO"
103
+ when 2, ::Logger::WARN then "WARN"
104
+ when 3, ::Logger::ERROR then "ERROR"
105
+ when 4, ::Logger::FATAL then "FATAL"
106
+ else "UNKNOWN"
82
107
  end
83
108
  end
84
109
  end
@@ -32,7 +32,7 @@ module RailswatchGem
32
32
 
33
33
  data = {
34
34
  event_type: "mail",
35
- timestamp: Time.at(event.end).utc.iso8601(6),
35
+ timestamp: Time.at(event.time).utc.iso8601(6),
36
36
 
37
37
  # Identity
38
38
  mailer: payload[:mailer],
@@ -37,7 +37,7 @@ module RailswatchGem
37
37
  data = {
38
38
  event_type: "notification",
39
39
  name: event.name,
40
- timestamp: Time.at(event.end).utc.iso8601(6),
40
+ timestamp: Time.at(event.time).utc.iso8601(6),
41
41
  duration_ms: event.duration.round(2),
42
42
  request_id: request_id
43
43
  }
@@ -40,7 +40,7 @@ module RailswatchGem
40
40
 
41
41
  data = {
42
42
  event_type: "outgoing_request",
43
- timestamp: Time.at(event.end).utc.iso8601(6),
43
+ timestamp: Time.at(event.time).utc.iso8601(6),
44
44
 
45
45
  # Request Details
46
46
  method: payload[:method],
@@ -38,7 +38,7 @@ module RailswatchGem
38
38
  event_type: "query",
39
39
 
40
40
  # Without this, the query logs might look 0ms off from the request logs.
41
- timestamp: Time.at(event.end).utc.iso8601(6),
41
+ timestamp: Time.at(event.time).utc.iso8601(6),
42
42
 
43
43
  # Query Details
44
44
  name: name || "SQL",
@@ -39,7 +39,7 @@ module RailswatchGem
39
39
 
40
40
  data = {
41
41
  event_type: "request",
42
- timestamp: Time.at(event.end).utc.iso8601(6),
42
+ timestamp: Time.at(event.time).utc.iso8601(6),
43
43
  request_id: request_id,
44
44
 
45
45
  # HTTP Context
@@ -43,7 +43,7 @@ module RailswatchGem
43
43
 
44
44
  data = {
45
45
  event_type: "scheduled_task",
46
- timestamp: Time.at(event.end).utc.iso8601(6),
46
+ timestamp: Time.at(event.time).utc.iso8601(6),
47
47
 
48
48
  # Task Identity
49
49
  name: task_name,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailswatchGem
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railswatch_gem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Hammett