Middleware in the queue
library enables pre and post-processing around your job handlers, facilitating functionalities like logging, tracing, and metrics collection without cluttering your core logic.
Middleware functions encapsulate your job processing logic. They're executed in sequence, allowing for operations to be inserted before and after the main job handler executes.
Middleware is crafted as a function that accepts a HandlerFunc
and returns a modified HandlerFunc
incorporating the middleware's operations.
type MiddlewareFunc func(HandlerFunc) HandlerFunc
Below is an example of logging middleware:
func LoggingMiddleware(logger *log.Logger) MiddlewareFunc {
return func(next HandlerFunc) HandlerFunc {
return func(ctx context.Context, job *Job) error {
logger.Printf("Starting job: %s", job.Type)
err := next(ctx, job)
if err != nil {
logger.Printf("Job %s failed: %v", job.Type, err)
} else {
logger.Printf("Job %s completed successfully", job.Type)
}
return err
}
}
}
Middleware can be applied at various levels for different scopes of control: globally, to job groups, or to individual job handlers.
Impacts all jobs processed by a worker.
worker.Use(LoggingMiddleware(log.New(os.Stdout, "", log.LstdFlags)))
Targets all jobs within a specified group.
emailJobs := worker.Group("email")
emailJobs.Use(TracingMiddleware(tracer))
Applies directly to a specified job handler.
worker.Register("send_email", SendEmailHandler, WithMiddleware(LoggingMiddleware(log.New(os.Stdout, "", log.LstdFlags))))
- Simplicity: Middleware should focus on a single responsibility for clarity and maintainability.
- Error Handling: Middleware offers a strategic point for consistent error management.
- Performance: Consider the impact on performance, especially for operations involving I/O.
Thoughtful middleware use can significantly streamline and enhance the handling of background jobs within the queue
library.