A simple but powerful log streaming service for Platform-as-a-Service (PaaS) deployments. Busl provides real-time log streaming from applications to web interfaces via HTTP/2.
Busl Log Streaming Architecture
github.com/aluminumio/busl busl server (pubsub service) busltee package (library) busltee command (CLI tool) /streams/ HTTP/2 transport HTTP API imports uses Your Application log-stream (wrapper) imports Data Flow Command Web Viewers stdout/stderr HTTP POST streams toA pubsub (publish/subscribe) service that:
- Creates and manages log streams
- Accepts log data via HTTP POST
- Streams data to multiple subscribers via Server-Sent Events (SSE)
- Supports HTTP/2 for efficient streaming
A Go library and CLI tool that:
- Captures stdout/stderr from processes
- Streams output to busl server endpoints
- Handles retries and connection management
- Works like Unix
teebut for network streaming
A lightweight wrapper that:
- Parses environment variables for stream configuration
- Executes commands while streaming their output
- Used in PaaS deployments for build/release logging
# Clone and build
git clone https://github.com/aluminumio/busl
cd busl
go build ./cmd/busl
# Run the server
export PORT=5001
export STORAGE_URL="redis://localhost:6379" # Optional: for persistence
./busl# Create a stream
export STREAM_ID=$(uuidgen)
curl -X PUT http://localhost:5001/streams/$STREAM_ID
# Stream a command's output
./busltee http://localhost:5001/streams/$STREAM_ID -- your-command arg1 arg2
# Or use the log-stream wrapper with environment variables
export ALUMINUMIO_STREAM_URL_MYAPP=http://localhost:5001/streams/$STREAM_ID
./log-stream -- your-command arg1 arg2Create a simple HTML file to view streams:
<!DOCTYPE html>
<html>
<head>
<title>Log Viewer</title>
<style>
body { font-family: monospace; background: #1e1e1e; color: #d4d4d4; }
#logs { white-space: pre-wrap; padding: 20px; }
.error { color: #f48771; }
.info { color: #4ec9b0; }
</style>
</head>
<body>
<h1>Live Log Stream</h1>
<div id="logs"></div>
<script>
const streamId = new URLSearchParams(window.location.search).get('stream');
const logs = document.getElementById('logs');
if (streamId) {
const evtSource = new EventSource(`http://localhost:5001/streams/${streamId}`);
evtSource.onmessage = function(e) {
const line = document.createElement('div');
line.textContent = e.data;
if (e.data.includes('ERROR')) line.className = 'error';
if (e.data.includes('INFO')) line.className = 'info';
logs.appendChild(line);
window.scrollTo(0, document.body.scrollHeight);
};
evtSource.onerror = function(e) {
logs.appendChild(document.createTextNode('\n[Connection Error]\n'));
};
} else {
logs.textContent = 'Please provide ?stream=STREAM_ID in the URL';
}
</script>
</body>
</html>Save as viewer.html and open with: file:///path/to/viewer.html?stream=YOUR_STREAM_ID
Environment variables for the busl server:
# Required
PORT=5001 # HTTP port to listen on
# Optional
STORAGE_URL=redis://localhost:6379 # Redis URL for persistence
CREDENTIALS=username:password # Basic auth for stream creation
REQUEST_TIMEOUT=300 # Request timeout in seconds
STREAM_TIMEOUT=86400 # Stream expiry in secondsFor production use with systemd:
[Unit]
Description=Busl Log Streaming Server
After=network.target
[Service]
Type=simple
User=busl
WorkingDirectory=/opt/busl
ExecStart=/opt/busl/busl
Restart=always
Environment="PORT=5001"
Environment="STORAGE_URL=redis://localhost:6379"
Environment="CREDENTIALS=admin:secretpassword"
[Install]
WantedBy=multi-user.targetThe log-stream wrapper is designed for PaaS environments:
# In your buildpack or deployment script
export ALUMINUMIO_STREAM_URL_BUILD=https://logs.yourpaas.com/streams/$BUILD_ID
./log-stream -- make build
# For application logs
export ALUMINUMIO_STREAM_URL_APP=https://logs.yourpaas.com/streams/$APP_ID
./log-stream -- ./start-app.sh- Use HTTPS in production
- Set
CREDENTIALSfor basic auth on stream creation - Use random UUIDs for stream IDs
- Implement rate limiting via reverse proxy
- Set appropriate
STREAM_TIMEOUTvalues
PUT /streams/{stream-id}
Authorization: Basic base64(username:password) # If CREDENTIALS is set
POST /streams/{stream-id}
Content-Type: text/plain
Your log data here...
GET /streams/{stream-id}
Accept: text/event-stream
# Or with range for resuming
GET /streams/{stream-id}
Range: bytes=1024-
# Build all components
make all
# Build individual components
make busl # Server
make busltee # CLI tool
# Run tests
make test- Scalable: HTTP/2 support for efficient multiplexing
- Reliable: Built-in retry logic and connection management
- Simple: Clean HTTP API, works with any language
- Flexible: Can be used standalone or integrated into larger systems
- Compatible: Works with standard tools (curl, EventSource, etc.)
MIT License - see LICENSE file for details