Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions tool-sidecar/src/unstract/tool_sidecar/log_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import json
import logging
import os
import signal
import time
from datetime import UTC, datetime
from typing import Any
Expand All @@ -25,6 +26,17 @@

logger = logging.getLogger(__name__)

# Global shutdown flag for graceful termination
_shutdown_requested = False


def signal_handler(signum, frame):
"""Handle shutdown signals gracefully."""
global _shutdown_requested
signal_name = signal.Signals(signum).name
logger.info(f"Received {signal_name}, initiating graceful shutdown...")
_shutdown_requested = True

Comment on lines +33 to +39
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix unused frame arg in signal handler (lint) and keep handler minimal.

Rename the unused parameter to underscore to satisfy Ruff ARG001 and keep the handler lean.

Apply this diff:

-def signal_handler(signum, frame):
+def signal_handler(signum, _frame):
     """Handle shutdown signals gracefully."""
     global _shutdown_requested
     signal_name = signal.Signals(signum).name
     logger.info(f"Received {signal_name}, initiating graceful shutdown...")
     _shutdown_requested = True

Optional: consider replacing the module‑level bool with a threading.Event for clearer intent and to avoid globals.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def signal_handler(signum, frame):
"""Handle shutdown signals gracefully."""
global _shutdown_requested
signal_name = signal.Signals(signum).name
logger.info(f"Received {signal_name}, initiating graceful shutdown...")
_shutdown_requested = True
def signal_handler(signum, _frame):
"""Handle shutdown signals gracefully."""
global _shutdown_requested
signal_name = signal.Signals(signum).name
logger.info(f"Received {signal_name}, initiating graceful shutdown...")
_shutdown_requested = True
🧰 Tools
🪛 Ruff (0.13.1)

33-33: Unused function argument: frame

(ARG001)

🤖 Prompt for AI Agents
In tool-sidecar/src/unstract/tool_sidecar/log_processor.py around lines 33 to
39, the signal_handler currently accepts an unused parameter named frame which
triggers lint ARG001; rename the unused parameter to _frame (or just _ ) to
satisfy Ruff and keep the handler minimal by leaving only the logging and
setting of the shutdown flag (remove any extra comments or logic inside the
handler). Optionally, consider replacing the module-level boolean
_shutdown_requested with a threading.Event to avoid globals and make shutdown
signaling clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kirtimanmishrazipstack can you address this comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!


class LogProcessor:
def __init__(
Expand Down Expand Up @@ -210,14 +222,22 @@ def is_valid_log_type(self, log_type: str | None) -> bool:
def monitor_logs(self) -> None:
"""Main loop to monitor log file for new content and completion signals.
Uses file polling with position tracking to efficiently read new lines.
Handles graceful shutdown via SIGTERM.
"""
global _shutdown_requested
logger.info("Starting log monitoring...")
if not self.wait_for_log_file():
raise TimeoutError("Log file was not created within timeout period")

# Monitor the file for new content
with open(self.log_path) as f:
while True:
# Check for shutdown signal
if _shutdown_requested:
logger.info("Shutdown requested, performing final log collection...")
self._final_log_collection(f)
break

# Remember current position
where = f.tell()
line = f.readline()
Expand All @@ -241,11 +261,43 @@ def monitor_logs(self) -> None:
logger.info("Completion signal received")
break

logger.info("Log monitoring completed")

def _final_log_collection(self, file_handle) -> None:
"""Perform final collection of any remaining logs before shutdown."""
logger.info("Performing final log collection...")

# Give main container brief moment to write final logs
time.sleep(0.2)

lines_collected = 0

# Read any remaining lines in the file
while True:
line = file_handle.readline()
if not line:
break

lines_collected += 1
log_line = self.process_log_line(line.strip())

if log_line.is_terminated:
logger.info("Found completion signal during final collection")

logger.info(
f"Final log collection completed, processed {lines_collected} remaining lines"
)


def main():
"""Main entry point for the sidecar container.
Sets up the log processor with environment variables and starts monitoring.
"""
# Set up signal handlers for graceful shutdown
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
logger.info("Signal handlers registered for graceful shutdown")

# Get configuration from environment
log_path = os.getenv(Env.LOG_PATH, "/shared/logs/logs.txt")
redis_host = os.getenv(Env.REDIS_HOST)
Expand Down