smart_message 0.0.8 → 0.0.9
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 +4 -4
- data/.gitignore +1 -0
- data/.irbrc +24 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile.lock +6 -1
- data/README.md +289 -15
- data/docs/README.md +3 -1
- data/docs/addressing.md +119 -13
- data/docs/architecture.md +68 -0
- data/docs/dead_letter_queue.md +673 -0
- data/docs/dispatcher.md +87 -0
- data/docs/examples.md +59 -1
- data/docs/getting-started.md +8 -1
- data/docs/logging.md +382 -326
- data/docs/message_filtering.md +451 -0
- data/examples/01_point_to_point_orders.rb +54 -53
- data/examples/02_publish_subscribe_events.rb +14 -10
- data/examples/03_many_to_many_chat.rb +16 -8
- data/examples/04_redis_smart_home_iot.rb +20 -10
- data/examples/05_proc_handlers.rb +12 -11
- data/examples/06_custom_logger_example.rb +95 -100
- data/examples/07_error_handling_scenarios.rb +4 -2
- data/examples/08_entity_addressing_basic.rb +18 -6
- data/examples/08_entity_addressing_with_filtering.rb +27 -9
- data/examples/09_dead_letter_queue_demo.rb +559 -0
- data/examples/09_regex_filtering_microservices.rb +407 -0
- data/examples/10_header_block_configuration.rb +263 -0
- data/examples/11_global_configuration_example.rb +219 -0
- data/examples/README.md +102 -0
- data/examples/dead_letters.jsonl +12 -0
- data/examples/performance_metrics/benchmark_results_ractor_20250818_205603.json +135 -0
- data/examples/performance_metrics/benchmark_results_ractor_20250818_205831.json +135 -0
- data/examples/performance_metrics/benchmark_results_test_20250818_204942.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_204942.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_204959.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205044.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205109.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205252.json +130 -0
- data/examples/performance_metrics/benchmark_results_unknown_20250819_172852.json +130 -0
- data/examples/performance_metrics/compare_benchmarks.rb +519 -0
- data/examples/performance_metrics/dead_letters.jsonl +3100 -0
- data/examples/performance_metrics/performance_benchmark.rb +344 -0
- data/examples/show_logger.rb +367 -0
- data/examples/show_me.rb +145 -0
- data/examples/temp.txt +94 -0
- data/examples/tmux_chat/bot_agent.rb +4 -2
- data/examples/tmux_chat/human_agent.rb +4 -2
- data/examples/tmux_chat/room_monitor.rb +4 -2
- data/examples/tmux_chat/shared_chat_system.rb +6 -3
- data/lib/smart_message/addressing.rb +259 -0
- data/lib/smart_message/base.rb +121 -599
- data/lib/smart_message/circuit_breaker.rb +2 -1
- data/lib/smart_message/configuration.rb +199 -0
- data/lib/smart_message/dead_letter_queue.rb +27 -10
- data/lib/smart_message/dispatcher.rb +90 -49
- data/lib/smart_message/header.rb +5 -0
- data/lib/smart_message/logger/base.rb +21 -1
- data/lib/smart_message/logger/default.rb +88 -138
- data/lib/smart_message/logger/lumberjack.rb +324 -0
- data/lib/smart_message/logger/null.rb +81 -0
- data/lib/smart_message/logger.rb +17 -9
- data/lib/smart_message/messaging.rb +100 -0
- data/lib/smart_message/plugins.rb +132 -0
- data/lib/smart_message/serializer/base.rb +25 -8
- data/lib/smart_message/serializer/json.rb +5 -4
- data/lib/smart_message/subscription.rb +193 -0
- data/lib/smart_message/transport/base.rb +72 -41
- data/lib/smart_message/transport/memory_transport.rb +7 -5
- data/lib/smart_message/transport/redis_transport.rb +15 -45
- data/lib/smart_message/transport/stdout_transport.rb +18 -8
- data/lib/smart_message/transport.rb +1 -34
- data/lib/smart_message/utilities.rb +142 -0
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message/versioning.rb +85 -0
- data/lib/smart_message/wrapper.rb.bak +132 -0
- data/lib/smart_message.rb +74 -28
- data/smart_message.gemspec +3 -0
- metadata +76 -3
- data/lib/smart_message/serializer.rb +0 -10
- data/lib/smart_message/wrapper.rb +0 -43
@@ -0,0 +1,344 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# performance_benchmark.rb
|
3
|
+
#
|
4
|
+
# A comprehensive performance measurement tool to compare implementation subjects
|
5
|
+
|
6
|
+
SUBJECT = ARGV.shift || "unknown"
|
7
|
+
|
8
|
+
|
9
|
+
require 'bundler/setup'
|
10
|
+
require 'benchmark'
|
11
|
+
require 'json'
|
12
|
+
require 'fileutils'
|
13
|
+
require_relative '../../lib/smart_message'
|
14
|
+
|
15
|
+
class PerformanceBenchmark
|
16
|
+
SCENARIOS = {
|
17
|
+
cpu_light: { name: "CPU Light", iterations: 1000, sleep: 0 },
|
18
|
+
cpu_heavy: { name: "CPU Heavy", iterations: 500, computation: 10000 },
|
19
|
+
io_light: { name: "I/O Light", iterations: 800, sleep: 0.001 },
|
20
|
+
io_heavy: { name: "I/O Heavy", iterations: 200, sleep: 0.01 },
|
21
|
+
mixed: { name: "Mixed Load", iterations: 600, computation: 5000, sleep: 0.005 }
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@results = {}
|
26
|
+
@start_time = nil
|
27
|
+
@memory_before = nil
|
28
|
+
@dispatcher = SmartMessage::Dispatcher.new
|
29
|
+
setup_test_messages
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_all_scenarios
|
33
|
+
puts "🚀 Starting SmartMessage Performance Benchmark"
|
34
|
+
puts "Ruby Version: #{RUBY_VERSION}"
|
35
|
+
puts "Platform: #{RUBY_PLATFORM}"
|
36
|
+
puts "Processor Count: #{processor_count}"
|
37
|
+
puts "=" * 60
|
38
|
+
|
39
|
+
@start_time = Time.now
|
40
|
+
@memory_before = memory_usage
|
41
|
+
|
42
|
+
SCENARIOS.each do |scenario_key, config|
|
43
|
+
puts "\n📊 Running scenario: #{config[:name]}"
|
44
|
+
run_scenario(scenario_key, config)
|
45
|
+
end
|
46
|
+
|
47
|
+
generate_report
|
48
|
+
save_results
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def setup_test_messages
|
54
|
+
# Define test message classes for different scenarios
|
55
|
+
|
56
|
+
# CPU Light - Simple message processing
|
57
|
+
define_message_class(:CpuLightMessage, :process_cpu_light)
|
58
|
+
|
59
|
+
# CPU Heavy - Computation-intensive processing
|
60
|
+
define_message_class(:CpuHeavyMessage, :process_cpu_heavy)
|
61
|
+
|
62
|
+
# I/O Light - Short sleep simulation
|
63
|
+
define_message_class(:IoLightMessage, :process_io_light)
|
64
|
+
|
65
|
+
# I/O Heavy - Longer I/O simulation
|
66
|
+
define_message_class(:IoHeavyMessage, :process_io_heavy)
|
67
|
+
|
68
|
+
# Mixed - Both CPU and I/O
|
69
|
+
define_message_class(:MixedMessage, :process_mixed)
|
70
|
+
end
|
71
|
+
|
72
|
+
def define_message_class(class_name, processor_method)
|
73
|
+
message_class = Class.new(SmartMessage::Base) do
|
74
|
+
property :scenario_id, required: true
|
75
|
+
property :message_id, required: true
|
76
|
+
property :timestamp, required: true
|
77
|
+
property :computation_cycles
|
78
|
+
property :sleep_duration
|
79
|
+
|
80
|
+
def process
|
81
|
+
# This gets called by the processor methods
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Set the class name first
|
86
|
+
Object.const_set(class_name, message_class)
|
87
|
+
|
88
|
+
# Now set the from address after the class has a name
|
89
|
+
message_class.from('benchmark-service')
|
90
|
+
|
91
|
+
# Configure transport and serializer - need to capture dispatcher reference
|
92
|
+
dispatcher = @dispatcher
|
93
|
+
message_class.config do
|
94
|
+
transport SmartMessage::Transport::MemoryTransport.new(dispatcher: dispatcher, auto_process: true)
|
95
|
+
serializer SmartMessage::Serializer::JSON.new
|
96
|
+
end
|
97
|
+
|
98
|
+
# Register processor with dispatcher
|
99
|
+
@dispatcher.add(class_name.to_s, "PerformanceBenchmark.#{processor_method}")
|
100
|
+
end
|
101
|
+
|
102
|
+
def run_scenario(scenario_key, config)
|
103
|
+
scenario_start = Time.now
|
104
|
+
processed_count = 0
|
105
|
+
errors = 0
|
106
|
+
|
107
|
+
# Setup monitoring
|
108
|
+
completed_before = @dispatcher.completed_task_count rescue 0
|
109
|
+
|
110
|
+
# Generate and publish messages
|
111
|
+
messages = generate_messages(scenario_key, config)
|
112
|
+
|
113
|
+
publish_start = Time.now
|
114
|
+
messages.each do |message|
|
115
|
+
begin
|
116
|
+
message.publish
|
117
|
+
rescue => e
|
118
|
+
errors += 1
|
119
|
+
puts "Error publishing message: #{e.message}" if errors < 5
|
120
|
+
end
|
121
|
+
end
|
122
|
+
publish_time = Time.now - publish_start
|
123
|
+
|
124
|
+
# Wait for processing to complete
|
125
|
+
target_completed = completed_before + config[:iterations] - errors
|
126
|
+
processing_start = Time.now
|
127
|
+
|
128
|
+
wait_for_completion(target_completed, config[:iterations])
|
129
|
+
processing_time = Time.now - processing_start
|
130
|
+
|
131
|
+
completed_after = @dispatcher.completed_task_count rescue target_completed
|
132
|
+
actually_processed = completed_after - completed_before
|
133
|
+
|
134
|
+
scenario_time = Time.now - scenario_start
|
135
|
+
|
136
|
+
# Store results
|
137
|
+
@results[scenario_key] = {
|
138
|
+
name: config[:name],
|
139
|
+
messages_generated: config[:iterations],
|
140
|
+
messages_published: config[:iterations] - errors,
|
141
|
+
messages_processed: actually_processed,
|
142
|
+
errors: errors,
|
143
|
+
times: {
|
144
|
+
total: scenario_time,
|
145
|
+
publish: publish_time,
|
146
|
+
processing: processing_time
|
147
|
+
},
|
148
|
+
throughput: {
|
149
|
+
messages_per_second: actually_processed / scenario_time,
|
150
|
+
publish_rate: (config[:iterations] - errors) / publish_time
|
151
|
+
},
|
152
|
+
dispatcher_stats: @dispatcher.status
|
153
|
+
}
|
154
|
+
|
155
|
+
puts " ✅ Processed #{actually_processed}/#{config[:iterations]} messages in #{scenario_time.round(3)}s"
|
156
|
+
puts " 📈 Throughput: #{(actually_processed / scenario_time).round(1)} msg/sec"
|
157
|
+
puts " 🚀 Publish rate: #{((config[:iterations] - errors) / publish_time).round(1)} msg/sec"
|
158
|
+
puts " ❌ Errors: #{errors}" if errors > 0
|
159
|
+
end
|
160
|
+
|
161
|
+
def generate_messages(scenario_key, config)
|
162
|
+
message_class = Object.const_get(scenario_class_name(scenario_key))
|
163
|
+
|
164
|
+
config[:iterations].times.map do |i|
|
165
|
+
message_class.new(
|
166
|
+
scenario_id: scenario_key.to_s,
|
167
|
+
message_id: "#{scenario_key}_#{i}",
|
168
|
+
timestamp: Time.now.to_f,
|
169
|
+
computation_cycles: config[:computation],
|
170
|
+
sleep_duration: config[:sleep]
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def scenario_class_name(scenario_key)
|
176
|
+
case scenario_key
|
177
|
+
when :cpu_light then :CpuLightMessage
|
178
|
+
when :cpu_heavy then :CpuHeavyMessage
|
179
|
+
when :io_light then :IoLightMessage
|
180
|
+
when :io_heavy then :IoHeavyMessage
|
181
|
+
when :mixed then :MixedMessage
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def wait_for_completion(target_completed, max_iterations)
|
186
|
+
timeout = 60 # seconds
|
187
|
+
start_wait = Time.now
|
188
|
+
|
189
|
+
while (Time.now - start_wait) < timeout
|
190
|
+
current_completed = @dispatcher.completed_task_count rescue 0
|
191
|
+
|
192
|
+
if current_completed >= target_completed
|
193
|
+
break
|
194
|
+
end
|
195
|
+
|
196
|
+
# Check if dispatcher is still running
|
197
|
+
unless @dispatcher.running?
|
198
|
+
puts " ⚠️ Dispatcher stopped running"
|
199
|
+
break
|
200
|
+
end
|
201
|
+
|
202
|
+
sleep 0.1
|
203
|
+
end
|
204
|
+
|
205
|
+
if (Time.now - start_wait) >= timeout
|
206
|
+
puts " ⚠️ Timeout waiting for message processing"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
def generate_report
|
212
|
+
puts "\n" + "=" * 60
|
213
|
+
puts "📋 PERFORMANCE BENCHMARK REPORT"
|
214
|
+
puts "=" * 60
|
215
|
+
|
216
|
+
total_time = Time.now - @start_time
|
217
|
+
memory_after = memory_usage
|
218
|
+
memory_used = memory_after - @memory_before
|
219
|
+
|
220
|
+
puts "\n📊 Overall Stats:"
|
221
|
+
puts " Total runtime: #{total_time.round(3)}s"
|
222
|
+
puts " Memory used: #{memory_used.round(2)} MB"
|
223
|
+
puts " Implementation Subject: #{SUBJECT}"
|
224
|
+
|
225
|
+
puts "\n📈 Scenario Results:"
|
226
|
+
@results.each do |scenario_key, data|
|
227
|
+
puts "\n #{data[:name]}:"
|
228
|
+
puts " Messages: #{data[:messages_processed]}/#{data[:messages_generated]}"
|
229
|
+
puts " Total time: #{data[:times][:total].round(3)}s"
|
230
|
+
puts " Throughput: #{data[:throughput][:messages_per_second].round(1)} msg/sec"
|
231
|
+
puts " Publish rate: #{data[:throughput][:publish_rate].round(1)} msg/sec"
|
232
|
+
puts " Errors: #{data[:errors]}" if data[:errors] > 0
|
233
|
+
end
|
234
|
+
|
235
|
+
puts "\n🏆 Best Performing Scenarios:"
|
236
|
+
sorted_by_throughput = @results.sort_by { |k, v| -v[:throughput][:messages_per_second] }
|
237
|
+
sorted_by_throughput.first(3).each_with_index do |(scenario_key, data), index|
|
238
|
+
puts " #{index + 1}. #{data[:name]}: #{data[:throughput][:messages_per_second].round(1)} msg/sec"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def save_results
|
243
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
244
|
+
filename = "benchmark_results_#{SUBJECT}_#{timestamp}.json"
|
245
|
+
|
246
|
+
report_data = {
|
247
|
+
benchmark_info: {
|
248
|
+
timestamp: Time.now.iso8601,
|
249
|
+
subject: SUBJECT,
|
250
|
+
ruby_version: RUBY_VERSION,
|
251
|
+
platform: RUBY_PLATFORM,
|
252
|
+
processors: processor_count
|
253
|
+
},
|
254
|
+
overall_stats: {
|
255
|
+
total_runtime: Time.now - @start_time,
|
256
|
+
memory_used_mb: memory_usage - @memory_before
|
257
|
+
},
|
258
|
+
scenario_results: @results
|
259
|
+
}
|
260
|
+
|
261
|
+
File.write(filename, JSON.pretty_generate(report_data))
|
262
|
+
puts "\n💾 Results saved to: #{filename}"
|
263
|
+
end
|
264
|
+
|
265
|
+
def memory_usage
|
266
|
+
`ps -o rss= -p #{Process.pid}`.to_i / 1024.0 # Convert KB to MB
|
267
|
+
rescue
|
268
|
+
0
|
269
|
+
end
|
270
|
+
|
271
|
+
def processor_count
|
272
|
+
case RUBY_PLATFORM
|
273
|
+
when /linux/
|
274
|
+
`nproc`.to_i
|
275
|
+
when /darwin/
|
276
|
+
`sysctl -n hw.ncpu`.to_i
|
277
|
+
else
|
278
|
+
`echo $NUMBER_OF_PROCESSORS`.to_i
|
279
|
+
end
|
280
|
+
rescue
|
281
|
+
4 # fallback
|
282
|
+
end
|
283
|
+
|
284
|
+
# Processor methods called by the dispatcher
|
285
|
+
|
286
|
+
def self.process_cpu_light(message_header, encoded_message)
|
287
|
+
# Simple processing - just decode
|
288
|
+
message_data = JSON.parse(encoded_message)
|
289
|
+
message_data['processed_at'] = Time.now.to_f
|
290
|
+
end
|
291
|
+
|
292
|
+
def self.process_cpu_heavy(message_header, encoded_message)
|
293
|
+
message_data = JSON.parse(encoded_message)
|
294
|
+
|
295
|
+
# CPU-intensive computation
|
296
|
+
cycles = message_data['computation_cycles'] || 10000
|
297
|
+
result = 0
|
298
|
+
cycles.times { |i| result += Math.sqrt(i) * Math.sin(i) }
|
299
|
+
|
300
|
+
message_data['processed_at'] = Time.now.to_f
|
301
|
+
message_data['computation_result'] = result
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.process_io_light(message_header, encoded_message)
|
305
|
+
message_data = JSON.parse(encoded_message)
|
306
|
+
|
307
|
+
# Light I/O simulation
|
308
|
+
sleep_time = message_data['sleep_duration'] || 0.001
|
309
|
+
sleep(sleep_time)
|
310
|
+
|
311
|
+
message_data['processed_at'] = Time.now.to_f
|
312
|
+
end
|
313
|
+
|
314
|
+
def self.process_io_heavy(message_header, encoded_message)
|
315
|
+
message_data = JSON.parse(encoded_message)
|
316
|
+
|
317
|
+
# Heavy I/O simulation
|
318
|
+
sleep_time = message_data['sleep_duration'] || 0.01
|
319
|
+
sleep(sleep_time)
|
320
|
+
|
321
|
+
message_data['processed_at'] = Time.now.to_f
|
322
|
+
end
|
323
|
+
|
324
|
+
def self.process_mixed(message_header, encoded_message)
|
325
|
+
message_data = JSON.parse(encoded_message)
|
326
|
+
|
327
|
+
# Combined CPU and I/O
|
328
|
+
cycles = message_data['computation_cycles'] || 5000
|
329
|
+
sleep_time = message_data['sleep_duration'] || 0.005
|
330
|
+
|
331
|
+
result = 0
|
332
|
+
cycles.times { |i| result += Math.sqrt(i) }
|
333
|
+
sleep(sleep_time)
|
334
|
+
|
335
|
+
message_data['processed_at'] = Time.now.to_f
|
336
|
+
message_data['computation_result'] = result
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# Run the benchmark if called directly
|
341
|
+
if __FILE__ == $0
|
342
|
+
benchmark = PerformanceBenchmark.new
|
343
|
+
benchmark.run_all_scenarios
|
344
|
+
end
|
@@ -0,0 +1,367 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/show_logger.rb
|
3
|
+
#
|
4
|
+
# Demonstrates the various features of the SmartMessage default logger
|
5
|
+
# and shows how applications can use the logger directly.
|
6
|
+
|
7
|
+
require_relative '../lib/smart_message'
|
8
|
+
|
9
|
+
puts "=" * 80
|
10
|
+
puts "SmartMessage Logger Feature Demonstration"
|
11
|
+
puts "=" * 80
|
12
|
+
|
13
|
+
# Example 1: Basic Logger Configuration
|
14
|
+
puts "\n1. Basic Logger Configuration"
|
15
|
+
puts "-" * 40
|
16
|
+
|
17
|
+
SmartMessage.configure do |config|
|
18
|
+
config.logger = STDOUT
|
19
|
+
config.log_level = :info
|
20
|
+
config.log_format = :text
|
21
|
+
config.log_colorize = true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get the configured logger for direct use
|
25
|
+
logger = SmartMessage.configuration.default_logger
|
26
|
+
puts "✓ Logger type: #{logger.class}"
|
27
|
+
puts "✓ Log level: #{logger.level}"
|
28
|
+
puts "✓ Format: #{logger.format}"
|
29
|
+
puts "✓ Colorized: #{logger.colorize}"
|
30
|
+
|
31
|
+
# Demonstrate different log levels with colors
|
32
|
+
puts "\nTesting different log levels (should show colors):"
|
33
|
+
logger.debug("This is a debug message - appears in light gray")
|
34
|
+
logger.info("This is an info message - appears in white")
|
35
|
+
logger.warn("This is a warning message - appears in yellow")
|
36
|
+
logger.error("This is an error message - appears in red")
|
37
|
+
logger.fatal("This is a fatal message - appears in bold red")
|
38
|
+
|
39
|
+
# Example 2: JSON Format Logger
|
40
|
+
puts "\n\n2. JSON Format Logger"
|
41
|
+
puts "-" * 40
|
42
|
+
|
43
|
+
SmartMessage.reset_configuration!
|
44
|
+
SmartMessage.configure do |config|
|
45
|
+
config.logger = STDOUT
|
46
|
+
config.log_level = :info
|
47
|
+
config.log_format = :json
|
48
|
+
config.log_include_source = true
|
49
|
+
config.log_structured_data = true
|
50
|
+
config.log_colorize = false # No colors in JSON
|
51
|
+
end
|
52
|
+
|
53
|
+
json_logger = SmartMessage.configuration.default_logger
|
54
|
+
puts "✓ JSON Logger configured"
|
55
|
+
|
56
|
+
json_logger.info("JSON formatted message")
|
57
|
+
json_logger.warn("Warning with structured data",
|
58
|
+
component: "auth",
|
59
|
+
user_id: 12345,
|
60
|
+
action: "login_attempt")
|
61
|
+
json_logger.error("Error with context",
|
62
|
+
error_code: "AUTH_001",
|
63
|
+
timestamp: Time.now.iso8601,
|
64
|
+
severity: "high")
|
65
|
+
|
66
|
+
# Example 3: File Logger with Rolling
|
67
|
+
puts "\n\n3. File Logger with Size-based Rolling"
|
68
|
+
puts "-" * 40
|
69
|
+
|
70
|
+
SmartMessage.reset_configuration!
|
71
|
+
SmartMessage.configure do |config|
|
72
|
+
config.logger = "log/demo_app.log"
|
73
|
+
config.log_level = :debug
|
74
|
+
config.log_format = :text
|
75
|
+
config.log_include_source = true
|
76
|
+
config.log_colorize = false # No colors for file output
|
77
|
+
config.log_options = {
|
78
|
+
roll_by_size: true,
|
79
|
+
max_file_size: 1024, # Small size for demo (1KB)
|
80
|
+
keep_files: 3
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
file_logger = SmartMessage.configuration.default_logger
|
85
|
+
puts "✓ File logger with rolling configured"
|
86
|
+
puts "✓ Log file: #{file_logger.log_file}"
|
87
|
+
puts "✓ Rolling enabled: #{file_logger.respond_to?(:options) ? 'Yes' : 'No'}"
|
88
|
+
|
89
|
+
# Generate some log entries to trigger rolling
|
90
|
+
puts "\nGenerating log entries (check log/ directory)..."
|
91
|
+
50.times do |i|
|
92
|
+
file_logger.info("Log entry #{i + 1} - generating content to trigger file rolling when size limit is reached")
|
93
|
+
file_logger.debug("Debug entry #{i + 1}", entry_number: i + 1, batch: "demo")
|
94
|
+
end
|
95
|
+
|
96
|
+
puts "✓ Generated 100 log entries to demonstrate file rolling"
|
97
|
+
|
98
|
+
# Example 4: Date-based Rolling Logger
|
99
|
+
puts "\n\n4. Date-based Rolling Logger"
|
100
|
+
puts "-" * 40
|
101
|
+
|
102
|
+
SmartMessage.reset_configuration!
|
103
|
+
SmartMessage.configure do |config|
|
104
|
+
config.logger = "log/daily_app.log"
|
105
|
+
config.log_level = :info
|
106
|
+
config.log_format = :text
|
107
|
+
config.log_options = {
|
108
|
+
roll_by_date: true,
|
109
|
+
date_pattern: '%Y-%m-%d'
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
date_logger = SmartMessage.configuration.default_logger
|
114
|
+
puts "✓ Date-based rolling logger configured"
|
115
|
+
|
116
|
+
date_logger.info("Application started", app_version: "1.2.3")
|
117
|
+
date_logger.info("Daily log rotation enabled")
|
118
|
+
|
119
|
+
# Example 5: Application Logger Pattern
|
120
|
+
puts "\n\n5. Application Logger Pattern"
|
121
|
+
puts "-" * 40
|
122
|
+
|
123
|
+
# Configure a production-like logger
|
124
|
+
SmartMessage.reset_configuration!
|
125
|
+
SmartMessage.configure do |config|
|
126
|
+
config.logger = "log/application.log"
|
127
|
+
config.log_level = :info
|
128
|
+
config.log_format = :json
|
129
|
+
config.log_include_source = true
|
130
|
+
config.log_structured_data = true
|
131
|
+
config.log_options = {
|
132
|
+
roll_by_size: true,
|
133
|
+
max_file_size: 10 * 1024 * 1024, # 10 MB
|
134
|
+
keep_files: 5,
|
135
|
+
roll_by_date: false
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
# Create an application class that uses the SmartMessage logger
|
140
|
+
class DemoApplication
|
141
|
+
def initialize
|
142
|
+
@logger = SmartMessage.configuration.default_logger
|
143
|
+
@logger.info("DemoApplication initialized", component: "app", pid: Process.pid)
|
144
|
+
end
|
145
|
+
|
146
|
+
def start
|
147
|
+
@logger.info("Starting application", action: "start")
|
148
|
+
|
149
|
+
# Simulate some application work
|
150
|
+
process_users
|
151
|
+
handle_requests
|
152
|
+
|
153
|
+
@logger.info("Application started successfully", action: "start", status: "success")
|
154
|
+
end
|
155
|
+
|
156
|
+
def stop
|
157
|
+
@logger.info("Stopping application", action: "stop")
|
158
|
+
@logger.info("Application stopped", action: "stop", status: "success")
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def process_users
|
164
|
+
@logger.debug("Processing user data", action: "process_users")
|
165
|
+
|
166
|
+
users = [
|
167
|
+
{ id: 1, name: "Alice", email: "alice@example.com" },
|
168
|
+
{ id: 2, name: "Bob", email: "bob@example.com" },
|
169
|
+
{ id: 3, name: "Charlie", email: "charlie@example.com" }
|
170
|
+
]
|
171
|
+
|
172
|
+
users.each do |user|
|
173
|
+
@logger.info("Processing user",
|
174
|
+
action: "process_user",
|
175
|
+
user_id: user[:id],
|
176
|
+
user_name: user[:name])
|
177
|
+
|
178
|
+
# Simulate some processing time
|
179
|
+
sleep(0.1)
|
180
|
+
end
|
181
|
+
|
182
|
+
@logger.info("User processing completed",
|
183
|
+
action: "process_users",
|
184
|
+
status: "completed",
|
185
|
+
user_count: users.size)
|
186
|
+
end
|
187
|
+
|
188
|
+
def handle_requests
|
189
|
+
@logger.debug("Handling incoming requests", action: "handle_requests")
|
190
|
+
|
191
|
+
requests = [
|
192
|
+
{ method: "GET", path: "/api/users", status: 200 },
|
193
|
+
{ method: "POST", path: "/api/users", status: 201 },
|
194
|
+
{ method: "GET", path: "/api/users/1", status: 200 },
|
195
|
+
{ method: "DELETE", path: "/api/users/2", status: 404 }
|
196
|
+
]
|
197
|
+
|
198
|
+
requests.each_with_index do |request, index|
|
199
|
+
level = request[:status] >= 400 ? :warn : :info
|
200
|
+
|
201
|
+
@logger.send(level, "HTTP request processed",
|
202
|
+
action: "http_request",
|
203
|
+
method: request[:method],
|
204
|
+
path: request[:path],
|
205
|
+
status_code: request[:status],
|
206
|
+
request_id: "req_#{index + 1}")
|
207
|
+
end
|
208
|
+
|
209
|
+
@logger.info("Request handling completed",
|
210
|
+
action: "handle_requests",
|
211
|
+
status: "completed",
|
212
|
+
request_count: requests.size)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
puts "✓ Application logger configured for production use"
|
217
|
+
|
218
|
+
# Run the demo application
|
219
|
+
app = DemoApplication.new
|
220
|
+
app.start
|
221
|
+
sleep(0.5) # Simulate runtime
|
222
|
+
app.stop
|
223
|
+
|
224
|
+
# Example 6: Multiple Logger Configurations
|
225
|
+
puts "\n\n6. Multiple Logger Configurations"
|
226
|
+
puts "-" * 40
|
227
|
+
|
228
|
+
# You can create multiple logger instances with different configurations
|
229
|
+
console_logger = SmartMessage::Logger::Lumberjack.new(
|
230
|
+
log_file: STDERR,
|
231
|
+
level: :warn,
|
232
|
+
format: :text,
|
233
|
+
colorize: true,
|
234
|
+
include_source: false
|
235
|
+
)
|
236
|
+
|
237
|
+
file_logger_json = SmartMessage::Logger::Lumberjack.new(
|
238
|
+
log_file: "log/json_output.log",
|
239
|
+
level: :debug,
|
240
|
+
format: :json,
|
241
|
+
include_source: true,
|
242
|
+
structured_data: true
|
243
|
+
)
|
244
|
+
|
245
|
+
puts "✓ Console logger (STDERR, warnings only, colorized)"
|
246
|
+
puts "✓ File logger (JSON format, debug level)"
|
247
|
+
|
248
|
+
console_logger.warn("This appears on console in yellow")
|
249
|
+
console_logger.error("This appears on console in red")
|
250
|
+
console_logger.info("This won't appear (below warn level)")
|
251
|
+
|
252
|
+
file_logger_json.debug("Debug message to JSON file", module: "demo", test: true)
|
253
|
+
file_logger_json.info("Info message to JSON file", event: "demonstration")
|
254
|
+
|
255
|
+
# Example 7: Integration with SmartMessage Classes
|
256
|
+
puts "\n\n7. Integration with SmartMessage Classes"
|
257
|
+
puts "-" * 40
|
258
|
+
|
259
|
+
# Reset to a simple configuration for message examples
|
260
|
+
SmartMessage.reset_configuration!
|
261
|
+
SmartMessage.configure do |config|
|
262
|
+
config.logger = STDOUT
|
263
|
+
config.log_level = :debug
|
264
|
+
config.log_format = :text
|
265
|
+
config.log_colorize = true
|
266
|
+
end
|
267
|
+
|
268
|
+
# Define a sample message class
|
269
|
+
class DemoMessage < SmartMessage::Base
|
270
|
+
property :title, required: true
|
271
|
+
property :content
|
272
|
+
property :priority, default: 'normal'
|
273
|
+
|
274
|
+
config do
|
275
|
+
transport SmartMessage::Transport::StdoutTransport.new
|
276
|
+
serializer SmartMessage::Serializer::Json.new
|
277
|
+
from 'demo-logger-app'
|
278
|
+
end
|
279
|
+
|
280
|
+
def process
|
281
|
+
# Messages automatically use the configured SmartMessage logger
|
282
|
+
logger.info("Processing demo message",
|
283
|
+
message_id: _sm_header.uuid,
|
284
|
+
title: title,
|
285
|
+
priority: priority)
|
286
|
+
|
287
|
+
case priority
|
288
|
+
when 'high'
|
289
|
+
logger.warn("High priority message requires attention",
|
290
|
+
title: title,
|
291
|
+
priority: priority)
|
292
|
+
when 'critical'
|
293
|
+
logger.error("Critical message needs immediate action",
|
294
|
+
title: title,
|
295
|
+
priority: priority)
|
296
|
+
else
|
297
|
+
logger.info("Normal message processed",
|
298
|
+
title: title,
|
299
|
+
priority: priority)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
puts "✓ SmartMessage classes automatically use the configured logger"
|
305
|
+
|
306
|
+
# Create and publish some demo messages
|
307
|
+
messages = [
|
308
|
+
{ title: "Welcome Message", content: "Hello, World!", priority: "normal" },
|
309
|
+
{ title: "System Alert", content: "High CPU usage detected", priority: "high" },
|
310
|
+
{ title: "Security Breach", content: "Unauthorized access attempt", priority: "critical" }
|
311
|
+
]
|
312
|
+
|
313
|
+
# Get the logger for demonstrating message instance logging
|
314
|
+
app_logger = SmartMessage.configuration.default_logger
|
315
|
+
|
316
|
+
messages.each do |msg_data|
|
317
|
+
begin
|
318
|
+
message = DemoMessage.new(
|
319
|
+
title: msg_data[:title],
|
320
|
+
content: msg_data[:content],
|
321
|
+
priority: msg_data[:priority]
|
322
|
+
)
|
323
|
+
|
324
|
+
puts "<<<>>>>>>>>"
|
325
|
+
# Example of logging an info message that contains the message instance
|
326
|
+
# Shows how to log SmartMessage structure: full message, header, and payload
|
327
|
+
app_logger.info({action: "Publishing SmartMessage instance",
|
328
|
+
message_class: message.class.name,
|
329
|
+
message_uuid: message._sm_header.uuid,
|
330
|
+
message_from: message._sm_header.from,
|
331
|
+
header: message._sm_header,
|
332
|
+
payload: message._sm_payload,
|
333
|
+
full_message: message})
|
334
|
+
|
335
|
+
puts "<<<<<<<<<<"
|
336
|
+
|
337
|
+
message.publish
|
338
|
+
sleep(0.2) # Small delay for demonstration
|
339
|
+
rescue => e
|
340
|
+
puts "Error creating/publishing message: #{e.message}"
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
puts "\n" + "=" * 80
|
345
|
+
puts "Logger Demonstration Complete!"
|
346
|
+
puts "=" * 80
|
347
|
+
|
348
|
+
puts "\nFiles created in log/ directory:"
|
349
|
+
log_files = Dir.glob("log/**/*").select { |f| File.file?(f) }
|
350
|
+
log_files.each do |file|
|
351
|
+
size = File.size(file)
|
352
|
+
puts " #{file} (#{size} bytes)"
|
353
|
+
end
|
354
|
+
|
355
|
+
puts "\nFeatures demonstrated:"
|
356
|
+
puts " ✓ Basic text logging with colors"
|
357
|
+
puts " ✓ JSON structured logging"
|
358
|
+
puts " ✓ File logging with size-based rolling"
|
359
|
+
puts " ✓ Date-based log rolling"
|
360
|
+
puts " ✓ Application integration patterns"
|
361
|
+
puts " ✓ Multiple logger configurations"
|
362
|
+
puts " ✓ SmartMessage class integration"
|
363
|
+
puts " ✓ Different log levels and formatting"
|
364
|
+
puts " ✓ Structured data logging"
|
365
|
+
puts " ✓ Source location tracking"
|
366
|
+
|
367
|
+
puts "\nTip: Check the generated log files to see the different output formats!"
|