Lifecycle plugins allow you to hook into webhook request processing at three key points in the request lifecycle. This enables you to add custom functionality like metrics collection, error reporting, request logging, and more.
The webhook processing lifecycle provides three hooks:
on_request: Called before handler execution with request environment dataon_response: Called after successful handler execution with response dataon_error: Called when any error occurs during request processing
All lifecycle plugins have access to the global stats and failbot instruments for metrics and error reporting.
All lifecycle plugins must inherit from Hooks::Plugins::Lifecycle and can implement any or all of the lifecycle methods:
class MetricsLifecycle < Hooks::Plugins::Lifecycle
def on_request(env)
# Called before handler execution
# env contains Rack environment with request details
stats.increment("webhook.requests", {
path: env["PATH_INFO"],
method: env["REQUEST_METHOD"]
})
log.info "Processing webhook request: #{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
end
def on_response(env, response)
# Called after successful handler execution
# env contains the request environment
# response contains the handler's response data
stats.timing("webhook.response_time", env["hooks.processing_time"] || 0)
log.info "Webhook processed successfully: #{response.inspect}"
end
def on_error(exception, env)
# Called when any error occurs during request processing
# exception is the error that occurred
# env contains the request environment
failbot.report(exception, {
path: env["PATH_INFO"],
handler: env["hooks.handler"],
method: env["REQUEST_METHOD"]
})
log.error "Webhook processing failed: #{exception.message}"
end
endThe environment hash contains standard Rack environment variables plus webhook-specific data:
{
"REQUEST_METHOD" => "POST",
"PATH_INFO" => "/webhook/my-endpoint",
"HTTP_X_GITHUB_EVENT" => "push",
"HTTP_X_HUB_SIGNATURE_256" => "sha256=...",
"hooks.handler" => "MyHandler",
"hooks.config" => { ... }, # Endpoint configuration
"hooks.payload" => { ... }, # Parsed webhook payload
"hooks.headers" => { ... }, # Cleaned HTTP headers
"hooks.processing_time" => 0.123 # Available in on_response
}The response parameter in on_response contains the data returned by your handler:
{
status: "success",
message: "Webhook processed",
data: { ... }
}Lifecycle plugins have access to global components for cross-cutting concerns:
def on_request(env)
log.debug("Request details: #{env.inspect}")
log.info("Processing #{env['HTTP_X_GITHUB_EVENT']} event")
log.warn("Missing expected header") unless env["HTTP_X_GITHUB_EVENT"]
log.error("Critical validation failure")
enddef on_request(env)
# Increment counters (example)
stats.increment("webhook.requests", { event: env["HTTP_X_GITHUB_EVENT"] })
# Record values (example)
stats.record("webhook.payload_size", env["CONTENT_LENGTH"].to_i)
# Measure execution time (example)
stats.measure("webhook.processing", { handler: env["hooks.handler"] }) do
# Processing happens in the handler
end
end
def on_response(env, response)
# Record timing from environment (example)
stats.timing("webhook.duration", env["hooks.processing_time"])
enddef on_error(exception, env)
# Report errors with context (example)
failbot.report(exception, {
endpoint: env["PATH_INFO"],
event_type: env["HTTP_X_GITHUB_EVENT"],
handler: env["hooks.handler"]
})
# Report critical errors (example)
failbot.critical("Handler crashed", { handler: env["hooks.handler"] })
# Report warnings (example)
failbot.warning("Slow webhook processing", { duration: env["hooks.processing_time"] })
end
def on_request(env)
# Capture and report exceptions during processing (example)
failbot.capture({ context: "request_validation" }) do
validate_webhook_signature(env)
end
endTo use custom lifecycle plugins, specify the lifecycle_plugin_dir in your configuration:
# hooks.yml
lifecycle_plugin_dir: ./plugins/lifecycle
handler_plugin_dir: ./plugins/handlers
auth_plugin_dir: ./plugins/authPlace your lifecycle plugin files in the specified directory:
plugins/
└── lifecycle/
├── metrics_lifecycle.rb
├── audit_lifecycle.rb
└── performance_lifecycle.rb
Plugin files should be named using snake_case and the class name should be PascalCase:
metrics_lifecycle.rb→MetricsLifecycleaudit_lifecycle.rb→AuditLifecycleperformance_lifecycle.rb→PerformanceLifecycle
class MonitoringLifecycle < Hooks::Plugins::Lifecycle
def on_request(env)
# Record request metrics
stats.increment("webhook.requests.total", {
method: env["REQUEST_METHOD"],
path: env["PATH_INFO"],
event: env["HTTP_X_GITHUB_EVENT"] || "unknown"
})
# Log request start
log.info("Webhook request started", {
method: env["REQUEST_METHOD"],
path: env["PATH_INFO"],
user_agent: env["HTTP_USER_AGENT"],
content_length: env["CONTENT_LENGTH"]
})
end
def on_response(env, response)
# Record successful processing
stats.increment("webhook.requests.success", {
handler: env["hooks.handler"],
event: env["HTTP_X_GITHUB_EVENT"]
})
# Record processing time
if env["hooks.processing_time"]
stats.timing("webhook.processing_time", env["hooks.processing_time"], {
handler: env["hooks.handler"]
})
end
log.info("Webhook request completed successfully", {
handler: env["hooks.handler"],
response_type: response.class.name,
processing_time: env["hooks.processing_time"]
})
end
def on_error(exception, env)
# Record error metrics
stats.increment("webhook.requests.error", {
handler: env["hooks.handler"],
error_type: exception.class.name,
event: env["HTTP_X_GITHUB_EVENT"]
})
# Report error with full context
failbot.report(exception, {
handler: env["hooks.handler"],
method: env["REQUEST_METHOD"],
path: env["PATH_INFO"],
event: env["HTTP_X_GITHUB_EVENT"],
user_agent: env["HTTP_USER_AGENT"],
content_length: env["CONTENT_LENGTH"]
})
log.error("Webhook request failed", {
error: exception.message,
handler: env["hooks.handler"],
path: env["PATH_INFO"]
})
end
end- Keep lifecycle methods fast: Avoid slow operations that could impact webhook processing performance
- Handle errors gracefully: Lifecycle plugins should not cause webhook processing to fail
- Use appropriate log levels: Debug for detailed info, info for normal flow, warn for issues, error for failures
- Include relevant context: Add useful tags and context to metrics and error reports
- Test thoroughly: Lifecycle plugins run for every webhook request, so bugs have high impact