<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged datastreaming</title>
<link>https://coderlegion.com/tag/datastreaming</link>
<description>Powered by Question2Answer</description>
<item>
<title>Kafka 101: Why Event Streaming is the Central Nervous System of Modern Data</title>
<link>https://coderlegion.com/15587/kafka-101-why-event-streaming-is-the-central-nervous-system-of-modern-data</link>
<description>&lt;p&gt;Digging into a new universe of data streaming after IBM’s recent accquisition of Confluent!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j0i0twnt306qpqbvp20g.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
Data is no longer a static asset — it’s a live pulse. Following IBM’s $11B acquisition of Confluent, the bridge between real-time streaming and enterprise AI has never been shorter. I’m diving into the new universes of data-in-motion to see how this ‘Smart Data’ foundation is changing the game for hybrid cloud and agentic AI. &lt;/p&gt;
&lt;p&gt;In the traditional world of data, systems often talk to each other like a game of “telephone” — one service calls another, which calls another, creating a fragile web of dependencies. If one system goes down or slows down, the entire chain breaks. &lt;strong&gt;Event streaming&lt;/strong&gt; changes this paradigm by turning data into a continuous flow of events.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apache Kafka&lt;/strong&gt; acts as the “central nervous system” for this flow. Instead of direct connections, services publish events to a central cluster, and any interested service can “tune in” to listen. This decoupling allows businesses to react to customer actions in real-time — whether that’s processing a payment, updating a live dashboard, or triggering a delivery — without the systems ever needing to know each other exists.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Implementation: Building an E-commerce Stream&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjuyodzq1ift41st5iuj.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Using two basic code samples I found on the net, one as a “producer” and one as a “consumer” to explain the concepts of Kafka, I asked Bob to build a simple comprehensive application.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple Producer&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from confluent_kafka import Producer
import json

# Configuration for connecting to the Kafka cluster
config = {'bootstrap.servers': 'localhost:9092'}
producer = Producer(config)

def delivery_report(err, msg):
    if err is not None:
        print(f&quot;Message delivery failed: {err}&quot;)
    else:
        print(f&quot;Order sent to {msg.topic()} [{msg.partition()}]&quot;)

# Simulate an order
order_data = {
    &quot;order_id&quot;: 1001,
    &quot;user&quot;: &quot;jane_doe&quot;,
    &quot;total&quot;: 59.99,
    &quot;items&quot;: [&quot;Wireless Mouse&quot;, &quot;Keyboard&quot;]
}

# Trigger the send (Asynchronous)
producer.produce(
    'orders', 
    key=&quot;1001&quot;, 
    value=json.dumps(order_data), 
    callback=delivery_report
)

producer.flush() # Wait for any outstanding messages to be delivered

&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Simple Consumer&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from confluent_kafka import Consumer

config = {
    'bootstrap.servers': 'localhost:9092',
    'group.id': 'shipping-group', # Helps Kafka track which orders this group has seen
    'auto.offset.reset': 'earliest'
}

consumer = Consumer(config)
consumer.subscribe(['orders'])

try:
    while True:
        msg = consumer.poll(1.0) # Check for new messages every 1 second
        if msg is None: continue
        if msg.error():
            print(f&quot;Consumer error: {msg.error()}&quot;)
            continue

        print(f&quot;Received Order: {msg.value().decode('utf-8')}&quot;)
        print(&quot;Action: Initiating packaging and shipping...&quot;)
finally:
    consumer.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reading my conceptual samples, Bob provided the application structure which follows.&lt;/p&gt;
&lt;h4 id=&quot;the-producer-capturing-the-event&quot;&gt;The Producer: Capturing the Event&lt;/h4&gt;
&lt;p&gt;The Producer (found in &lt;code&gt;src/producer.py&lt;/code&gt;) acts as the storefront. When an order is placed, it creates a JSON-serializable dictionary containing the order ID, user details, and items. It uses the &lt;code&gt;confluent-kafka&lt;/code&gt; library to &quot;produce&quot; this message to a topic called &lt;code&gt;orders&lt;/code&gt;, using the &lt;code&gt;order_id&lt;/code&gt; as a key to ensure all updates for a specific order land in the same partition. A delivery callback function is utilized to provide immediate feedback on whether the message successfully reached the Kafka broker or if a retry is necessary.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yexfofty4yn3i5zyc2zq.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;&quot;&quot;&quot;
Kafka Producer - E-commerce Order Processing System
Simulates an online store sending order events to Kafka
&quot;&quot;&quot;

