22//!
33//! Streams container logs with optional filtering, timestamps, and follow mode.
44
5- use crate :: cli_platform:: is_dev_binary;
65use crate :: output:: { format_docker_error_anyhow, log_level_style} ;
76use anyhow:: { Result , anyhow} ;
87use clap:: Args ;
@@ -149,13 +148,24 @@ async fn stream_broker_logs(
149148 line_prefix : Option < & str > ,
150149 quiet : bool ,
151150) -> Result < ( ) > {
152- ensure_systemd_available ( client) . await ?;
153- let cmd = build_broker_journalctl_command ( args) ?;
154- let exec_id = create_broker_exec ( client, cmd) . await ?;
155- stream_broker_exec_output ( args, client, & exec_id, line_prefix, quiet) . await
151+ if ensure_systemd_available ( client) . await ? {
152+ let cmd = build_broker_journalctl_command ( args) ?;
153+ let exec_id = create_broker_exec ( client, cmd) . await ?;
154+ stream_broker_exec_output ( args, client, & exec_id, line_prefix, quiet) . await
155+ } else {
156+ if !quiet {
157+ eprintln ! (
158+ "{}" ,
159+ style( "Systemd/journald not available. Falling back to container logs (filtered)." )
160+ . yellow( )
161+ ) ;
162+ eprintln ! ( ) ;
163+ }
164+ stream_broker_logs_from_container ( args, client, line_prefix, quiet) . await
165+ }
156166}
157167
158- async fn ensure_systemd_available ( client : & DockerClient ) -> Result < ( ) > {
168+ async fn ensure_systemd_available ( client : & DockerClient ) -> Result < bool > {
159169 let systemd_available = exec_command_exit_code (
160170 client,
161171 CONTAINER_NAME ,
@@ -165,28 +175,7 @@ async fn ensure_systemd_available(client: &DockerClient) -> Result<()> {
165175 . unwrap_or ( 1 )
166176 == 0 ;
167177
168- if systemd_available {
169- Ok ( ( ) )
170- } else {
171- let remove_cmd = if is_dev_binary ( ) {
172- "just run stop --remove"
173- } else {
174- "occ stop --remove"
175- } ;
176- let start_cmd = if is_dev_binary ( ) {
177- "just run start"
178- } else {
179- "occ start"
180- } ;
181-
182- Err ( anyhow ! (
183- "Broker logs require systemd/journald inside the container.\n \
184- This host doesn't support systemd-in-container or the container was created in Tini mode.\n \
185- Recreate the container on a supported Linux host with:\n {}\n {}",
186- style( remove_cmd) . cyan( ) ,
187- style( start_cmd) . cyan( )
188- ) )
189- }
178+ Ok ( systemd_available)
190179}
191180
192181fn build_broker_journalctl_command ( args : & LogsArgs ) -> Result < Vec < String > > {
@@ -296,6 +285,64 @@ async fn stream_broker_exec_output(
296285 Ok ( ( ) )
297286}
298287
288+ async fn stream_broker_logs_from_container (
289+ args : & LogsArgs ,
290+ client : & DockerClient ,
291+ line_prefix : Option < & str > ,
292+ quiet : bool ,
293+ ) -> Result < ( ) > {
294+ let follow = !args. no_follow ;
295+ let options = LogsOptions {
296+ stdout : true ,
297+ stderr : true ,
298+ follow,
299+ tail : args. lines . clone ( ) ,
300+ timestamps : args. timestamps ,
301+ ..Default :: default ( )
302+ } ;
303+
304+ let mut stream = client. inner ( ) . logs ( CONTAINER_NAME , Some ( options) ) ;
305+
306+ while let Some ( result) = stream. next ( ) . await {
307+ match result {
308+ Ok ( output) => {
309+ if let Some ( line) = log_output_to_line ( output) {
310+ if should_skip_broker_fallback_line ( & line, args) {
311+ continue ;
312+ }
313+ emit_log_line ( & line, args, line_prefix, quiet) ;
314+ }
315+ }
316+ Err ( _) => {
317+ if follow
318+ && !container_is_running ( client, CONTAINER_NAME )
319+ . await
320+ . unwrap_or ( false )
321+ && !quiet
322+ {
323+ eprintln ! ( ) ;
324+ eprintln ! ( "{}" , style( "Container stopped" ) . dim( ) ) ;
325+ }
326+ break ;
327+ }
328+ }
329+ }
330+
331+ Ok ( ( ) )
332+ }
333+
334+ fn should_skip_broker_fallback_line ( line : & str , args : & LogsArgs ) -> bool {
335+ if args. grep . is_some ( ) {
336+ return false ;
337+ }
338+
339+ !broker_log_matches ( line)
340+ }
341+
342+ fn broker_log_matches ( line : & str ) -> bool {
343+ line. contains ( "opencode_broker" ) || line. contains ( "opencode-broker" )
344+ }
345+
299346fn log_output_to_line ( output : LogOutput ) -> Option < String > {
300347 match output {
301348 LogOutput :: StdOut { message } | LogOutput :: StdErr { message } => {
0 commit comments