log_streamer.py 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. import threading
  2. from typing import Callable
  3. import docker
  4. class LogStreamer:
  5. """Streams Docker container logs to stdout.
  6. This class provides a way to stream logs from a Docker container directly to stdout
  7. through the provided logging function.
  8. """
  9. def __init__(
  10. self,
  11. container: docker.models.containers.Container,
  12. logFn: Callable,
  13. ):
  14. self.log = logFn
  15. self.log_generator = container.logs(stream=True, follow=True)
  16. self._stop_event = threading.Event()
  17. # Start the stdout streaming thread
  18. self.stdout_thread = threading.Thread(target=self._stream_logs)
  19. self.stdout_thread.daemon = True
  20. self.stdout_thread.start()
  21. def _stream_logs(self):
  22. """Stream logs from the Docker container to stdout."""
  23. try:
  24. for log_line in self.log_generator:
  25. if self._stop_event.is_set():
  26. break
  27. if log_line:
  28. decoded_line = log_line.decode('utf-8').rstrip()
  29. self.log('debug', f'[inside container] {decoded_line}')
  30. except Exception as e:
  31. self.log('error', f'Error streaming docker logs to stdout: {e}')
  32. def __del__(self):
  33. if self.stdout_thread and self.stdout_thread.is_alive():
  34. self.close(timeout=5)
  35. def close(self, timeout: float = 5.0):
  36. """Clean shutdown of the log streaming."""
  37. self._stop_event.set()
  38. if self.stdout_thread and self.stdout_thread.is_alive():
  39. self.stdout_thread.join(timeout)
  40. # Close the log generator to release the file descriptor
  41. if hasattr(self.log_generator, 'close'):
  42. self.log_generator.close()