ez_logs_agent 0.1.0

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.
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EzLogsAgent
4
+ # FlushScheduler orchestrates periodic flushing of buffered events to the server.
5
+ #
6
+ # Responsibilities:
7
+ # - Start a single background thread that wakes up every `send_interval` seconds
8
+ # - Flush Buffer and send events via RetrySender
9
+ # - Shut down cleanly when requested
10
+ #
11
+ # Non-responsibilities:
12
+ # - Does NOT retry (RetrySender handles that)
13
+ # - Does NOT interpret Transport results
14
+ # - Does NOT manage Rails lifecycle (Railtie handles that)
15
+ # - Does NOT mutate or inspect events
16
+ class FlushScheduler
17
+ class << self
18
+ # Start the background flush thread
19
+ # Idempotent: safe to call multiple times
20
+ def start
21
+ return if running?
22
+
23
+ @stop_requested = false
24
+ @thread = Thread.new { run_loop }
25
+
26
+ Logger.debug("[FlushScheduler] Started")
27
+ rescue => error
28
+ Logger.error("[FlushScheduler] Failed to start: #{error.class} - #{error.message}")
29
+ end
30
+
31
+ # Stop the background flush thread
32
+ # Blocks until thread exits
33
+ # Idempotent: safe to call multiple times
34
+ def stop
35
+ return unless running?
36
+
37
+ @stop_requested = true
38
+ @thread.join if @thread
39
+
40
+ Logger.debug("[FlushScheduler] Stopped")
41
+ rescue => error
42
+ Logger.error("[FlushScheduler] Failed to stop: #{error.class} - #{error.message}")
43
+ end
44
+
45
+ # Check if the scheduler is currently running
46
+ def running?
47
+ @thread&.alive? == true
48
+ end
49
+
50
+ private
51
+
52
+ # Background loop: sleep → flush → send → repeat
53
+ def run_loop
54
+ loop do
55
+ break if @stop_requested
56
+
57
+ sleep_interval
58
+ break if @stop_requested
59
+
60
+ flush_and_send
61
+ end
62
+ rescue => error
63
+ Logger.error("[FlushScheduler] Loop crashed: #{error.class} - #{error.message}")
64
+ end
65
+
66
+ # Sleep for configured interval
67
+ def sleep_interval
68
+ interval = send_interval
69
+ sleep(interval)
70
+ rescue => error
71
+ Logger.error("[FlushScheduler] Sleep failed: #{error.message}")
72
+ sleep(5) # Defensive fallback
73
+ end
74
+
75
+ # Flush buffer and send events
76
+ def flush_and_send
77
+ events = Buffer.flush
78
+ return if events.nil? || events.empty?
79
+
80
+ RetrySender.send(events)
81
+ rescue => error
82
+ Logger.error("[FlushScheduler] Flush and send failed: #{error.class} - #{error.message}")
83
+ end
84
+
85
+ # Get send_interval from configuration with defensive fallback
86
+ def send_interval
87
+ interval = EzLogsAgent.configuration.send_interval
88
+ return 5 if interval.nil?
89
+ return 5 if !interval.is_a?(Numeric)
90
+ return 5 if interval <= 0
91
+
92
+ interval
93
+ rescue => error
94
+ Logger.error("[FlushScheduler] Failed to read send_interval: #{error.message}")
95
+ 5
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EzLogsAgent
4
+ # Minimal, defensive logging utility for the gem.
5
+ # Delegates to Rails.logger when available, falls back to STDERR.
6
+ # Never raises exceptions, never crashes the host application.
7
+ module Logger
8
+ LOG_LEVELS = {
9
+ debug: 0,
10
+ info: 1,
11
+ warn: 2,
12
+ error: 3
13
+ }.freeze
14
+
15
+ class << self
16
+ def debug(message)
17
+ log(:debug, message)
18
+ end
19
+
20
+ def info(message)
21
+ log(:info, message)
22
+ end
23
+
24
+ def warn(message)
25
+ log(:warn, message)
26
+ end
27
+
28
+ def error(message)
29
+ log(:error, message)
30
+ end
31
+
32
+ private
33
+
34
+ def log(level, message)
35
+ # Early return if this log level should not be logged
36
+ return unless should_log?(level)
37
+
38
+ prefixed_message = "[EzLogsAgent] #{message}"
39
+
40
+ if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
41
+ Rails.logger.public_send(level, prefixed_message)
42
+ else
43
+ # Fallback to stderr when Rails.logger is not available
44
+ # Note: should_log? already checked above, so this respects log_level
45
+ $stderr.puts("[#{level.upcase}] #{prefixed_message}")
46
+ end
47
+ rescue => e
48
+ # Defensive: logging must never crash the host application.
49
+ # Silently swallow any logging errors.
50
+ nil
51
+ end
52
+
53
+ def should_log?(level)
54
+ configured_level = EzLogsAgent.configuration.log_level
55
+ LOG_LEVELS[level] >= LOG_LEVELS[configured_level]
56
+ rescue => e
57
+ # If we can't determine log level, allow logging (fail open).
58
+ true
59
+ end
60
+ end
61
+ end
62
+ end