Context
Context allows you to define payload data included in every log entry, useful for adding request IDs, user IDs, or other contextual information spanning multiple log entries.
Basic context usage
logger = Dry.Logger(:my_app)
# Set context data
logger.context[:request_id] = "req-123"
logger.context[:user_id] = 42
# All subsequent logs include context automatically
logger.info("User action", action: "login")
# User action request_id="req-123" user_id=42 action="login"
logger.info("Profile updated")
# Profile updated request_id="req-123" user_id=42
Request-scoped context
Context is thread-local by default, making it perfect for web applications where each request runs in its own thread:
# In your Rack middleware or Rails controller
class RequestLogger
def call(env)
request = Rack::Request.new(env)
logger.context[:request_id] = request.request_id
logger.context[:ip] = request.ip
logger.context[:method] = request.request_method
logger.context[:path] = request.path
logger.info("Request started")
# Request started request_id="..." ip="192.168.1.1" method="GET" path="/users"
@app.call(env)
ensure
# Context automatically clears when thread ends
logger.context.clear
end
end
Isolated context
You can create a logger with an isolated context (not thread-local):
logger = Dry.Logger(:my_app, context: {})
logger.context[:component] = "database"
logger.info("Connection opened")
# Connection opened component="database"
Context best practices
Do use context for request-scoped data:
# Good - data relevant to all logs in this request
logger.context[:request_id] = request.id
logger.context[:user_id] = current_user.id
logger.context[:tenant_id] = current_tenant.id
Don't use context for log-specific data:
# Bad - this should be in the log entry itself
logger.context[:action] = "login"
logger.info("User action") # Wrong approach
# Good - put it in the specific log entry
logger.info("User action", action: "login")