smart_message 0.0.16 โ†’ 0.0.17

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,580 @@
1
+ # STDOUT Transport
2
+
3
+ The **STDOUT Transport** is a development and debugging transport that outputs messages to the console or files in human-readable formats. It inherits from `FileTransport` and provides specialized output formatting capabilities.
4
+
5
+ ## Overview
6
+
7
+ STDOUT Transport is perfect for:
8
+ - **Development debugging** - See messages in real-time during development
9
+ - **Application logging** - Structured message logging to files or console
10
+ - **Message tracing** - Track message flow through systems
11
+ - **Integration testing** - Verify message content and flow
12
+ - **Format testing** - Demonstrate different message serialization formats
13
+
14
+ ## Key Features
15
+
16
+ - ๐Ÿ“„ **Multiple Output Formats** - Pretty, JSON Lines, and compact JSON
17
+ - ๐Ÿ–ฅ๏ธ **Console or File Output** - Direct to STDOUT or file paths
18
+ - ๐Ÿ”„ **Optional Loopback** - Process messages locally after output
19
+ - ๐Ÿงต **Thread-Safe** - Safe for concurrent message publishing
20
+ - ๐Ÿ› ๏ธ **No Dependencies** - Built-in Ruby formatting capabilities
21
+ - ๐ŸŽจ **Pretty Printing** - Human-readable format using amazing_print
22
+
23
+ ## Architecture
24
+
25
+ ```
26
+ Publisher โ†’ StdoutTransport โ†’ Console/File Output โ†’ Optional Loopback โ†’ Local Processing
27
+ (format selection) (thread-safe) (if enabled) (message handling)
28
+ ```
29
+
30
+ STDOUT Transport inherits from `FileTransport` and adds specialized console output capabilities with multiple formatting options.
31
+
32
+ ## Configuration
33
+
34
+ ### Basic Setup
35
+
36
+ ```ruby
37
+ # Minimal configuration (outputs to console)
38
+ transport = SmartMessage::Transport::StdoutTransport.new
39
+
40
+ # With file output
41
+ transport = SmartMessage::Transport::StdoutTransport.new(
42
+ file_path: '/var/log/messages.log'
43
+ )
44
+
45
+ # With format specification
46
+ transport = SmartMessage::Transport::StdoutTransport.new(
47
+ format: :pretty,
48
+ loopback: true
49
+ )
50
+
51
+ # Full configuration
52
+ transport = SmartMessage::Transport::StdoutTransport.new(
53
+ file_path: '/var/log/app.log',
54
+ format: :jsonl,
55
+ loopback: false,
56
+ auto_create_dirs: true
57
+ )
58
+ ```
59
+
60
+ ### Using with SmartMessage
61
+
62
+ ```ruby
63
+ # Configure as default transport
64
+ SmartMessage.configure do |config|
65
+ config.default_transport = SmartMessage::Transport::StdoutTransport.new(
66
+ format: :pretty
67
+ )
68
+ end
69
+
70
+ # Use in message class
71
+ class LogMessage < SmartMessage::Base
72
+ property :level, required: true
73
+ property :message, required: true
74
+ property :timestamp, default: -> { Time.now.iso8601 }
75
+
76
+ transport :stdout
77
+
78
+ def process
79
+ puts "Log entry processed: #{level} - #{message}"
80
+ end
81
+ end
82
+ ```
83
+
84
+ ## Configuration Options
85
+
86
+ | Option | Type | Default | Description |
87
+ |--------|------|---------|-------------|
88
+ | `format` | Symbol | `:pretty` | Output format (`:pretty`, `:jsonl`, `:json`) |
89
+ | `file_path` | String | `nil` | File path for output (nil = STDOUT) |
90
+ | `loopback` | Boolean | `false` | Process messages locally after output |
91
+ | `auto_create_dirs` | Boolean | `true` | Automatically create parent directories |
92
+
93
+ ## Output Formats
94
+
95
+ ### Pretty Format (`:pretty`)
96
+
97
+ Uses `amazing_print` for human-readable, colorized output:
98
+
99
+ ```ruby
100
+ transport = SmartMessage::Transport::StdoutTransport.new(format: :pretty)
101
+
102
+ class UserMessage < SmartMessage::Base
103
+ property :name
104
+ property :email
105
+ transport transport
106
+ end
107
+
108
+ UserMessage.new(name: "Alice Johnson", email: "alice@example.com").publish
109
+
110
+ # Output:
111
+ # {
112
+ # :name => "Alice Johnson",
113
+ # :email => "alice@example.com"
114
+ # }
115
+ ```
116
+
117
+ ### JSON Lines Format (`:jsonl`)
118
+
119
+ One JSON object per line, ideal for log processing:
120
+
121
+ ```ruby
122
+ transport = SmartMessage::Transport::StdoutTransport.new(format: :jsonl)
123
+
124
+ UserMessage.new(name: "Bob Smith", email: "bob@example.com").publish
125
+ UserMessage.new(name: "Carol Williams", email: "carol@example.com").publish
126
+
127
+ # Output:
128
+ # {"name":"Bob Smith","email":"bob@example.com"}
129
+ # {"name":"Carol Williams","email":"carol@example.com"}
130
+ ```
131
+
132
+ ### Compact JSON Format (`:json`)
133
+
134
+ Compact JSON with no newlines:
135
+
136
+ ```ruby
137
+ transport = SmartMessage::Transport::StdoutTransport.new(format: :json)
138
+
139
+ UserMessage.new(name: "David Brown", email: "david@example.com").publish
140
+
141
+ # Output:
142
+ # {"name":"David Brown","email":"david@example.com"}
143
+ ```
144
+
145
+ ## Usage Examples
146
+
147
+ ### Development Debugging
148
+
149
+ ```ruby
150
+ class DebugMessage < SmartMessage::Base
151
+ property :component, required: true
152
+ property :action, required: true
153
+ property :data
154
+ property :timestamp, default: -> { Time.now }
155
+
156
+ transport SmartMessage::Transport::StdoutTransport.new(
157
+ format: :pretty,
158
+ loopback: true
159
+ )
160
+
161
+ def process
162
+ puts "[DEBUG] #{component}.#{action} completed at #{timestamp}"
163
+ end
164
+ end
165
+
166
+ # Publishing shows both formatted output and processes locally
167
+ DebugMessage.new(
168
+ component: "UserService",
169
+ action: "create_user",
170
+ data: { user_id: 123, email: "user@example.com" }
171
+ ).publish
172
+
173
+ # Output (formatted):
174
+ # {
175
+ # :component => "UserService",
176
+ # :action => "create_user",
177
+ # :data => {
178
+ # :user_id => 123,
179
+ # :email => "user@example.com"
180
+ # },
181
+ # :timestamp => 2024-01-15 10:30:45 -0800
182
+ # }
183
+ # [DEBUG] UserService.create_user completed at 2024-01-15 10:30:45 -0800
184
+ ```
185
+
186
+ ### Application Logging
187
+
188
+ ```ruby
189
+ class ApplicationLog < SmartMessage::Base
190
+ property :level, required: true, validation: %w[DEBUG INFO WARN ERROR FATAL]
191
+ property :message, required: true
192
+ property :module_name
193
+ property :timestamp, default: -> { Time.now.iso8601 }
194
+
195
+ transport SmartMessage::Transport::StdoutTransport.new(
196
+ format: :jsonl,
197
+ file_path: '/var/log/application.log'
198
+ )
199
+ end
200
+
201
+ # Log entries
202
+ ApplicationLog.new(
203
+ level: "INFO",
204
+ message: "Application started successfully",
205
+ module_name: "Main"
206
+ ).publish
207
+
208
+ ApplicationLog.new(
209
+ level: "ERROR",
210
+ message: "Database connection failed",
211
+ module_name: "DatabaseConnector"
212
+ ).publish
213
+
214
+ # File contents (/var/log/application.log):
215
+ # {"level":"INFO","message":"Application started successfully","module_name":"Main","timestamp":"2024-01-15T18:30:45Z"}
216
+ # {"level":"ERROR","message":"Database connection failed","module_name":"DatabaseConnector","timestamp":"2024-01-15T18:30:46Z"}
217
+ ```
218
+
219
+ ### Format Comparison Demo
220
+
221
+ ```ruby
222
+ class DemoMessage < SmartMessage::Base
223
+ property :first_name, description: "Person's first name"
224
+ property :last_name, description: "Person's last name"
225
+ end
226
+
227
+ # Pretty format example
228
+ puts "=== Pretty Format ==="
229
+ DemoMessage.new(first_name: "Alice", last_name: "Johnson").tap do |msg|
230
+ msg.transport(SmartMessage::Transport::StdoutTransport.new(format: :pretty))
231
+ end.publish
232
+
233
+ # JSON Lines format example
234
+ puts "\n=== JSON Lines Format ==="
235
+ transport_jsonl = SmartMessage::Transport::StdoutTransport.new(format: :jsonl)
236
+ DemoMessage.new(first_name: "Bob", last_name: "Smith").tap do |msg|
237
+ msg.transport(transport_jsonl)
238
+ end.publish
239
+ DemoMessage.new(first_name: "Carol", last_name: "Williams").tap do |msg|
240
+ msg.transport(transport_jsonl)
241
+ end.publish
242
+
243
+ # JSON format example
244
+ puts "\n=== JSON Format ==="
245
+ transport_json = SmartMessage::Transport::StdoutTransport.new(format: :json)
246
+ DemoMessage.new(first_name: "David", last_name: "Brown").tap do |msg|
247
+ msg.transport(transport_json)
248
+ end.publish
249
+ DemoMessage.new(first_name: "Emma", last_name: "Davis").tap do |msg|
250
+ msg.transport(transport_json)
251
+ end.publish
252
+ DemoMessage.new(first_name: "Frank", last_name: "Miller").tap do |msg|
253
+ msg.transport(transport_json)
254
+ end.publish
255
+ ```
256
+
257
+ ### Integration Testing
258
+
259
+ ```ruby
260
+ class TestMessage < SmartMessage::Base
261
+ property :test_id, required: true
262
+ property :expected_result
263
+ property :actual_result
264
+ property :status, default: 'pending'
265
+
266
+ transport SmartMessage::Transport::StdoutTransport.new(
267
+ format: :jsonl,
268
+ file_path: '/tmp/test_results.log',
269
+ loopback: true
270
+ )
271
+
272
+ def process
273
+ self.status = (expected_result == actual_result) ? 'passed' : 'failed'
274
+ puts "Test #{test_id}: #{status}"
275
+ end
276
+ end
277
+
278
+ # Test execution
279
+ TestMessage.new(
280
+ test_id: "AUTH_001",
281
+ expected_result: "authenticated",
282
+ actual_result: "authenticated"
283
+ ).publish
284
+
285
+ # Outputs to both file and console via loopback processing
286
+ ```
287
+
288
+ ## File Output Management
289
+
290
+ ### Automatic Directory Creation
291
+
292
+ ```ruby
293
+ # Creates parent directories automatically
294
+ transport = SmartMessage::Transport::StdoutTransport.new(
295
+ file_path: '/var/log/app/events/user_actions.log',
296
+ auto_create_dirs: true # Creates /var/log/app/events/ if needed
297
+ )
298
+ ```
299
+
300
+ ### File Rotation and Management
301
+
302
+ ```ruby
303
+ # Daily log rotation example
304
+ class DailyLogger < SmartMessage::Base
305
+ property :event, required: true
306
+ property :data
307
+
308
+ def self.current_log_path
309
+ date_str = Time.now.strftime("%Y-%m-%d")
310
+ "/var/log/app/daily/#{date_str}.log"
311
+ end
312
+
313
+ transport SmartMessage::Transport::StdoutTransport.new(
314
+ format: :jsonl,
315
+ file_path: current_log_path
316
+ )
317
+ end
318
+
319
+ # Each day's events go to separate files
320
+ DailyLogger.new(event: "user_login", data: { user_id: 123 }).publish
321
+ ```
322
+
323
+ ## API Reference
324
+
325
+ ### Instance Methods
326
+
327
+ #### `#format`
328
+ Returns the current output format.
329
+
330
+ ```ruby
331
+ transport = SmartMessage::Transport::StdoutTransport.new(format: :jsonl)
332
+ puts transport.format # => :jsonl
333
+ ```
334
+
335
+ #### `#file_path`
336
+ Returns the configured file path (nil for STDOUT).
337
+
338
+ ```ruby
339
+ transport = SmartMessage::Transport::StdoutTransport.new(file_path: '/tmp/output.log')
340
+ puts transport.file_path # => '/tmp/output.log'
341
+ ```
342
+
343
+ #### `#loopback?`
344
+ Checks if loopback processing is enabled.
345
+
346
+ ```ruby
347
+ transport = SmartMessage::Transport::StdoutTransport.new(loopback: true)
348
+ puts transport.loopback? # => true
349
+ ```
350
+
351
+ #### `#publish(message)`
352
+ Publishes a message object with the configured format.
353
+
354
+ ```ruby
355
+ transport.publish(message_instance)
356
+ ```
357
+
358
+ ## Performance Characteristics
359
+
360
+ - **Latency**: ~1ms (I/O dependent)
361
+ - **Throughput**: Limited by I/O operations (console/file writes)
362
+ - **Memory Usage**: Minimal (immediate output)
363
+ - **Threading**: Thread-safe file operations
364
+ - **Format Overhead**: Pretty > JSON Lines > JSON
365
+
366
+ ## Use Cases
367
+
368
+ ### Development Environment
369
+
370
+ ```ruby
371
+ # config/environments/development.rb
372
+ SmartMessage.configure do |config|
373
+ config.default_transport = SmartMessage::Transport::StdoutTransport.new(
374
+ format: :pretty,
375
+ loopback: true
376
+ )
377
+ config.logger.level = Logger::DEBUG
378
+ end
379
+ ```
380
+
381
+ ### Testing Environment
382
+
383
+ ```ruby
384
+ # config/environments/test.rb
385
+ SmartMessage.configure do |config|
386
+ config.default_transport = SmartMessage::Transport::StdoutTransport.new(
387
+ format: :jsonl,
388
+ file_path: Rails.root.join('tmp', 'test_messages.log')
389
+ )
390
+ end
391
+ ```
392
+
393
+ ### CI/CD Pipeline Integration
394
+
395
+ ```ruby
396
+ class CIMessage < SmartMessage::Base
397
+ property :stage, required: true
398
+ property :status, required: true
399
+ property :duration
400
+ property :details
401
+
402
+ transport SmartMessage::Transport::StdoutTransport.new(
403
+ format: :jsonl,
404
+ file_path: ENV['CI_LOG_PATH'] || '/tmp/ci_pipeline.log'
405
+ )
406
+ end
407
+
408
+ # CI stages can publish structured logs
409
+ CIMessage.new(
410
+ stage: "build",
411
+ status: "success",
412
+ duration: 45.2,
413
+ details: { artifacts: 3, warnings: 0 }
414
+ ).publish
415
+ ```
416
+
417
+ ### Microservices Debugging
418
+
419
+ ```ruby
420
+ class ServiceMessage < SmartMessage::Base
421
+ property :service_name, required: true
422
+ property :operation, required: true
423
+ property :request_id
424
+ property :response_time
425
+ property :status_code
426
+
427
+ transport SmartMessage::Transport::StdoutTransport.new(
428
+ format: :pretty,
429
+ loopback: false
430
+ )
431
+ end
432
+
433
+ # Service calls generate readable debug output
434
+ ServiceMessage.new(
435
+ service_name: "UserService",
436
+ operation: "authenticate",
437
+ request_id: "req_123",
438
+ response_time: 23.4,
439
+ status_code: 200
440
+ ).publish
441
+ ```
442
+
443
+ ## Best Practices
444
+
445
+ ### Development
446
+ - Use `:pretty` format for interactive debugging
447
+ - Enable `loopback` to process messages locally
448
+ - Use descriptive property names for clarity
449
+
450
+ ### Production Logging
451
+ - Use `:jsonl` format for structured logs
452
+ - Specify file paths with rotation patterns
453
+ - Disable `loopback` unless local processing needed
454
+
455
+ ### Testing
456
+ - Use file output to capture test message flows
457
+ - Use `:json` format for minimal output
458
+ - Clear log files between test runs
459
+
460
+ ### Performance
461
+ - `:json` format has lowest overhead
462
+ - Console output is slower than file output
463
+ - Consider asynchronous logging for high-volume scenarios
464
+
465
+ ## Thread Safety
466
+
467
+ STDOUT Transport is fully thread-safe:
468
+ - File operations use proper locking
469
+ - Format operations are stateless
470
+ - Multiple threads can publish concurrently
471
+
472
+ ```ruby
473
+ # Thread-safe concurrent publishing
474
+ threads = []
475
+ 10.times do |i|
476
+ threads << Thread.new do
477
+ 100.times do |j|
478
+ LogMessage.new(
479
+ level: "INFO",
480
+ message: "Thread #{i}, Message #{j}"
481
+ ).publish
482
+ end
483
+ end
484
+ end
485
+ threads.each(&:join)
486
+ ```
487
+
488
+ ## Error Handling
489
+
490
+ ```ruby
491
+ class ErrorMessage < SmartMessage::Base
492
+ property :error_type, required: true
493
+ property :error_message, required: true
494
+ property :stack_trace
495
+
496
+ transport SmartMessage::Transport::StdoutTransport.new(
497
+ format: :jsonl,
498
+ file_path: '/var/log/errors.log'
499
+ )
500
+
501
+ def process
502
+ # Handle error processing
503
+ case error_type
504
+ when 'critical'
505
+ send_alert(error_message)
506
+ when 'warning'
507
+ log_warning(error_message)
508
+ end
509
+ end
510
+ end
511
+
512
+ begin
513
+ risky_operation()
514
+ rescue => e
515
+ ErrorMessage.new(
516
+ error_type: 'critical',
517
+ error_message: e.message,
518
+ stack_trace: e.backtrace.join("\n")
519
+ ).publish
520
+ end
521
+ ```
522
+
523
+ ## Migration Patterns
524
+
525
+ ### From Console to File Logging
526
+
527
+ ```ruby
528
+ # Development: Console output
529
+ transport = SmartMessage::Transport::StdoutTransport.new(format: :pretty)
530
+
531
+ # Production: File logging
532
+ transport = SmartMessage::Transport::StdoutTransport.new(
533
+ format: :jsonl,
534
+ file_path: '/var/log/production.log'
535
+ )
536
+ ```
537
+
538
+ ### Format Evolution
539
+
540
+ ```ruby
541
+ # Start with pretty format for development
542
+ # Move to JSONL for production structured logging
543
+ # Upgrade to specialized transports (Redis) for distribution
544
+
545
+ case Rails.env
546
+ when 'development'
547
+ SmartMessage::Transport::StdoutTransport.new(format: :pretty, loopback: true)
548
+ when 'test'
549
+ SmartMessage::Transport::StdoutTransport.new(format: :jsonl, file_path: 'tmp/test.log')
550
+ when 'production'
551
+ SmartMessage::Transport::RedisTransport.new(url: ENV['REDIS_URL'])
552
+ end
553
+ ```
554
+
555
+ ## Examples
556
+
557
+ The STDOUT Transport is demonstrated in:
558
+ - **[examples/memory/06_stdout_publish_only.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/06_stdout_publish_only.rb)** - Comprehensive format demonstration with all three output formats
559
+
560
+ ### Running Examples
561
+
562
+ ```bash
563
+ # Navigate to SmartMessage directory
564
+ cd smart_message
565
+
566
+ # Run the STDOUT Transport format demo
567
+ ruby examples/memory/06_stdout_publish_only.rb
568
+
569
+ # Shows all three formats:
570
+ # - Pretty format output
571
+ # - JSON Lines format output
572
+ # - Compact JSON format output
573
+ ```
574
+
575
+ ## Related Documentation
576
+
577
+ - [File Transport](file-transport.md) - Base transport implementation
578
+ - [Transport Overview](../reference/transports.md) - All available transports
579
+ - [Memory Transport](memory-transport.md) - In-memory development transport
580
+ - [Troubleshooting Guide](../development/troubleshooting.md) - Testing and debugging strategies