--- title: Log Streaming description: Stream logs as they are being produced, while the process is still running. --- Log streaming lets you access and process session command logs in real time while a process is still running, or retrieve a snapshot of all logs produced so far. Use streaming for long-running or background commands. Use snapshots when you only need the current output. For entrypoint log streaming, see [process & code execution](/docs/en/process-code-execution#entrypoint-session). To stream logs while sending input to a running command, see [execute interactive commands](/docs/en/process-code-execution#execute-interactive-commands). ## Stream logs with callbacks Stream logs in the background while the rest of your system continues executing. Use this for continuous monitoring, debugging long-running jobs, or forwarding logs to other systems. When streaming with the Python SDK, avoid blocking operations inside stdout/stderr callbacks. Blocking synchronous callbacks can cause WebSocket disconnections. Use async callbacks where possible. ```python import asyncio from daytona import Daytona, SessionExecuteRequest async def main(): daytona = Daytona() sandbox = daytona.create() session_id = "streaming-session" sandbox.process.create_session(session_id) command = sandbox.process.execute_session_command( session_id, SessionExecuteRequest( command='for i in {1..5}; do echo "Step $i"; echo "Error $i" >&2; sleep 1; done', run_async=True, ), ) # Stream logs with separate callbacks logs_task = asyncio.create_task( sandbox.process.get_session_command_logs_async( session_id, command.cmd_id, lambda stdout: print(f"[STDOUT]: {stdout}"), lambda stderr: print(f"[STDERR]: {stderr}"), ) ) print("Continuing execution while logs are streaming...") await asyncio.sleep(3) print("Other operations completed!") # Wait for the logs to complete await logs_task sandbox.delete() if __name__ == "__main__": asyncio.run(main()) ``` ```typescript import { Daytona } from '@daytona/sdk' async function main() { const daytona = new Daytona() const sandbox = await daytona.create() const sessionId = "exec-session-1" await sandbox.process.createSession(sessionId) const command = await sandbox.process.executeSessionCommand( sessionId, { command: 'for i in {1..5}; do echo "Step $i"; echo "Error $i" >&2; sleep 1; done', runAsync: true, }, ) // Stream logs with separate callbacks const logsTask = sandbox.process.getSessionCommandLogs( sessionId, command.cmdId!, (stdout) => console.log('[STDOUT]:', stdout), (stderr) => console.log('[STDERR]:', stderr), ) console.log('Continuing execution while logs are streaming...') await new Promise((resolve) => setTimeout(resolve, 3000)) console.log('Other operations completed!') // Wait for the logs to complete await logsTask await sandbox.delete() } main() ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create session_id = 'streaming-session' sandbox.process.create_session(session_id) command = sandbox.process.execute_session_command( session_id: session_id, req: Daytona::SessionExecuteRequest.new( command: 'for i in {1..5}; do echo "Step $i"; echo "Error $i" >&2; sleep 1; done', run_async: true ) ) # Stream logs using a thread log_thread = Thread.new do sandbox.process.get_session_command_logs_async( session_id: session_id, command_id: command.cmd_id, on_stdout: ->(stdout) { puts "[STDOUT]: #{stdout}" }, on_stderr: ->(stderr) { puts "[STDERR]: #{stderr}" } ) end puts 'Continuing execution while logs are streaming...' sleep(3) puts 'Other operations completed!' # Wait for the logs to complete log_thread.join daytona.delete(sandbox) ``` ```go package main import ( "context" "fmt" "log" "os" "github.com/daytona/clients/sdk-go/pkg/daytona" ) func main() { client, _ := daytona.NewClient() ctx := context.Background() sandbox, _ := client.Create(ctx, nil) sessionID := "streaming-session" sandbox.Process.CreateSession(ctx, sessionID) // Execute async command that outputs to stdout and stderr cmd := `for i in 1 2 3 4 5; do echo "Step $i"; echo "Error $i" >&2; sleep 1; done` cmdResult, _ := sandbox.Process.ExecuteSessionCommand(ctx, sessionID, cmd, true, false) cmdID, _ := cmdResult["id"].(string) // Create channels for stdout and stderr stdout := make(chan string, 100) stderr := make(chan string, 100) // Stream logs in a goroutine go func() { err := sandbox.Process.GetSessionCommandLogsStream(ctx, sessionID, cmdID, stdout, stderr) if err != nil { log.Printf("Stream error: %v", err) } }() fmt.Println("Continuing execution while logs are streaming...") // Read from channels until both are closed stdoutOpen, stderrOpen := true, true for stdoutOpen || stderrOpen { select { case chunk, ok := <-stdout: if !ok { stdoutOpen = false } else { fmt.Fprintf(os.Stdout, "[STDOUT]: %s", chunk) } case chunk, ok := <-stderr: if !ok { stderrOpen = false } else { fmt.Fprintf(os.Stderr, "[STDERR]: %s", chunk) } } } fmt.Println("Streaming completed!") sandbox.Delete(ctx) } ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.SessionExecuteRequest; import io.daytona.sdk.model.SessionExecuteResponse; public class App { public static void main(String[] args) throws InterruptedException { try (Daytona daytona = new Daytona()) { Sandbox sandbox = daytona.create(); String sessionId = "streaming-session"; sandbox.getProcess().createSession(sessionId); SessionExecuteResponse command = sandbox.getProcess().executeSessionCommand( sessionId, new SessionExecuteRequest( "for i in {1..5}; do echo \"Step $i\"; echo \"Error $i\" >&2; sleep 1; done", true)); Thread logThread = new Thread(() -> sandbox.getProcess().getSessionCommandLogs( sessionId, command.getCmdId(), stdout -> System.out.print("[STDOUT]: " + stdout), stderr -> System.err.print("[STDERR]: " + stderr))); logThread.start(); System.out.println("Continuing execution while logs are streaming..."); Thread.sleep(3000); System.out.println("Other operations completed!"); logThread.join(); sandbox.delete(); } } } ``` ```bash # Follow session command logs in real-time (WebSocket) # wss://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/{sessionId}/command/{commandId}/logs?follow=true ``` ## Retrieve all existing logs Retrieve all logs produced up to the current point in time. Use this when the command has a predictable duration, or when you prefer to poll for output instead of streaming. ```python import time from daytona import Daytona, SessionExecuteRequest daytona = Daytona() sandbox = daytona.create() session_id = "exec-session-1" sandbox.process.create_session(session_id) # Execute a blocking command and wait for the result command = sandbox.process.execute_session_command( session_id, SessionExecuteRequest(command="echo 'Hello from stdout' && echo 'Hello from stderr' >&2") ) print(f"[STDOUT]: {command.stdout}") print(f"[STDERR]: {command.stderr}") print(f"[OUTPUT]: {command.output}") # Or execute command in the background and get the logs later command = sandbox.process.execute_session_command( session_id, SessionExecuteRequest( command='while true; do if (( RANDOM % 2 )); then echo "All good at $(date)"; else echo "Oops, an error at $(date)" >&2; fi; sleep 1; done', run_async=True ) ) time.sleep(5) # Get the logs up to the current point in time logs = sandbox.process.get_session_command_logs(session_id, command.cmd_id) print(f"[STDOUT]: {logs.stdout}") print(f"[STDERR]: {logs.stderr}") print(f"[OUTPUT]: {logs.output}") sandbox.delete() ``` ```typescript import { Daytona } from '@daytona/sdk' async function main() { const daytona = new Daytona() const sandbox = await daytona.create() const sessionId = "exec-session-1" await sandbox.process.createSession(sessionId) // Execute a blocking command and wait for the result const command = await sandbox.process.executeSessionCommand( sessionId, { command: 'echo "Hello from stdout" && echo "Hello from stderr" >&2', }, ) console.log(`[STDOUT]: ${command.stdout}`) console.log(`[STDERR]: ${command.stderr}`) console.log(`[OUTPUT]: ${command.output}`) // Or execute command in the background and get the logs later const command2 = await sandbox.process.executeSessionCommand( sessionId, { command: 'while true; do if (( RANDOM % 2 )); then echo "All good at $(date)"; else echo "Oops, an error at $(date)" >&2; fi; sleep 1; done', runAsync: true, }, ) await new Promise((resolve) => setTimeout(resolve, 5000)) // Get the logs up to the current point in time const logs = await sandbox.process.getSessionCommandLogs(sessionId, command2.cmdId!) console.log(`[STDOUT]: ${logs.stdout}`) console.log(`[STDERR]: ${logs.stderr}`) console.log(`[OUTPUT]: ${logs.output}`) await sandbox.delete() } main() ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create session_id = 'exec-session-1' sandbox.process.create_session(session_id) # Execute a blocking command and wait for the result command = sandbox.process.execute_session_command( session_id: session_id, req: Daytona::SessionExecuteRequest.new( command: 'echo "Hello from stdout" && echo "Hello from stderr" >&2' ) ) puts "[STDOUT]: #{command.stdout}" puts "[STDERR]: #{command.stderr}" puts "[OUTPUT]: #{command.output}" # Or execute command in the background and get the logs later command = sandbox.process.execute_session_command( session_id: session_id, req: Daytona::SessionExecuteRequest.new( command: 'while true; do if (( RANDOM % 2 )); then echo "All good at $(date)"; else echo "Oops, an error at $(date)" >&2; fi; sleep 1; done', run_async: true ) ) sleep(5) # Get the logs up to the current point in time logs = sandbox.process.get_session_command_logs( session_id: session_id, command_id: command.cmd_id ) puts "[STDOUT]: #{logs.stdout}" puts "[STDERR]: #{logs.stderr}" puts "[OUTPUT]: #{logs.output}" daytona.delete(sandbox) ``` ```go package main import ( "context" "fmt" "log" "time" "github.com/daytona/clients/sdk-go/pkg/daytona" ) func main() { client, _ := daytona.NewClient() ctx := context.Background() sandbox, _ := client.Create(ctx, nil) sessionID := "exec-session-1" sandbox.Process.CreateSession(ctx, sessionID) // Execute a blocking command and wait for the result cmd1, _ := sandbox.Process.ExecuteSessionCommand(ctx, sessionID, `echo "Hello from stdout" && echo "Hello from stderr" >&2`, false, false) if stdout, ok := cmd1["stdout"].(string); ok { fmt.Printf("[STDOUT]: %s\n", stdout) } if stderr, ok := cmd1["stderr"].(string); ok { fmt.Printf("[STDERR]: %s\n", stderr) } // Or execute command in the background and get the logs later cmd := `counter=1; while (( counter <= 5 )); do echo "Count: $counter"; ((counter++)); sleep 1; done` cmdResult, _ := sandbox.Process.ExecuteSessionCommand(ctx, sessionID, cmd, true, false) cmdID, _ := cmdResult["id"].(string) time.Sleep(5 * time.Second) // Get the logs up to the current point in time logs, err := sandbox.Process.GetSessionCommandLogs(ctx, sessionID, cmdID) if err != nil { log.Fatalf("Failed to get logs: %v", err) } fmt.Printf("[STDOUT]: %s\n", logs.Stdout) fmt.Printf("[STDERR]: %s\n", logs.Stderr) fmt.Printf("[OUTPUT]: %s\n", logs.Output) sandbox.Delete(ctx) } ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.SessionCommandLogsResponse; import io.daytona.sdk.model.SessionExecuteRequest; import io.daytona.sdk.model.SessionExecuteResponse; public class App { public static void main(String[] args) throws InterruptedException { try (Daytona daytona = new Daytona()) { Sandbox sandbox = daytona.create(); String sessionId = "exec-session-1"; sandbox.getProcess().createSession(sessionId); SessionExecuteResponse command = sandbox.getProcess().executeSessionCommand( sessionId, new SessionExecuteRequest( "echo 'Hello from stdout' && echo 'Hello from stderr' >&2", false)); System.out.println("[STDOUT]: " + command.getStdout()); System.out.println("[STDERR]: " + command.getStderr()); System.out.println("[OUTPUT]: " + command.getOutput()); SessionExecuteResponse asyncCmd = sandbox.getProcess().executeSessionCommand( sessionId, new SessionExecuteRequest( "while true; do if (( RANDOM % 2 )); then echo \"All good at $(date)\"; else echo \"Oops, an error at $(date)\" >&2; fi; sleep 1; done", true)); Thread.sleep(5000); SessionCommandLogsResponse logs = sandbox.getProcess().getSessionCommandLogs(sessionId, asyncCmd.getCmdId()); System.out.println("[STDOUT]: " + logs.getStdout()); System.out.println("[STDERR]: " + logs.getStderr()); System.out.println("[OUTPUT]: " + logs.getOutput()); sandbox.delete(); } } } ``` ```bash curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/{sessionId}/command/{commandId}/logs' ```