smart_message 0.0.2 → 0.0.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 +4 -4
- data/CHANGELOG.md +31 -1
- data/Gemfile.lock +6 -1
- data/README.md +92 -14
- data/docs/README.md +1 -0
- data/docs/architecture.md +41 -8
- data/docs/dispatcher.md +52 -16
- data/docs/getting-started.md +64 -2
- data/docs/logging.md +452 -0
- data/docs/message_processing.md +423 -0
- data/docs/proc_handlers_summary.md +247 -0
- data/docs/transports.md +202 -8
- data/examples/.gitignore +2 -0
- data/examples/04_redis_smart_home_iot.rb +649 -0
- data/examples/05_proc_handlers.rb +181 -0
- data/examples/06_custom_logger_example.rb +620 -0
- data/examples/README.md +118 -3
- data/examples/smart_home_iot_dataflow.md +257 -0
- data/lib/smart_message/base.rb +94 -4
- data/lib/smart_message/dispatcher.rb +22 -6
- data/lib/smart_message/logger/default.rb +217 -0
- data/lib/smart_message/logger.rb +9 -1
- data/lib/smart_message/transport/redis_transport.rb +190 -0
- data/lib/smart_message/transport/registry.rb +1 -0
- data/lib/smart_message/transport.rb +1 -0
- data/lib/smart_message/version.rb +1 -1
- data/smart_message.gemspec +1 -0
- metadata +25 -1
data/docs/logging.md
ADDED
@@ -0,0 +1,452 @@
|
|
1
|
+
# Logging in SmartMessage
|
2
|
+
|
3
|
+
SmartMessage provides flexible logging capabilities through its plugin architecture. This document covers the built-in default logger as well as how to create custom loggers.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
- [Default Logger](#default-logger)
|
8
|
+
- [Configuration Options](#configuration-options)
|
9
|
+
- [Output Destinations](#output-destinations)
|
10
|
+
- [Log Levels](#log-levels)
|
11
|
+
- [Message Lifecycle Logging](#message-lifecycle-logging)
|
12
|
+
- [Rails Integration](#rails-integration)
|
13
|
+
- [Custom Loggers](#custom-loggers)
|
14
|
+
- [Examples](#examples)
|
15
|
+
|
16
|
+
## Default Logger
|
17
|
+
|
18
|
+
SmartMessage includes a built-in `SmartMessage::Logger::Default` class that automatically detects your environment and chooses the best logging approach:
|
19
|
+
|
20
|
+
- **Rails Applications**: Uses `Rails.logger` with tagged logging
|
21
|
+
- **Standalone Ruby**: Uses Ruby's standard `Logger` class with file output
|
22
|
+
|
23
|
+
### Quick Start
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class MyMessage < SmartMessage::Base
|
27
|
+
property :content
|
28
|
+
|
29
|
+
config do
|
30
|
+
transport SmartMessage::Transport::StdoutTransport.new
|
31
|
+
serializer SmartMessage::Serializer::JSON.new
|
32
|
+
logger SmartMessage::Logger::Default.new # Zero configuration!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
## Configuration Options
|
38
|
+
|
39
|
+
The default logger accepts several configuration options:
|
40
|
+
|
41
|
+
### Basic Configuration
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
# Use defaults (Rails.logger or log/smart_message.log)
|
45
|
+
logger SmartMessage::Logger::Default.new
|
46
|
+
|
47
|
+
# Custom log file path
|
48
|
+
logger SmartMessage::Logger::Default.new(
|
49
|
+
log_file: '/var/log/my_app/messages.log'
|
50
|
+
)
|
51
|
+
|
52
|
+
# Custom log level
|
53
|
+
logger SmartMessage::Logger::Default.new(
|
54
|
+
level: Logger::DEBUG
|
55
|
+
)
|
56
|
+
|
57
|
+
# Both custom file and level
|
58
|
+
logger SmartMessage::Logger::Default.new(
|
59
|
+
log_file: 'logs/custom.log',
|
60
|
+
level: Logger::WARN
|
61
|
+
)
|
62
|
+
```
|
63
|
+
|
64
|
+
## Output Destinations
|
65
|
+
|
66
|
+
The default logger supports multiple output destinations:
|
67
|
+
|
68
|
+
### File Logging (Default)
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# Default file location (Rails convention)
|
72
|
+
logger SmartMessage::Logger::Default.new
|
73
|
+
# → log/smart_message.log
|
74
|
+
|
75
|
+
# Custom file path
|
76
|
+
logger SmartMessage::Logger::Default.new(
|
77
|
+
log_file: '/var/log/application/messages.log'
|
78
|
+
)
|
79
|
+
```
|
80
|
+
|
81
|
+
**Features:**
|
82
|
+
- Automatic log rotation (10 files, 10MB each)
|
83
|
+
- Directory creation if needed
|
84
|
+
- Timestamped entries with clean formatting
|
85
|
+
|
86
|
+
### STDOUT Logging
|
87
|
+
|
88
|
+
Perfect for containerized applications (Docker, Kubernetes):
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
logger SmartMessage::Logger::Default.new(
|
92
|
+
log_file: STDOUT,
|
93
|
+
level: Logger::INFO
|
94
|
+
)
|
95
|
+
```
|
96
|
+
|
97
|
+
### STDERR Logging
|
98
|
+
|
99
|
+
For error-focused logging:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
logger SmartMessage::Logger::Default.new(
|
103
|
+
log_file: STDERR,
|
104
|
+
level: Logger::WARN
|
105
|
+
)
|
106
|
+
```
|
107
|
+
|
108
|
+
### In-Memory Logging (Testing)
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
require 'stringio'
|
112
|
+
|
113
|
+
logger SmartMessage::Logger::Default.new(
|
114
|
+
log_file: StringIO.new,
|
115
|
+
level: Logger::DEBUG
|
116
|
+
)
|
117
|
+
```
|
118
|
+
|
119
|
+
## Log Levels
|
120
|
+
|
121
|
+
The default logger supports all standard Ruby log levels:
|
122
|
+
|
123
|
+
| Level | Numeric Value | Description |
|
124
|
+
|-------|--------------|-------------|
|
125
|
+
| `Logger::DEBUG` | 0 | Detailed debugging information |
|
126
|
+
| `Logger::INFO` | 1 | General information messages |
|
127
|
+
| `Logger::WARN` | 2 | Warning messages |
|
128
|
+
| `Logger::ERROR` | 3 | Error messages |
|
129
|
+
| `Logger::FATAL` | 4 | Fatal error messages |
|
130
|
+
|
131
|
+
### Environment-Based Defaults
|
132
|
+
|
133
|
+
The default logger automatically sets appropriate log levels based on your environment:
|
134
|
+
|
135
|
+
- **Rails Production**: `Logger::INFO`
|
136
|
+
- **Rails Test**: `Logger::ERROR`
|
137
|
+
- **Rails Development**: `Logger::DEBUG`
|
138
|
+
- **Non-Rails**: `Logger::INFO`
|
139
|
+
|
140
|
+
## Message Lifecycle Logging
|
141
|
+
|
142
|
+
The default logger automatically logs key events in the message lifecycle:
|
143
|
+
|
144
|
+
### Message Creation
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
# Logged at DEBUG level
|
148
|
+
message = MyMessage.new(content: "Hello")
|
149
|
+
# → [DEBUG] [SmartMessage] Created: MyMessage - {content: "Hello"}
|
150
|
+
```
|
151
|
+
|
152
|
+
### Message Publishing
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# Logged at INFO level
|
156
|
+
message.publish
|
157
|
+
# → [INFO] [SmartMessage] Published: MyMessage via StdoutTransport
|
158
|
+
```
|
159
|
+
|
160
|
+
### Message Reception
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
# Logged at INFO level when message is received
|
164
|
+
# → [INFO] [SmartMessage] Received: MyMessage (45 bytes)
|
165
|
+
```
|
166
|
+
|
167
|
+
### Message Processing
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
# Logged at INFO level after processing
|
171
|
+
# → [INFO] [SmartMessage] Processed: MyMessage - Success
|
172
|
+
```
|
173
|
+
|
174
|
+
### Subscription Management
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
# Logged at INFO level
|
178
|
+
MyMessage.subscribe
|
179
|
+
# → [INFO] [SmartMessage] Subscribed: MyMessage
|
180
|
+
|
181
|
+
MyMessage.unsubscribe
|
182
|
+
# → [INFO] [SmartMessage] Unsubscribed: MyMessage
|
183
|
+
```
|
184
|
+
|
185
|
+
### Error Logging
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
# Logged at ERROR level with full stack trace (DEBUG level)
|
189
|
+
# → [ERROR] [SmartMessage] Error in message processing: RuntimeError - Something went wrong
|
190
|
+
# → [DEBUG] [SmartMessage] Backtrace: ...
|
191
|
+
```
|
192
|
+
|
193
|
+
## Rails Integration
|
194
|
+
|
195
|
+
When running in a Rails application, the default logger provides enhanced integration:
|
196
|
+
|
197
|
+
### Automatic Detection
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
# Automatically uses Rails.logger when available
|
201
|
+
logger SmartMessage::Logger::Default.new
|
202
|
+
```
|
203
|
+
|
204
|
+
### Tagged Logging
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
# In Rails, all SmartMessage logs are tagged
|
208
|
+
Rails.logger.tagged('SmartMessage') do
|
209
|
+
# All SmartMessage logging happens here
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
### Rails Log File Location
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
# Uses Rails.root/log/smart_message.log when Rails is detected
|
217
|
+
logger SmartMessage::Logger::Default.new
|
218
|
+
# → Rails.root.join('log', 'smart_message.log')
|
219
|
+
```
|
220
|
+
|
221
|
+
### Rails Environment Handling
|
222
|
+
|
223
|
+
The logger respects Rails environment settings:
|
224
|
+
|
225
|
+
- **Production**: INFO level, structured logging
|
226
|
+
- **Development**: DEBUG level, verbose output
|
227
|
+
- **Test**: ERROR level, minimal output
|
228
|
+
|
229
|
+
## Custom Loggers
|
230
|
+
|
231
|
+
You can create custom loggers by inheriting from `SmartMessage::Logger::Base`:
|
232
|
+
|
233
|
+
### Basic Custom Logger
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
class SmartMessage::Logger::MyCustomLogger < SmartMessage::Logger::Base
|
237
|
+
def initialize(external_logger)
|
238
|
+
@logger = external_logger
|
239
|
+
end
|
240
|
+
|
241
|
+
def log_message_created(message)
|
242
|
+
@logger.debug "Created message: #{message.class.name}"
|
243
|
+
end
|
244
|
+
|
245
|
+
def log_message_published(message, transport)
|
246
|
+
@logger.info "Published #{message.class.name} via #{transport.class.name}"
|
247
|
+
end
|
248
|
+
|
249
|
+
# Implement other lifecycle methods as needed...
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
### Wrapper for Third-Party Loggers
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
# Semantic Logger example
|
257
|
+
class SmartMessage::Logger::SemanticLogger < SmartMessage::Logger::Base
|
258
|
+
def initialize(semantic_logger = nil)
|
259
|
+
@logger = semantic_logger || SemanticLogger['SmartMessage']
|
260
|
+
end
|
261
|
+
|
262
|
+
def log_message_created(message)
|
263
|
+
@logger.debug "Message created", message_class: message.class.name
|
264
|
+
end
|
265
|
+
|
266
|
+
def log_error(context, error)
|
267
|
+
@logger.error "Error in #{context}", exception: error
|
268
|
+
end
|
269
|
+
end
|
270
|
+
```
|
271
|
+
|
272
|
+
### Multi-Logger (Broadcast)
|
273
|
+
|
274
|
+
```ruby
|
275
|
+
class SmartMessage::Logger::MultiLogger < SmartMessage::Logger::Base
|
276
|
+
def initialize(*loggers)
|
277
|
+
@loggers = loggers
|
278
|
+
end
|
279
|
+
|
280
|
+
def log_message_created(message)
|
281
|
+
@loggers.each { |logger| logger.log_message_created(message) }
|
282
|
+
end
|
283
|
+
|
284
|
+
# Other methods follow same pattern...
|
285
|
+
end
|
286
|
+
|
287
|
+
# Usage
|
288
|
+
logger SmartMessage::Logger::MultiLogger.new(
|
289
|
+
SmartMessage::Logger::Default.new(log_file: 'app.log'),
|
290
|
+
SmartMessage::Logger::Default.new(log_file: STDOUT),
|
291
|
+
SmartMessage::Logger::MyCustomLogger.new(external_system)
|
292
|
+
)
|
293
|
+
```
|
294
|
+
|
295
|
+
## Examples
|
296
|
+
|
297
|
+
### Development Setup
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
class OrderMessage < SmartMessage::Base
|
301
|
+
property :order_id, String, required: true
|
302
|
+
property :customer_id, String, required: true
|
303
|
+
property :amount, Float, required: true
|
304
|
+
|
305
|
+
config do
|
306
|
+
transport SmartMessage::Transport::StdoutTransport.new(loopback: true)
|
307
|
+
serializer SmartMessage::Serializer::JSON.new
|
308
|
+
|
309
|
+
# Verbose logging for development
|
310
|
+
logger SmartMessage::Logger::Default.new(
|
311
|
+
log_file: STDOUT,
|
312
|
+
level: Logger::DEBUG
|
313
|
+
)
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.process(message_header, message_payload)
|
317
|
+
data = JSON.parse(message_payload)
|
318
|
+
|
319
|
+
# Logger is available in process method
|
320
|
+
logger.info "Processing order #{data['order_id']}"
|
321
|
+
|
322
|
+
# Business logic here
|
323
|
+
result = process_order(data)
|
324
|
+
|
325
|
+
logger.info "Order #{data['order_id']} completed: #{result}"
|
326
|
+
result
|
327
|
+
end
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
### Production Setup
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
class NotificationMessage < SmartMessage::Base
|
335
|
+
property :recipient, String, required: true
|
336
|
+
property :subject, String, required: true
|
337
|
+
property :body, String, required: true
|
338
|
+
|
339
|
+
config do
|
340
|
+
transport SmartMessage::Transport::RedisTransport.new
|
341
|
+
serializer SmartMessage::Serializer::JSON.new
|
342
|
+
|
343
|
+
# Production logging with file rotation
|
344
|
+
logger SmartMessage::Logger::Default.new(
|
345
|
+
log_file: '/var/log/app/notifications.log',
|
346
|
+
level: Logger::INFO
|
347
|
+
)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
```
|
351
|
+
|
352
|
+
### Docker/Kubernetes Setup
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
class EventMessage < SmartMessage::Base
|
356
|
+
property :event_type, String, required: true
|
357
|
+
property :data, Hash, required: true
|
358
|
+
|
359
|
+
config do
|
360
|
+
transport SmartMessage::Transport::RedisTransport.new(
|
361
|
+
redis_url: ENV['REDIS_URL']
|
362
|
+
)
|
363
|
+
serializer SmartMessage::Serializer::JSON.new
|
364
|
+
|
365
|
+
# Container-friendly STDOUT logging
|
366
|
+
logger SmartMessage::Logger::Default.new(
|
367
|
+
log_file: STDOUT,
|
368
|
+
level: ENV['LOG_LEVEL']&.upcase&.to_sym || Logger::INFO
|
369
|
+
)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
```
|
373
|
+
|
374
|
+
### Testing Setup
|
375
|
+
|
376
|
+
```ruby
|
377
|
+
# In test_helper.rb or similar
|
378
|
+
class TestMessage < SmartMessage::Base
|
379
|
+
property :test_data, Hash
|
380
|
+
|
381
|
+
config do
|
382
|
+
transport SmartMessage::Transport::MemoryTransport.new
|
383
|
+
serializer SmartMessage::Serializer::JSON.new
|
384
|
+
|
385
|
+
# Minimal logging for tests
|
386
|
+
logger SmartMessage::Logger::Default.new(
|
387
|
+
log_file: StringIO.new,
|
388
|
+
level: Logger::FATAL # Only fatal errors in tests
|
389
|
+
)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
```
|
393
|
+
|
394
|
+
### Instance-Level Logger Override
|
395
|
+
|
396
|
+
```ruby
|
397
|
+
# Different logger for specific instances
|
398
|
+
class PriorityMessage < SmartMessage::Base
|
399
|
+
property :priority, String
|
400
|
+
property :data, Hash
|
401
|
+
|
402
|
+
config do
|
403
|
+
transport SmartMessage::Transport::RedisTransport.new
|
404
|
+
serializer SmartMessage::Serializer::JSON.new
|
405
|
+
logger SmartMessage::Logger::Default.new # Default logger
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Override logger for high-priority messages
|
410
|
+
priority_logger = SmartMessage::Logger::Default.new(
|
411
|
+
log_file: '/var/log/priority.log',
|
412
|
+
level: Logger::DEBUG
|
413
|
+
)
|
414
|
+
|
415
|
+
urgent_message = PriorityMessage.new(priority: 'urgent', data: {...})
|
416
|
+
urgent_message.logger(priority_logger) # Override for this instance
|
417
|
+
urgent_message.publish
|
418
|
+
```
|
419
|
+
|
420
|
+
## Best Practices
|
421
|
+
|
422
|
+
1. **Use the default logger** unless you have specific requirements
|
423
|
+
2. **Log to STDOUT** in containerized environments
|
424
|
+
3. **Use appropriate log levels** - avoid DEBUG in production
|
425
|
+
4. **Tag your logs** for better searchability
|
426
|
+
5. **Consider structured logging** for production systems
|
427
|
+
6. **Test your logging** - ensure logs are helpful for debugging
|
428
|
+
7. **Monitor log volume** - excessive logging can impact performance
|
429
|
+
8. **Rotate log files** to prevent disk space issues (default logger handles this)
|
430
|
+
|
431
|
+
## Troubleshooting
|
432
|
+
|
433
|
+
### No logs appearing
|
434
|
+
- Check log level settings
|
435
|
+
- Verify file permissions
|
436
|
+
- Ensure logger is configured
|
437
|
+
|
438
|
+
### Too much logging
|
439
|
+
- Increase log level (DEBUG → INFO → WARN → ERROR)
|
440
|
+
- Consider filtering in production
|
441
|
+
|
442
|
+
### Performance issues
|
443
|
+
- Lower log level in production
|
444
|
+
- Use asynchronous logging for high-volume systems
|
445
|
+
- Consider structured logging formats
|
446
|
+
|
447
|
+
### Rails integration not working
|
448
|
+
- Ensure Rails is loaded before SmartMessage
|
449
|
+
- Check that `Rails.logger` is available
|
450
|
+
- Verify Rails environment is set correctly
|
451
|
+
|
452
|
+
For more troubleshooting tips, see the [Troubleshooting Guide](troubleshooting.md).
|