from confluent_kafka import Producer
import json
import time
import random
from datetime import datetime

# Configuration for connecting to the Kafka cluster
config = {
    'bootstrap.servers': 'localhost:9092',
    'client.id': 'ecommerce-producer'
}

producer = Producer(config)

def delivery_report(err, msg):
    &quot;&quot;&quot;
    Callback function called once for each message produced to indicate delivery result.
    Triggered by poll() or flush().
    &quot;&quot;&quot;
    if err is not None:
        print(f&quot;❌ Message delivery failed: {err}&quot;)
    else:
        print(f&quot;✅ Order sent to topic '{msg.topic()}' [partition {msg.partition()}] at offset {msg.offset()}&quot;)

# Sample product catalog
PRODUCTS = [
    {&quot;name&quot;: &quot;Wireless Mouse&quot;, &quot;price&quot;: 29.99},
    {&quot;name&quot;: &quot;Mechanical Keyboard&quot;, &quot;price&quot;: 89.99},
    {&quot;name&quot;: &quot;USB-C Cable&quot;, &quot;price&quot;: 12.99},
    {&quot;name&quot;: &quot;Laptop Stand&quot;, &quot;price&quot;: 45.00},
    {&quot;name&quot;: &quot;Webcam HD&quot;, &quot;price&quot;: 79.99},
    {&quot;name&quot;: &quot;Headphones&quot;, &quot;price&quot;: 149.99},
    {&quot;name&quot;: &quot;Monitor 27\&quot;&quot;, &quot;price&quot;: 299.99},
    {&quot;name&quot;: &quot;Desk Lamp&quot;, &quot;price&quot;: 34.99}
]

USERS = [&quot;alice_smith&quot;, &quot;bob_jones&quot;, &quot;charlie_brown&quot;, &quot;diana_prince&quot;, &quot;eve_adams&quot;]

def generate_order(order_id):
    &quot;&quot;&quot;Generate a random order&quot;&quot;&quot;
    num_items = random.randint(1, 4)
    items = random.sample(PRODUCTS, num_items)
    
    order_data = {
        &quot;order_id&quot;: order_id,
        &quot;user&quot;: random.choice(USERS),
        &quot;timestamp&quot;: datetime.now().isoformat(),
        &quot;items&quot;: [item[&quot;name&quot;] for item in items],
        &quot;total&quot;: round(sum(item[&quot;price&quot;] for item in items), 2),
        &quot;status&quot;: &quot;pending&quot;
    }
    return order_data

