Skip to main content

Endpoint

ws://your-server:5000/logs/ws
Establishes a WebSocket connection for real-time streaming of webhook log entries as they are generated. Supports filtering to receive only relevant log entries.
Security Warning: This endpoint is NOT protected by authentication. Never expose it outside your trusted network. Deploy behind a reverse proxy with authentication, use firewall rules to restrict access, and never expose WebSocket ports directly to the internet. Log streams may contain GitHub tokens, user information, repository details, and sensitive webhook payloads.

Connection Parameters

All parameters are optional query parameters in the WebSocket URL. Use them to filter the log stream.
hook_id
string
Filter stream by specific GitHub delivery IDExample: ws://localhost:5000/logs/ws?hook_id=abc123-def456
pr_number
integer
Filter stream by pull request numberExample: ws://localhost:5000/logs/ws?pr_number=123
repository
string
Filter stream by repository name in owner/repo formatExample: ws://localhost:5000/logs/ws?repository=my-org/my-repo
event_type
string
Filter stream by GitHub event typeExample: ws://localhost:5000/logs/ws?event_type=pull_request
github_user
string
Filter stream by GitHub usernameExample: ws://localhost:5000/logs/ws?github_user=octocat
level
string
Filter stream by log level: DEBUG, INFO, WARNING, ERRORExample: ws://localhost:5000/logs/ws?level=ERROR
If no filters are provided, the WebSocket will stream all log entries in real-time.

Message Format

Each WebSocket message is a JSON object representing a single log entry:
timestamp
string
ISO 8601 timestamp (e.g., 2025-01-30T10:30:00.123000)
level
string
Log level: DEBUG, INFO, WARNING, or ERROR
logger_name
string
Logger component name (e.g., GithubWebhook, PullRequestHandler)
message
string
Log message text
hook_id
string
GitHub delivery ID (x-github-delivery)
event_type
string
GitHub event type (e.g., pull_request, check_run)
repository
string
Repository name in owner/repo format
pr_number
integer
Pull request number
github_user
string
GitHub username (api_user)

Examples

JavaScript/Browser

// Connect to WebSocket
const ws = new WebSocket("ws://localhost:5000/logs/ws?level=ERROR");

// Handle connection open
ws.onopen = function(event) {
  console.log("WebSocket connected");
};

// Handle incoming messages
ws.onmessage = function(event) {
  const logEntry = JSON.parse(event.data);
  console.log(`[${logEntry.level}] ${logEntry.message}`);
  
  // Display in UI
  const logElement = document.createElement('div');
  logElement.className = `log-entry log-${logEntry.level.toLowerCase()}`;
  logElement.textContent = `${logEntry.timestamp} - ${logEntry.message}`;
  document.getElementById('log-container').appendChild(logElement);
};

// Handle errors
ws.onerror = function(error) {
  console.error("WebSocket error:", error);
};

// Handle connection close
ws.onclose = function(event) {
  console.log("WebSocket disconnected", event.code, event.reason);
  
  // Reconnect after 5 seconds
  setTimeout(() => {
    console.log("Reconnecting...");
    connectWebSocket();
  }, 5000);
};

Python (websockets library)

import asyncio
import json
import websockets

async def stream_logs():
    uri = "ws://localhost:5000/logs/ws?repository=my-org/my-repo"
    
    async with websockets.connect(uri) as websocket:
        print("Connected to log stream")
        
        try:
            async for message in websocket:
                log_entry = json.loads(message)
                print(f"[{log_entry['level']}] {log_entry['message']}")
        except websockets.exceptions.ConnectionClosed:
            print("Connection closed")

asyncio.run(stream_logs())

Node.js (ws library)

const WebSocket = require('ws');

const ws = new WebSocket('ws://localhost:5000/logs/ws?pr_number=123');

ws.on('open', function open() {
  console.log('Connected to log stream');
});

ws.on('message', function message(data) {
  const logEntry = JSON.parse(data);
  console.log(`[${logEntry.timestamp}] [${logEntry.level}] ${logEntry.message}`);
});

ws.on('error', function error(err) {
  console.error('WebSocket error:', err);
});

ws.on('close', function close(code, reason) {
  console.log(`Connection closed: ${code} - ${reason}`);
});

Filter by Multiple Parameters

const params = new URLSearchParams({
  repository: 'my-org/my-repo',
  level: 'ERROR',
  event_type: 'pull_request'
});

const ws = new WebSocket(`ws://localhost:5000/logs/ws?${params}`);

Connection Lifecycle

Connection Establishment

  1. Client initiates WebSocket handshake
  2. Server accepts connection and adds to active connections set
  3. Server starts monitoring log directory for new entries
  4. New log entries are filtered and sent to client in real-time

