Logging in SmartMessage¶
SmartMessage provides comprehensive logging capabilities with support for multiple output formats, colorization, structured logging, and file rolling. Built on the Lumberjack logging framework, it offers production-ready features with flexible configuration options.
Table of Contents¶
- Quick Start
- Configuration Options
- Output Formats
- Colorized Console Output
- File Rolling
- Structured Logging
- Application Integration
- SmartMessage Integration
- Examples
- Best Practices
Quick Start¶
Configure SmartMessage logging through the global configuration block:
SmartMessage.configure do |config|
config.logger = STDOUT # Output destination
config.log_level = :info # Log level
config.log_format = :text # Format
config.log_colorize = true # Enable colors
end
# Access the logger in your application
logger = SmartMessage.configuration.default_logger
logger.info("Application started", component: "main")
Configuration Options¶
Global Configuration¶
All logging configuration is done through the SmartMessage.configure
block:
SmartMessage.configure do |config|
# Required: Output destination
config.logger = STDOUT # or file path, STDERR
# Optional: Logging behavior
config.log_level = :info # :debug, :info, :warn, :error, :fatal
config.log_format = :text # :text, :json
config.log_colorize = true # Enable colorized console output
config.log_include_source = false # Include file/line information
config.log_structured_data = false # Enable structured metadata
# Optional: File rolling options
config.log_options = {
roll_by_size: true,
max_file_size: 10 * 1024 * 1024, # 10 MB
keep_files: 5, # Keep 5 old files
roll_by_date: false, # Alternative: date-based rolling
date_pattern: '%Y-%m-%d' # Daily pattern
}
end
Configuration Details¶
Option | Type | Default | Description |
---|---|---|---|
logger |
String/IO | Required | Output destination (file path, STDOUT, STDERR) |
log_level |
Symbol | :info |
Log level (:debug , :info , :warn , :error , :fatal ) |
log_format |
Symbol | :text |
Output format (:text , :json ) |
log_colorize |
Boolean | false |
Enable colorized console output |
log_include_source |
Boolean | false |
Include source file and line information |
log_structured_data |
Boolean | false |
Enable structured data logging |
log_options |
Hash | {} |
Additional Lumberjack options |
Output Formats¶
Text Format (Default)¶
Human-readable text output with optional colorization:
SmartMessage.configure do |config|
config.logger = STDOUT
config.log_format = :text
config.log_colorize = true
end
logger = SmartMessage.configuration.default_logger
logger.info("User login successful", user_id: 12345)
# Output: 2025-01-15 10:30:45 [INFO] User login successful user_id=12345
JSON Format¶
Machine-readable structured JSON output:
SmartMessage.configure do |config|
config.logger = "log/application.log"
config.log_format = :json
config.log_structured_data = true
config.log_include_source = true
end
logger = SmartMessage.configuration.default_logger
logger.info("User action", user_id: 12345, action: "login")
# Output: {"timestamp":"2025-01-15T10:30:45.123Z","level":"INFO","message":"User action","user_id":12345,"action":"login","source":"app.rb:42:in `authenticate`"}
Colorized Console Output¶
SmartMessage provides colorized console output for improved readability during development:
SmartMessage.configure do |config|
config.logger = STDOUT
config.log_format = :text
config.log_colorize = true
end
logger = SmartMessage.configuration.default_logger
logger.debug("Debug message") # Green background, black text, bold
logger.info("Info message") # Bright white text
logger.warn("Warning message") # Yellow background, white bold text
logger.error("Error message") # Light red background, white bold text
logger.fatal("Fatal message") # Light red background, yellow bold text
Color Scheme¶
Level | Foreground | Background | Style |
---|---|---|---|
DEBUG | Black | Green | Bold |
INFO | Bright White | None | None |
WARN | White | Yellow | Bold |
ERROR | White | Light Red | Bold |
FATAL | Yellow | Light Red | Bold |
Note: Colorization is automatically disabled for file output to keep log files clean.
File Rolling¶
SmartMessage supports both size-based and date-based log file rolling:
Size-Based Rolling¶
SmartMessage.configure do |config|
config.logger = "log/application.log"
config.log_options = {
roll_by_size: true,
max_file_size: 10 * 1024 * 1024, # 10 MB
keep_files: 5 # Keep 5 old files
}
end
Files are named: application.log
, application.log.1
, application.log.2
, etc.
Date-Based Rolling¶
SmartMessage.configure do |config|
config.logger = "log/application.log"
config.log_options = {
roll_by_date: true,
date_pattern: '%Y-%m-%d' # Daily rolling
}
end
Files are named: application.log.2025-01-15
, application.log.2025-01-14
, etc.
Rolling Options¶
Option | Type | Description |
---|---|---|
roll_by_size |
Boolean | Enable size-based rolling |
max_file_size |
Integer | Maximum file size in bytes |
keep_files |
Integer | Number of old files to keep |
roll_by_date |
Boolean | Enable date-based rolling |
date_pattern |
String | Date format pattern |
Structured Logging¶
Enable structured data logging to include metadata with your log entries:
SmartMessage.configure do |config|
config.logger = "log/application.log"
config.log_format = :json
config.log_structured_data = true
config.log_include_source = true
end
logger = SmartMessage.configuration.default_logger
# Log with structured data
logger.info("User registration",
user_id: "user123",
email: "user@example.com",
registration_source: "web",
timestamp: Time.now.iso8601)
# Log with block for conditional data
logger.warn("Database slow query") do
{
query: "SELECT * FROM users WHERE status = ?",
duration_ms: 1500,
table: "users",
slow_query: true
}
end
Application Integration¶
Accessing the Logger¶
The configured logger is available globally:
# Get the globally configured logger
logger = SmartMessage.configuration.default_logger
# Use in your application
logger.info("Application starting", version: "1.0.0")
logger.warn("Configuration missing", config_key: "database_url")
logger.error("Service unavailable", service: "payment_gateway")
Class-Level Integration¶
class OrderService
def initialize
@logger = SmartMessage.configuration.default_logger
end
def process_order(order)
@logger.info("Processing order",
order_id: order.id,
customer_id: order.customer_id,
amount: order.amount)
begin
# Process order logic
result = perform_processing(order)
@logger.info("Order completed",
order_id: order.id,
status: "success",
processing_time_ms: result[:duration])
rescue StandardError => e
@logger.error("Order processing failed",
order_id: order.id,
error: e.message,
error_class: e.class.name)
raise
end
end
end
SmartMessage Integration¶
SmartMessage classes automatically use the configured logger:
class OrderMessage < SmartMessage::Base
property :order_id, required: true
property :customer_id, required: true
property :amount, required: true
config do
transport SmartMessage::Transport::StdoutTransport.new
serializer SmartMessage::Serializer::Json.new
from 'order-service'
end
def process
# Logger is automatically available
logger.info("Processing order message",
message_id: _sm_header.uuid,
order_id: order_id,
customer_id: customer_id,
amount: amount)
# Log the complete message structure
logger.debug("Message details",
header: _sm_header.to_h,
payload: _sm_payload,
full_message: to_h)
# Process the order
case amount
when 0..100
logger.info("Small order processed", order_id: order_id)
when 101..1000
logger.warn("Medium order requires review", order_id: order_id)
else
logger.error("Large order requires manual approval",
order_id: order_id,
amount: amount)
end
end
end
Examples¶
Development Configuration¶
Perfect for local development with colorized console output:
SmartMessage.configure do |config|
config.logger = STDOUT
config.log_level = :debug
config.log_format = :text
config.log_colorize = true
config.log_include_source = true
end
Production Configuration¶
Production setup with JSON logging and file rolling:
SmartMessage.configure do |config|
config.logger = "/var/log/app/smartmessage.log"
config.log_level = :info
config.log_format = :json
config.log_colorize = false
config.log_structured_data = true
config.log_include_source = false
config.log_options = {
roll_by_size: true,
max_file_size: 50 * 1024 * 1024, # 50 MB
keep_files: 10
}
end
Docker/Container Configuration¶
Container-friendly setup with structured STDOUT logging:
SmartMessage.configure do |config|
config.logger = STDOUT
config.log_level = ENV['LOG_LEVEL']&.downcase&.to_sym || :info
config.log_format = :json
config.log_colorize = false
config.log_structured_data = true
config.log_include_source = true
end
Testing Configuration¶
Minimal logging for test environments:
SmartMessage.configure do |config|
config.logger = STDERR
config.log_level = :error
config.log_format = :text
config.log_colorize = false
end
Multiple Logger Configurations¶
You can create multiple logger instances for different purposes:
# Configure global logger
SmartMessage.configure do |config|
config.logger = "log/application.log"
config.log_level = :info
config.log_format = :json
end
# Create additional loggers for specific needs
console_logger = SmartMessage::Logger::Lumberjack.new(
log_file: STDERR,
level: :warn,
format: :text,
colorize: true
)
debug_logger = SmartMessage::Logger::Lumberjack.new(
log_file: "log/debug.log",
level: :debug,
format: :text,
include_source: true
)
# Use in application
console_logger.warn("Service degraded")
debug_logger.debug("Detailed debugging info", state: app_state)
Best Practices¶
1. Environment-Based Configuration¶
Configure logging based on your environment:
case ENV['RAILS_ENV'] || ENV['ENVIRONMENT']
when 'production'
SmartMessage.configure do |config|
config.logger = "/var/log/app/smartmessage.log"
config.log_level = :info
config.log_format = :json
config.log_structured_data = true
config.log_options = { roll_by_size: true, max_file_size: 50.megabytes, keep_files: 10 }
end
when 'development'
SmartMessage.configure do |config|
config.logger = STDOUT
config.log_level = :debug
config.log_format = :text
config.log_colorize = true
config.log_include_source = true
end
when 'test'
SmartMessage.configure do |config|
config.logger = STDERR
config.log_level = :error
config.log_format = :text
end
end
2. Structured Data¶
Use structured data for better log analysis:
# Good: Structured data
logger.info("User action",
user_id: user.id,
action: "login",
ip_address: request.ip,
user_agent: request.user_agent)
# Avoid: String interpolation
logger.info("User #{user.id} logged in from #{request.ip}")
3. Appropriate Log Levels¶
Use log levels appropriately:
- DEBUG: Detailed information for diagnosing problems
- INFO: General information about program execution
- WARN: Something unexpected happened, but the application is still working
- ERROR: A serious problem occurred, but the application can continue
- FATAL: A very serious error occurred, application may not be able to continue
4. Performance Considerations¶
- Use appropriate log levels in production (avoid DEBUG)
- Consider async logging for high-volume applications
- Use structured data instead of string concatenation
- Be mindful of log volume and storage costs
5. Security¶
- Never log sensitive data (passwords, tokens, credit card numbers)
- Sanitize user input before logging
- Use structured data to avoid log injection attacks
# Good: Structured data prevents injection
logger.info("User input received", user_input: params[:query])
# Avoid: Direct string interpolation
logger.info("User searched for: #{params[:query]}")
6. Testing¶
Test your logging configuration:
# Test that logs are being generated
require 'stringio'
log_output = StringIO.new
SmartMessage.configure do |config|
config.logger = log_output
config.log_level = :debug
end
logger = SmartMessage.configuration.default_logger
logger.info("Test message")
assert_includes log_output.string, "Test message"
For more information, see the comprehensive logging example at examples/show_logger.rb
.