def produce_orders(num_orders=10, delay=2):
    &quot;&quot;&quot;
    Produce multiple orders to Kafka
    
    Args:
        num_orders: Number of orders to generate
        delay: Delay in seconds between orders
    &quot;&quot;&quot;
    print(f&quot; Starting E-commerce Order Producer&quot;)
    print(f&quot; Will produce {num_orders} orders with {delay}s delay between each&quot;)
    print(f&quot; Connected to Kafka at: {config['bootstrap.servers']}&quot;)
    print(&quot;-&quot; * 70)
    
    for i in range(1, num_orders + 1):
        try:
            # Generate order
            order_data = generate_order(1000 + i)
            
            # Print order details
            print(f&quot;\n Order #{order_data['order_id']}:&quot;)
            print(f&quot;   User: {order_data['user']}&quot;)
            print(f&quot;   Items: {', '.join(order_data['items'])}&quot;)
            print(f&quot;   Total: ${order_data['total']}&quot;)
            
            # Produce message to Kafka
            producer.produce(
                topic='orders',
                key=str(order_data['order_id']),
                value=json.dumps(order_data),
                callback=delivery_report
            )
            
            # Trigger delivery reports by polling
            producer.poll(0)
            
            # Wait before next order
            if i &amp;lt; num_orders:
                time.sleep(delay)
                
        except KeyboardInterrupt:
            print(&quot;\n⚠️  Producer interrupted by user&quot;)
            break
        except Exception as e:
            print(f&quot;❌ Error producing message: {e}&quot;)
    
    # Wait for all messages to be delivered
    print(&quot;\n⏳ Flushing remaining messages...&quot;)
    producer.flush()
    print(&quot;✅ All orders sent successfully!&quot;)

if __name__ == &quot;__main__&quot;:
    import sys
    
    # Parse command line arguments
    num_orders = int(sys.argv[1]) if len(sys.argv) &amp;gt; 1 else 10
    delay = float(sys.argv[2]) if len(sys.argv) &amp;gt; 2 else 2
    
    try:
        produce_orders(num_orders, delay)
    except KeyboardInterrupt:
        print(&quot;\n Producer stopped&quot;)
    except Exception as e:
        print(f&quot;❌ Fatal error: {e}&quot;)

# Made with Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-consumer-decoupled-processing&quot;&gt;The Consumer: Decoupled Processing&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/er5ozyspeu54dffbwckn.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
The Consumer logic (found in &lt;code&gt;src/consumer.py&lt;/code&gt;) demonstrates the power of Consumer Groups. Multiple independent services—Shipping, Email, and Analytics—all subscribe to the same orders topic simultaneously. Because each service belongs to its own group (e.g., &lt;code&gt;shipping-group&lt;/code&gt;), Kafka tracks their progress (offsets) individually. This means the Shipping department can process messages at its own pace without affecting the speed of the Email service or the Analytics dashboard.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;&quot;&quot;&quot;
Kafka Consumer - E-commerce Order Processing System
Simulates different departments (Shipping, Email, Analytics) consuming order events
&quot;&quot;&quot;

from confluent_kafka import Consumer, KafkaError
import json
import sys
from datetime import datetime

def create_consumer(group_id, bootstrap_servers='localhost:9092'):
    &quot;&quot;&quot;
    Create and configure a Kafka consumer
    
    Args:
        group_id: Consumer group identifier
        bootstrap_servers: Kafka broker addresses
    
    Returns:
        Configured Consumer instance
    &quot;&quot;&quot;
    config = {
        'bootstrap.servers': bootstrap_servers,
        'group.id': group_id,
        'auto.offset.reset': 'earliest',  # Start from beginning if no offset exists
        'enable.auto.commit': True,
        'auto.commit.interval.ms': 1000,
        'session.timeout.ms': 6000,
        'client.id': f'{group_id}-client'
    }
    
    return Consumer(config)

def process_shipping(order):
    &quot;&quot;&quot;Process order for shipping department&quot;&quot;&quot;
    print(f&quot;\n SHIPPING DEPARTMENT&quot;)
    print(f&quot;   Order ID: {order['order_id']}&quot;)
    print(f&quot;   Customer: {order['user']}&quot;)
    print(f&quot;   Items to pack: {', '.join(order['items'])}&quot;)
    print(f&quot;   ✅ Initiating packaging and shipping process...&quot;)

def process_email(order):
    &quot;&quot;&quot;Process order for email service&quot;&quot;&quot;
    print(f&quot;\n EMAIL SERVICE&quot;)
    print(f&quot;   Order ID: {order['order_id']}&quot;)
    print(f&quot;   Recipient: {order['user']}&quot;)
    print(f&quot;   Total: ${order['total']}&quot;)
    print(f&quot;   ✅ Sending order confirmation email...&quot;)

def process_analytics(order):
    &quot;&quot;&quot;Process order for analytics dashboard&quot;&quot;&quot;
    print(f&quot;\n ANALYTICS DASHBOARD&quot;)
    print(f&quot;   Order ID: {order['order_id']}&quot;)
    print(f&quot;   Revenue: ${order['total']}&quot;)
    print(f&quot;   Items count: {len(order['items'])}&quot;)
    print(f&quot;   Timestamp: {order['timestamp']}&quot;)
    print(f&quot;   ✅ Updating live sales dashboard...&quot;)

# Department processors mapping
PROCESSORS = {
    'shipping-group': process_shipping,
    'email-group': process_email,
    'analytics-group': process_analytics
}

def consume_orders(group_id, topic='orders'):
    &quot;&quot;&quot;
    Consume orders from Kafka topic
    
    Args:
        group_id: Consumer group (shipping-group, email-group, analytics-group)
        topic: Kafka topic to subscribe to
    &quot;&quot;&quot;
    consumer = create_consumer(group_id)
    consumer.subscribe([topic])
    
    processor = PROCESSORS.get(group_id, process_shipping)
    department = group_id.replace('-group', '').upper()
    
    print(f&quot; Starting {department} Consumer&quot;)
    print(f&quot; Consumer Group: {group_id}&quot;)
    print(f&quot; Subscribed to topic: {topic}&quot;)
    print(f&quot; Connected to Kafka at: localhost:9092&quot;)
    print(&quot;-&quot; * 70)
    print(&quot;⏳ Waiting for messages... (Press Ctrl+C to stop)\n&quot;)
    
    try:
        message_count = 0
        while True:
            # Poll for messages (timeout in seconds)
            msg = consumer.poll(timeout=1.0)
            
            if msg is None:
                continue
            
            if msg.error():
                if msg.error().code() == KafkaError._PARTITION_EOF:
                    # End of partition event
                    print(f&quot; Reached end of partition {msg.partition()}&quot;)
                else:
                    print(f&quot;❌ Consumer error: {msg.error()}&quot;)
                continue
            
            # Process the message
            try:
                message_count += 1
                order_data = json.loads(msg.value().decode('utf-8'))
                
                print(f&quot;\n{'='*70}&quot;)
                print(f&quot; Message #{message_count} received from partition {msg.partition()} at offset {msg.offset()}&quot;)
                
                # Call the appropriate processor
                processor(order_data)
                
                print(f&quot;{'='*70}&quot;)
                
            except json.JSONDecodeError as e:
                print(f&quot;❌ Failed to decode message: {e}&quot;)
            except Exception as e:
                print(f&quot;❌ Error processing message: {e}&quot;)
    
    except KeyboardInterrupt:
        print(f&quot;\n\n⚠️  Consumer interrupted by user&quot;)
        print(f&quot; Total messages processed: {message_count}&quot;)
    
    finally:
        # Close the consumer to commit final offsets
        print(&quot; Closing consumer...&quot;)
        consumer.close()
        print(&quot;✅ Consumer closed successfully&quot;)

if __name__ == &quot;__main__&quot;:
    # Parse command line arguments
    if len(sys.argv) &amp;lt; 2:
        print(&quot;Usage: python consumer.py &amp;lt;group_id&amp;gt; [topic]&quot;)
        print(&quot;\nAvailable consumer groups:&quot;)
        print(&quot;  - shipping-group   : Processes orders for shipping&quot;)
        print(&quot;  - email-group      : Sends confirmation emails&quot;)
        print(&quot;  - analytics-group  : Updates analytics dashboard&quot;)
        print(&quot;\nExample: python consumer.py shipping-group&quot;)
        sys.exit(1)
    
    group_id = sys.argv[1]
    topic = sys.argv[2] if len(sys.argv) &amp;gt; 2 else 'orders'
    
    if group_id not in PROCESSORS:
        print(f&quot;⚠️  Warning: Unknown group_id '{group_id}'. Using default processor.&quot;)
    
    try:
        consume_orders(group_id, topic)
    except Exception as e:
        print(f&quot;❌ Fatal error: {e}&quot;)
        sys.exit(1)

# Made with Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-cluster-ensuring-reliability&quot;&gt;The Cluster: Ensuring Reliability&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxxi1qwyj7mhx3oezanl.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;The heart of the system is the &lt;strong&gt;Broker Kafka Cluster&lt;/strong&gt; (defined in &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;[Architecture.md](https://github.com/aairom/kafka-101/blob/main/Docs/Architecture.md)&lt;/code&gt;). By running three brokers coordinated by Zookeeper, the architecture ensures &lt;strong&gt;High Availability&lt;/strong&gt;. Each data partition is replicated across at least two brokers; if one broker fails, another automatically takes over as the &quot;leader,&quot; ensuring that no orders are lost and the system remains online. This fault tolerance is what makes Kafka &quot;production-ready&quot; compared to simple message queues.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;version: '3.8'

services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.5.0
    hostname: zookeeper
    container_name: zookeeper
    ports:
      - &quot;2181:2181&quot;
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    volumes:
      - zookeeper-data:/var/lib/zookeeper/data
      - zookeeper-logs:/var/lib/zookeeper/log

  kafka-broker-1:
    image: confluentinc/cp-kafka:7.5.0
    hostname: kafka-broker-1
    container_name: kafka-broker-1
    depends_on:
      - zookeeper
    ports:
      - &quot;9092:9092&quot;
      - &quot;19092:19092&quot;
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker-1:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_JMX_PORT: 9101
      KAFKA_JMX_HOSTNAME: localhost
    volumes:
      - kafka-broker-1-data:/var/lib/kafka/data

  kafka-broker-2:
    image: confluentinc/cp-kafka:7.5.0
    hostname: kafka-broker-2
    container_name: kafka-broker-2
    depends_on:
      - zookeeper
    ports:
      - &quot;9093:9093&quot;
      - &quot;19093:19093&quot;
    environment:
      KAFKA_BROKER_ID: 2
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker-2:29093,PLAINTEXT_HOST://localhost:9093
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_JMX_PORT: 9102
      KAFKA_JMX_HOSTNAME: localhost
    volumes:
      - kafka-broker-2-data:/var/lib/kafka/data

  kafka-broker-3:
    image: confluentinc/cp-kafka:7.5.0
    hostname: kafka-broker-3
    container_name: kafka-broker-3
    depends_on:
      - zookeeper
    ports:
      - &quot;9094:9094&quot;
      - &quot;19094:19094&quot;
    environment:
      KAFKA_BROKER_ID: 3
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker-3:29094,PLAINTEXT_HOST://localhost:9094
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_JMX_PORT: 9103
      KAFKA_JMX_HOSTNAME: localhost
    volumes:
      - kafka-broker-3-data:/var/lib/kafka/data

volumes:
  zookeeper-data:
  zookeeper-logs:
  kafka-broker-1-data:
  kafka-broker-2-data:
  kafka-broker-3-data:

# Made with Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-monitor-real-time-observability&quot;&gt;The Monitor: Real-Time Observability&lt;/h4&gt;
&lt;p&gt;To manage this distributed flow, the Monitor Runner (&lt;code&gt;monitor_runner.py&lt;/code&gt;) provides a unified view of the entire ecosystem. It orchestrates the lifecycle of the producer and all consumer threads while capturing their standard output into a web-based dashboard. This allows developers to see the &quot;path of a message&quot; in real-time: from the moment the Producer sends Order #1001 to the moment all three consumer groups acknowledge and process it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;&quot;&quot;&quot;
Kafka Monitor Web Server
Provides a web interface to monitor producer and consumer outputs in real-time
&quot;&quot;&quot;

import asyncio
import json
import subprocess
import threading
from datetime import datetime
from pathlib import Path
from http.server import HTTPServer, SimpleHTTPRequestHandler
import socketserver

class TerminalMonitor:
    &quot;&quot;&quot;Monitor terminal outputs and store them&quot;&quot;&quot;
    
    def __init__(self):
        self.outputs = {
            'producer': [],
            'shipping': [],
            'email': [],
            'analytics': []
        }
        self.max_lines = 1000  # Keep last 1000 lines per terminal
        
    def add_output(self, terminal_id, line):
        &quot;&quot;&quot;Add a line to terminal output&quot;&quot;&quot;
        timestamp = datetime.now().strftime('%H:%M:%S')
        entry = {
            'timestamp': timestamp,
            'line': line
        }
        
        if terminal_id in self.outputs:
            self.outputs[terminal_id].append(entry)
            # Keep only last max_lines
            if len(self.outputs[terminal_id]) &amp;gt; self.max_lines:
                self.outputs[terminal_id] = self.outputs[terminal_id][-self.max_lines:]
    
    def get_outputs(self):
        &quot;&quot;&quot;Get all terminal outputs&quot;&quot;&quot;
        return self.outputs
    
    def clear_terminal(self, terminal_id):
        &quot;&quot;&quot;Clear a specific terminal&quot;&quot;&quot;
        if terminal_id in self.outputs:
            self.outputs[terminal_id] = []
    
    def clear_all(self):
        &quot;&quot;&quot;Clear all terminals&quot;&quot;&quot;
        for terminal_id in self.outputs:
            self.outputs[terminal_id] = []

# Global monitor instance
monitor = TerminalMonitor()

class MonitorRequestHandler(SimpleHTTPRequestHandler):
    &quot;&quot;&quot;Custom HTTP request handler&quot;&quot;&quot;
    
    def do_GET(self):
        &quot;&quot;&quot;Handle GET requests&quot;&quot;&quot;
        if self.path == '/':
            self.path = '/monitor.html'
        elif self.path == '/api/outputs':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            self.wfile.write(json.dumps(monitor.get
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15587/kafka-101-why-event-streaming-is-the-central-nervous-system-of-modern-data</guid>
<pubDate>Thu, 23 Apr 2026 05:55:23 +0000</pubDate>
</item>
</channel>
</rss>