diff --git a/cli/planoai/main.py b/cli/planoai/main.py index 4d72a12e..a63f294e 100644 --- a/cli/planoai/main.py +++ b/cli/planoai/main.py @@ -483,9 +483,21 @@ def generate_prompt_targets(file): is_flag=True, ) @click.option("--follow", help="Follow the logs", is_flag=True) -def logs(debug, follow): +@click.option( + "--docker", + default=False, + help="Stream logs from a Docker-based Plano instance.", + is_flag=True, +) +def logs(debug, follow, docker): """Stream logs from access logs services.""" + if not docker: + from planoai.native_runner import native_logs + + native_logs(debug=debug, follow=follow) + return + plano_process = None try: if debug: diff --git a/cli/planoai/native_runner.py b/cli/planoai/native_runner.py index c5ccd6e7..0e39a1fd 100644 --- a/cli/planoai/native_runner.py +++ b/cli/planoai/native_runner.py @@ -423,3 +423,41 @@ def native_validate_config(plano_config_file): # Suppress verbose print output from config_generator with contextlib.redirect_stdout(io.StringIO()): validate_and_render_schema() + + +def native_logs(debug=False, follow=False): + """Stream logs from native-mode Plano.""" + import glob as glob_mod + + log_dir = os.path.join(PLANO_RUN_DIR, "logs") + if not os.path.isdir(log_dir): + log.error(f"No native log directory found at {log_dir}") + log.error("Is Plano running? Start it with: planoai up ") + sys.exit(1) + + log_files = sorted(glob_mod.glob(os.path.join(log_dir, "access_*.log"))) + if debug: + log_files.extend( + [ + os.path.join(log_dir, "envoy.log"), + os.path.join(log_dir, "brightstaff.log"), + ] + ) + + # Filter to files that exist + log_files = [f for f in log_files if os.path.exists(f)] + if not log_files: + log.error(f"No log files found in {log_dir}") + sys.exit(1) + + tail_args = ["tail"] + if follow: + tail_args.append("-f") + tail_args.extend(log_files) + + try: + proc = subprocess.Popen(tail_args, stdout=sys.stdout, stderr=sys.stderr) + proc.wait() + except KeyboardInterrupt: + if proc.poll() is None: + proc.terminate()