Using Lists for Request Logging in Redis: A Practical Guide
Learn to implement efficient request logging using Redis lists. Discover how to append activity, limit log size, and retrieve recent history for your API.

Previously in this course, we covered managing hash operations to store structured user data. In this lesson, we add a logging layer to our ongoing API project, using Redis Lists to track recent user activity.
While hashes are perfect for single-entity state, they fall short when you need to maintain an ordered sequence of events. For tracking "who did what and when," we turn to the Redis List data type.
Why Use Redis Lists for Logging?
A Redis List is a linked list of strings. It excels at ordered data, allowing you to push elements to either the head or the tail with $O(1)$ performance. Unlike a relational database, where an INSERT into a massive logs table might require index updates and disk I/O, appending to a Redis list is an in-memory operation that doesn't slow down as your log grows.
In our API project, we want to keep track of the last 100 requests. If we stored this in a database, we would constantly be cleaning up old rows to prevent the table from ballooning. With Redis, we can handle the append and the cleanup in a single, atomic flow.
The Logging Pattern: LPUSH and LTRIM

To implement a "recent activity" log, we follow a two-step pattern:
LPUSH: Add the new log entry to the head of the list.LTRIM: Trim the list to a fixed size, discarding the oldest entries.
Worked Example: Implementing the Logger
Assuming you have already set up your project baseline, let's create a simple function to record an activity log.
JAVASCRIPT// activityLogger.js const { createClient } = require(CE9178">'redis'); const client = createClient(); client.connect(); async function logRequest(userId, action) { const logKey = CE9178">`logs:user:${userId}`; const logEntry = JSON.stringify({ action, timestamp: new Date().toISOString() }); // 1. Push the new log to the left(start) of the list await client.lPush(logKey, logEntry); // 2. Keep only the most recent 10 logs // Index 0 is the newest, 9 is the oldest await client.lTrim(logKey, 0, 9); } // Example usage logRequest(CE9178">'user_123', CE9178">'GET /api/products');
In this example, LPUSH places the newest entry at index 0. When we call LTRIM(key, 0, 9), Redis keeps the first 10 elements (indices 0 through 9) and discards everything else. This ensures our memory footprint remains constant regardless of how many requests a user makes.
Retrieving History
Since the list is ordered chronologically (newest first), retrieving the full history is trivial. We use LRANGE to fetch the entire list.
JAVASCRIPTasync function getRecentActivity(userId) { const logKey = CE9178">`logs:user:${userId}`; // Fetch all logs from index 0 to 9 return await client.lRange(logKey, 0, 9); }
Hands-on Exercise
- Modify the
logRequestfunction above to accept a custom limit (e.g.,maxLogs). - Implement a route in your API that calls
logRequestevery time a request is made. - Add an endpoint
GET /api/historythat useslRangeto return the last 5 logs for the current user. - Verify using
redis-cliby runningLRANGE logs:user:user_123 0 -1after making a few requests.
Common Pitfalls
- Forgetting to Trim: Without
LTRIM, your list will grow indefinitely, eventually consuming all available RAM. Always pair a push with a trim when the log size must be fixed. - Performance at Scale: While
LPUSHandLTRIMare $O(1)$, remember that if you store massive JSON blobs, you might hit network bandwidth limits. Keep your log entries lean (timestamps, short action descriptions, and IDs). - Key Expiration: If you want these logs to disappear after a period of inactivity, don't forget to set a TTL on the list using
EXPIRE.
FAQ
Can I use RPUSH instead of LPUSH?
Yes, but then your newest items will be at the end of the list. You would then need to use LTRIM differently to keep the last $N$ items. LPUSH is standard for "newest-first" feeds.
Is this a replacement for a database audit log? No. This is for recent activity (e.g., a "Last 10 Actions" widget). For permanent audit trails, you should write to a persistent store, similar to how one might handle auditing changes in a CMS.
What happens if I set LTRIM to index 0, 0?
The list will be truncated to exactly one element—the most recent one.
Recap
By combining LPUSH and LTRIM, we created a self-maintaining, fixed-size log buffer. This approach is highly efficient for real-time activity tracking, offloading the burden of sorting and cleanup from your primary database.
Up next: Set Operations: Intersection and Union
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

AI Automation & Agentic Workflow Development
Automate the repetitive work eating your time — content pipelines, data workflows, and agentic AI tasks that run themselves.