Message Flow

Disconnection

Connections are closed when:
  • Client explicitly closes the connection
  • Server shuts down (sends close code 1001 with reason “Server shutdown”)
  • Network error or timeout occurs
  • Internal server error (sends close code 1011 with reason “Internal server error”)

Error Handling

Server Errors

If the log directory is not found, the server sends an error message:
{
  "error": "Log directory not found"
}

Connection Errors

  • Code 1001: Server shutdown (normal closure)
  • Code 1011: Internal server error
  • Code 1006: Abnormal closure (network issue)

Use Cases

Real-Time Monitoring Dashboard

// Monitor all errors in real-time
const errorStream = new WebSocket('ws://localhost:5000/logs/ws?level=ERROR');

errorStream.onmessage = function(event) {
  const error = JSON.parse(event.data);
  
  // Update dashboard metrics
  incrementErrorCount();
  displayErrorNotification(error);
  
  // Log to analytics
  trackError({
    timestamp: error.timestamp,
    repository: error.repository,
    message: error.message
  });
};

PR-Specific Monitoring

// Watch logs for specific PR
function monitorPR(prNumber) {
  const ws = new WebSocket(`ws://localhost:5000/logs/ws?pr_number=${prNumber}`);
  
  ws.onmessage = function(event) {
    const log = JSON.parse(event.data);
    updatePRStatusUI(prNumber, log);
  };
  
  return ws;
}

const pr123Stream = monitorPR(123);

Repository Activity Feed

// Live activity feed for repository
const repoFeed = new WebSocket('ws://localhost:5000/logs/ws?repository=my-org/my-repo');

repoFeed.onmessage = function(event) {
  const activity = JSON.parse(event.data);
  addToActivityFeed({
    time: new Date(activity.timestamp),
    type: activity.event_type,
    message: activity.message,
    user: activity.github_user
  });
};

Multi-Filter Monitoring

// Monitor errors for specific repository
const params = new URLSearchParams({
  repository: 'my-org/critical-repo',
  level: 'ERROR'
});

const criticalErrors = new WebSocket(`ws://localhost:5000/logs/ws?${params}`);

criticalErrors.onmessage = function(event) {
  const error = JSON.parse(event.data);
  sendPagerDutyAlert(error);
};

Connection Troubleshooting

WebSocket Connection Fails

Check firewall rules:
# Allow WebSocket port
sudo ufw allow 5000/tcp
Verify server is running:
curl http://localhost:5000/health
Check reverse proxy configuration (if using nginx):
location /logs/ws {
    proxy_pass http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 86400;
}

No Messages Received

Verify filters are correct:
// Check filter matches actual log data
const ws = new WebSocket('ws://localhost:5000/logs/ws?repository=correct-org/correct-repo');
Test without filters:
// Should receive all logs
const ws = new WebSocket('ws://localhost:5000/logs/ws');
Check log files exist:
ls -la /path/to/data/logs/

Connection Drops Frequently

Implement reconnection logic:
function connectWebSocket() {
  const ws = new WebSocket('ws://localhost:5000/logs/ws');
  
  ws.onclose = function(event) {
    console.log('Connection closed, reconnecting in 5s...');
    setTimeout(connectWebSocket, 5000);
  };
  
  return ws;
}

let ws = connectWebSocket();
Check network stability:
ping -c 100 your-server-hostname
Increase proxy timeout (if using reverse proxy):
proxy_read_timeout 3600s;  # 1 hour

High Memory Usage

Reduce filter scope to minimize message volume:
// Instead of monitoring all logs
const ws = new WebSocket('ws://localhost:5000/logs/ws');

// Monitor only specific repository and level
const ws = new WebSocket('ws://localhost:5000/logs/ws?repository=my-org/my-repo&level=ERROR');

Performance Considerations

  • Memory efficient: Server uses streaming architecture
  • Concurrent connections: Server maintains set of active WebSocket connections
  • Filter early: Filters applied before sending to reduce bandwidth
  • Automatic cleanup: Connections removed from set on disconnect
  • Latency: Less than 100ms from log generation to client delivery

Security Best Practices

  1. Never expose WebSocket publicly - use VPN or private network only
  2. Deploy behind authenticated reverse proxy (mandatory for production)
  3. Use WSS (WebSocket Secure) over TLS in production
  4. Implement client-side token authentication for additional security
  5. Monitor WebSocket connections and limit concurrent connections
  6. Log access to WebSocket endpoint for audit trails

Example Secure Configuration (nginx)

server {
    listen 443 ssl;
    server_name logs.internal.company.com;
    
    # TLS configuration
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # Basic authentication
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    location /logs/ws {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;
        
        # Additional security headers
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Build docs developers (and LLMs) love