11import argparse
22import sys
3+ from datetime import datetime
4+ from typing import Optional
35
46from dstack ._internal .cli .commands import APIBaseCommand
57from dstack ._internal .cli .services .completion import RunNameCompleter
68from dstack ._internal .core .errors import CLIError
9+ from dstack ._internal .utils .common import parse_since
710from dstack ._internal .utils .logging import get_logger
811
912logger = get_logger (__name__ )
@@ -30,14 +33,25 @@ def _register(self):
3033 type = int ,
3134 default = 0 ,
3235 )
36+ self ._parser .add_argument (
37+ "--since" ,
38+ help = (
39+ "Show only logs newer than the specified date."
40+ " Can be a duration (e.g. 10s, 5m, 1d) or an RFC 3339 string (e.g. 2023-09-24T15:30:00Z)."
41+ ),
42+ type = str ,
43+ )
3344 self ._parser .add_argument ("run_name" ).completer = RunNameCompleter (all = True ) # type: ignore[attr-defined]
3445
3546 def _command (self , args : argparse .Namespace ):
3647 super ()._command (args )
3748 run = self .api .runs .get (args .run_name )
3849 if run is None :
3950 raise CLIError (f"Run { args .run_name } not found" )
51+
52+ start_time = _get_start_time (args .since )
4053 logs = run .logs (
54+ start_time = start_time ,
4155 diagnose = args .diagnose ,
4256 replica_num = args .replica ,
4357 job_num = args .job ,
@@ -48,3 +62,12 @@ def _command(self, args: argparse.Namespace):
4862 sys .stdout .buffer .flush ()
4963 except KeyboardInterrupt :
5064 pass
65+
66+
67+ def _get_start_time (since : Optional [str ]) -> Optional [datetime ]:
68+ if since is None :
69+ return None
70+ try :
71+ return parse_since (since )
72+ except ValueError as e :
73+ raise CLIError (e .args [0 ])
0 commit comments