@ceschiatti/redistail
v0.0.20
Published
CLI tool for monitoring Redis streams and PubSub
Downloads
205
Readme
Redistail
A real-time Redis message monitoring CLI, similar to tail -f for log files.
Supports both Pub/Sub channels and Redis Streams with automatic
reconnection, multi-day stream traversal, and colored output.
| Feature | Description |
| :--- | :--- |
| Pub/Sub monitoring | Subscribe to any channel and display messages as they arrive |
| Stream monitoring | Continuous polling via blocking XREAD with entry ID continuity |
| Multi-day streams | Automatically traverse daily-partitioned streams (Cedro protocol) |
| Date filtering | Retrieve historical messages with --from and transition to real-time |
| Colored output | ANSI color-coded timestamps, channels, fields, and errors |
| JSON pretty-print | Automatic detection and formatting of JSON payloads |
| Cross-platform | Pre-compiled binaries for Linux, macOS, and Windows via Bun |
| Effect-TS native | Built entirely with Effect services, layers, and structured errors |
Table of Contents
- Installation
- Quick Start
- Architecture
- CLI Reference
- Monitoring Modes
- Configuration
- Services & Layers
- Error Model
- Types Reference
- Build System
- Output Format
- Examples
- Troubleshooting
- Peer Dependencies
Installation
Global Installation
# Using pnpm (recommended)
pnpm add -g @ceschiatti/redistail
# Using npm
npm install -g @ceschiatti/redistail
# Using bun
bun add -g @ceschiatti/redistailLocal Installation
pnpm add @ceschiatti/redistail
# Run locally
npx redistail --helpBinary Distribution
The npm package ships only the JS bundle (~2 MB). On postinstall, a platform-specific
compiled binary is downloaded from
GitHub Releases. If the download fails
(no network, no release for the platform), the launcher falls back to
node dist/redistail.js automatically.
To skip binary download: REDISTAIL_SKIP_BINARY_DOWNLOAD=1 pnpm add -g @ceschiatti/redistail
Verify Installation
redistail --version
redistail --helpQuick Start
# Monitor a Pub/Sub channel
redistail pubsub my-channel
# Monitor a Redis Stream
redistail stream my-stream
# Historical messages + real-time transition
redistail stream my-stream --from "2026-01-09T10:00:00Z"
# Custom Redis connection
REDIS_HOST=redis.example.com redistail pubsub notificationsArchitecture
CLI args + env vars
|
v
CLIConfigService (parses args, loads env config, shows help/version)
|
v
RedistailConfig (immutable config: redis + display + monitoring)
/ | \
v v v
DisplayService PubSubMonitorService StreamMonitorService
(formatting) (channel monitoring) (stream monitoring)
| | |
| v v
| RedisPubSub RedisStream
| (effect-redis) (effect-redis)
| \ /
| v v
| RedisConnectionOptions
| |
v v
stdout/stderr Redis ServerConnection Optimization
The CLI creates only the Redis connections needed for the selected mode:
| Mode | Redis Connections | Layer Used |
| :--- | :--- | :--- |
| pubsub | 2 (publish + subscribe) | PubSubAppLive |
| stream | 2 (producer + consumer) | StreamAppLive |
| --help / --version | 0 | CLIConfigServiceLive |
| Invalid args | 0 | CLIConfigServiceLive |
CLI Reference
Syntax
redistail <CONNECTION_TYPE> <TOPIC_NAME> [OPTIONS]Arguments
| Argument | Values | Description |
| :--- | :--- | :--- |
| CONNECTION_TYPE | pubsub, stream | Type of Redis connection |
| TOPIC_NAME | any string | Channel name (Pub/Sub) or stream key (Streams) |
Options
| Option | Description |
| :--- | :--- |
| -h, --help | Show help message with all options and environment variables |
| -v, --version | Show version, build hash, compile date, Effect and effect-redis versions |
| --from <date> | (Stream only) Retrieve historical messages from a date, then transition to real-time |
Version Output
redistail 0.0.17
Build: 4cc301e
Compiled: 2026-01-19T10:37:28.864Z (UTC)
Effect: 3.19.16
effect-redis: 0.0.32Monitoring Modes
Pub/Sub Monitoring
redistail pubsub channel-name- Subscribes to the specified channel via
RedisPubSub.subscribe() - Displays messages as they are published
- Shows timestamp, channel name, and message content
- Automatic reconnection with exponential backoff (up to
maxReconnectAttempts) - Continues running until interrupted (Ctrl+C)
Stream Monitoring
redistail stream stream-name- Reads from the stream starting from the latest entry (
$) - Uses blocking XREAD to wait for new entries
- Shows timestamp, entry ID, and all message fields
- Maintains entry ID continuity across reconnections
Stream with Date Filter
redistail stream stream-name --from "2026-01-09T10:00:00Z"- Retrieves all historical messages from the date onwards via
XRANGE - Displays them in chronological order
- Seamlessly transitions to real-time monitoring from the last seen entry ID
- No messages are lost during the transition
Supported date formats:
| Format | Example |
| :--- | :--- |
| Keywords | today, yesterday |
| ISO 8601 | 2026-01-09T10:00:00Z, 2026-01-09 |
| RFC 2822 | Fri, 09 Jan 2026 10:00:00 GMT |
| Simple | Jan 9 2026, 2026/01/09 |
Edge cases:
- Future date: Warning logged, monitoring starts from latest entry
- Old date: All available messages are retrieved
- Empty stream: Proceeds directly to real-time mode
- Invalid format: Clear error with supported format examples
Multi-day Stream Monitoring
For daily-partitioned streams (common in the Cedro protocol), provide a partial
stream name (3 colon-separated fields) with --from:
redistail stream stream:raw:WING26 --from yesterdayStream name formats:
- Partial (3 fields):
stream:<type>:<ticker>(e.g.,stream:raw:WING26) - Full (6 fields):
stream:<type>:<ticker>:<year>:<month>:<day>
How it works:
- Generates date range from
--fromdate to today - Constructs full stream names for each day (e.g.,
stream:raw:WING26:2026:01:16) - Sequentially reads all historical messages from each day's stream
- On the final day (today), transitions to real-time monitoring
- Missing or empty streams are skipped with a warning
📅 Multi-day mode: stream:raw:WING26 from 2026-01-16T00:00:00.000Z
⏳ Iterating through daily streams, then transitioning to real-time...
📆 Processing stream: stream:raw:WING26:2026:01:16
📆 Processing stream: stream:raw:WING26:2026:01:17
📆 Processing stream: stream:raw:WING26:2026:01:18 (switching to real-time)Configuration
All configuration is loaded from environment variables via Effect.Config with
validation and defaults.
Environment Variables
Redis Connection
| Variable | Default | Description |
| :--- | :--- | :--- |
| REDIS_HOST | 127.0.0.1 | Redis server hostname |
| REDIS_PORT | 6379 | Redis server port (1–65535) |
| REDIS_URL | — | Complete Redis URL (overrides host/port) |
| REDIS_TIMEOUT | 5000 | Connection timeout in milliseconds |
| REDIS_RETRY_ATTEMPTS | 3 | Number of connection retry attempts |
| REDIS_RETRY_DELAY | 1000 | Delay between retries in milliseconds |
Display
| Variable | Default | Description |
| :--- | :--- | :--- |
| REDISTAIL_COLORS | true | Enable ANSI colored output |
| REDISTAIL_TIMESTAMPS | true | Show timestamps in output |
| REDISTAIL_PRETTY_JSON | true | Pretty-print JSON content |
Monitoring
| Variable | Default | Description |
| :--- | :--- | :--- |
| REDISTAIL_BLOCK_TIMEOUT | 5000 | Stream blocking timeout in milliseconds |
| REDISTAIL_MAX_RECONNECT_ATTEMPTS | 5 | Maximum reconnection attempts |
Configuration Examples
# Production
REDIS_URL=redis://user:[email protected]:6380 \
REDISTAIL_COLORS=false \
redistail stream orders
# Development
REDIS_HOST=localhost REDISTAIL_PRETTY_JSON=true redistail pubsub app-logs
# CI/CD (minimal output)
REDISTAIL_COLORS=false REDISTAIL_TIMESTAMPS=false redistail stream eventsServices & Layers
Redistail is built as a set of Effect-TS services composed via layers.
Service Catalog
| Service | Tag | Purpose |
| :--- | :--- | :--- |
| CLIConfigService | 'CLIConfigService' | Argument parsing, env config loading, help/version display |
| DisplayService | '@redistail/DisplayService' | Message formatting, color output, stdout/stderr separation |
| PubSubMonitorService | 'PubSubMonitorService' | Pub/Sub channel monitoring with reconnection |
| StreamMonitorService | 'StreamMonitorService' | Stream monitoring, date filtering, multi-day traversal |
| SignalHandlerService | 'SignalHandlerService' | SIGINT/SIGTERM handling, graceful shutdown |
| DateFilterService | 'DateFilterService' | Date parsing and Redis stream ID conversion |
Layer Composition
// Full application layer (all services)
import { AppLive } from './layers.js';
// Mode-specific layers (optimized connection count)
import { PubSubAppLive, StreamAppLive } from './layers.js';
// Testing layers
import { createTestAppLayer, createMockAppLayer } from './layers.js';Layer Dependency Chain
CLIConfigServiceLive (no deps)
|
v
RedistailConfigLive (depends on CLIConfigService)
/ \
v v
RedisConnectionLive DisplayServiceLive
|
v
RedisPubSubLive / RedisStreamLive
|
v
PubSubMonitorServiceLive / StreamMonitorServiceLiveError Model
Every operation fails with RedistailError, a discriminated union of three
tagged error types. All extend Data.TaggedError for pattern matching with
Effect.catchTag.
class CLIError extends Data.TaggedError('CLIError')<{
readonly reason: 'InvalidArguments' | 'MissingArguments'
| 'UnknownCommand' | 'InvalidDateFormat';
readonly message: string;
readonly context?: string;
}> {}
class ConfigError extends Data.TaggedError('ConfigError')<{
readonly reason: 'InvalidEnvironment' | 'ConnectionFailed'
| 'ValidationFailed';
readonly message: string;
readonly cause?: unknown;
readonly context?: string;
}> {}
class MonitoringError extends Data.TaggedError('MonitoringError')<{
readonly reason: 'ConnectionLost' | 'SubscriptionFailed'
| 'StreamReadFailed';
readonly message: string;
readonly retryable: boolean;
readonly cause?: unknown;
}> {}
type RedistailError = CLIError | ConfigError | MonitoringError;| Error | Tag | When |
| :--- | :--- | :--- |
| CLIError | 'CLIError' | Invalid arguments, missing arguments, bad date format |
| ConfigError | 'ConfigError' | Invalid environment variables, connection failures |
| MonitoringError | 'MonitoringError' | Connection lost, subscription failed, stream read failed |
Error Helper Functions
| Helper | Creates | Retryable |
| :--- | :--- | :--- |
| createInvalidArgumentError(msg) | CLIError (InvalidArguments) | — |
| createMissingArgumentError(msg) | CLIError (MissingArguments) | — |
| createDateFilterError(msg) | CLIError (InvalidDateFormat) | — |
| createEnvironmentError(msg) | ConfigError (InvalidEnvironment) | — |
| createConnectionError(msg) | ConfigError (ConnectionFailed) | — |
| createConnectionLostError(msg) | MonitoringError (ConnectionLost) | Yes |
| createSubscriptionFailedError(msg) | MonitoringError (SubscriptionFailed) | Configurable |
| createStreamReadFailedError(msg) | MonitoringError (StreamReadFailed) | Configurable |
Types Reference
CLI Configuration
interface CLIConfig {
readonly connectionType: 'pubsub' | 'stream';
readonly topicName: string;
readonly help?: boolean;
readonly version?: boolean;
readonly fromDate?: Date;
}Application Configuration
interface RedistailConfig {
readonly redis: {
readonly host: string;
readonly port: number;
readonly url?: string;
readonly timeout: number;
readonly retryAttempts: number;
readonly retryDelay: number;
};
readonly display: {
readonly colors: boolean;
readonly timestamps: boolean;
readonly prettyJson: boolean;
};
readonly monitoring: {
readonly blockTimeout: number;
readonly maxReconnectAttempts: number;
};
}Message Types
interface PubSubMessage {
readonly timestamp: Date;
readonly channel: string;
readonly content: string;
}
interface StreamMessage {
readonly timestamp: Date;
readonly streamKey: string;
readonly entryId: string;
readonly fields: Record<string, string>;
}Stream Name Utilities
| Function | Signature | Description |
| :--- | :--- | :--- |
| isPartialStreamName | (name: string) => boolean | Check if name has 3 colon-separated fields |
| isFullStreamName | (name: string) => boolean | Check if name has 6 colon-separated fields |
| constructDailyStreamName | (partial: string, date: Date) => string | Build full name from partial + date |
| generateDateRange | (from: Date, to: Date) => Date[] | Generate inclusive date array |
| generateDailyStreamNames | (partial: string, from: Date, to: Date) => string[] | Generate all daily stream names |
Build System
The build process is handled by build-cli.js and produces both ESM modules and
cross-platform compiled binaries.
Build Command
pnpm build # or: node build-cli.jsBuild Steps
- Version injection: Reads
src/version.tstemplate, replaces placeholders with git commit hash, package version, and ISO timestamp, writessrc/version-generated.ts - ESM bundle:
bun buildproducesdist/redistail.js(Node.js target, ESM) - Cross-platform binaries: Compiled via
bun build --compilefor 5 targets - Launcher: Copies
redistail-launcher.jstodist/
Build Targets
| Platform | Architecture | Output |
| :--- | :--- | :--- |
| Windows | x64 | redistail-win-x64.exe |
| Linux | x64 | redistail-linux-x64 |
| Linux | ARM64 | redistail-linux-arm64 |
| macOS | x64 | redistail-osx-x64 |
| macOS | ARM64 | redistail-osx-arm64 |
Distribution Model
Binaries are excluded from the npm package (via .npmignore) to keep the
tarball under ~2 MB. Instead, binaries are distributed through GitHub Releases:
- npm package ships:
dist/redistail.js+dist/redistail-launcher.js+scripts/postinstall.js postinstallrunsscripts/postinstall.js, which downloads the platform-specific binary fromhttps://github.com/6qat/monorepo/releases/download/redistail-v{version}/{binary}- Launcher (
redistail-launcher.js) checks for the binary; if absent, falls back tonode dist/redistail.js
Releasing Binaries
After pnpm build, upload the 5 binaries from dist/ to a GitHub Release
tagged redistail-v{version}:
gh release create redistail-v0.0.18 \
dist/redistail-linux-x64 \
dist/redistail-linux-arm64 \
dist/redistail-osx-x64 \
dist/redistail-osx-arm64 \
dist/redistail-win-x64.exe \
--title "redistail v0.0.18" \
--notes "Pre-compiled binaries for redistail v0.0.18"Output Format
Pub/Sub Messages
TIMESTAMP [CHANNEL] MESSAGE_CONTENT2026-01-09 15:30:45.123 [notifications] User alice logged inStream Messages
TIMESTAMP [STREAM_KEY:ENTRY_ID] FIELD1=VALUE1 FIELD2=VALUE2 ...2026-01-09 15:31:12.345 [events:1704636672345-0] user=alice action=loginIf a stream entry has a single data or message field, the field name is
omitted and the value is displayed directly (unwrapped payload).
Color Coding
| Color | Element |
| :--- | :--- |
| Gray (\x1b[90m) | Timestamps |
| Cyan (\x1b[36m) | Pub/Sub channel names |
| Magenta (\x1b[35m) | Stream names and entry IDs |
| Yellow (\x1b[33m) | Field names in streams |
| White (\x1b[37m) | Message content and field values |
| Red (\x1b[31m) | Error messages |
Binary Content Safety
Non-printable characters are automatically escaped as \xHH. Content with more
than 10% non-printable characters is treated as binary and fully escaped.
Examples
Basic Pub/Sub
redistail pubsub notifications2026-01-09 15:30:45.123 [notifications] Welcome to the system!
2026-01-09 15:30:47.456 [notifications] New user registeredMulti-day Data Recovery
redistail stream stream:raw:WING26 --from "2026-01-16"JSON Pretty-Printing
Input message: {"user":"alice","action":"login"}
2026-01-09 15:31:12.345 [api-events] {
"user": "alice",
"action": "login"
}Pipe to File
REDISTAIL_COLORS=false redistail stream events >> /var/log/redis-events.logMultiple Instances
redistail pubsub critical-alerts &
redistail stream user-events &
redistail pubsub app-logs &Bash Monitoring Script
#!/bin/bash
CHANNELS=("user-events" "system-logs" "error-alerts")
PIDS=()
for channel in "${CHANNELS[@]}"; do
redistail pubsub "$channel" > "/var/log/redis-$channel.log" &
PIDS+=($!)
done
trap 'kill "${PIDS[@]}"; exit' INT TERM
waitTroubleshooting
Connection Refused
❌ Error: Connection failed to redis://localhost:6379- Verify Redis is running:
redis-cli ping - Check host/port:
REDIS_HOST=localhost REDIS_PORT=6379 redistail --help - Test directly:
redis-cli -h localhost -p 6379 ping
No Messages Appearing
- Publish a test message:
redis-cli publish test-channel "hello" - Add a stream entry:
redis-cli xadd test-stream * msg hello - Verify topic name matches exactly
- Check stream length:
redis-cli xlen stream-name
Authentication Failed
Include credentials in URL: REDIS_URL=redis://user:pass@host:port
Intermittent Disconnections
- Increase retries:
REDISTAIL_MAX_RECONNECT_ATTEMPTS=10 - Adjust delay:
REDIS_RETRY_DELAY=2000 - Check network stability and Redis server logs
Colors Not Displaying
- Set explicitly:
REDISTAIL_COLORS=true - Piping disables colors — use
REDISTAIL_COLORS=trueto force - Check terminal:
echo $TERM
Date Filter Errors
# Invalid format
$ redistail stream events --from "invalid"
❌ CLI Error: Invalid date format: "invalid". Supported formats: ...
# --from with pubsub
$ redistail pubsub channel --from "2026-01-09"
❌ CLI Error: The --from option is only available for stream mode
# Missing value
$ redistail stream events --from
❌ CLI Error: The --from option requires a date argumentDebugging
# Test Redis connectivity
redis-cli -h $REDIS_HOST -p $REDIS_PORT ping
# Monitor all Redis commands
redis-cli monitor
# Test pub/sub manually
redis-cli subscribe test-channel
# In another terminal:
redis-cli publish test-channel "hello"Peer Dependencies
| Package | Version |
| :--- | :--- |
| effect | (provided via workspace catalog) |
| effect-redis | workspace:* |
| @effect/experimental | (provided via workspace catalog) |
| @effect/platform-bun | (provided via workspace catalog) |
