1+ use std:: { fs, path:: PathBuf } ;
2+
3+ use anyhow:: Context ;
14use clap:: { Parser , Subcommand } ;
25
36mod api;
@@ -16,6 +19,10 @@ struct Cli {
1619 /// Enable verbose logging
1720 #[ arg( short, long, global = true ) ]
1821 verbose : bool ,
22+
23+ /// Directory for lapdev-cli log files
24+ #[ arg( long = "log-dir" , env = "LAPDEV_LOG_DIR" , value_name = "PATH" ) ]
25+ log_dir : Option < PathBuf > ,
1926}
2027
2128#[ derive( Subcommand ) ]
@@ -56,6 +63,12 @@ enum Commands {
5663async fn main ( ) -> anyhow:: Result < ( ) > {
5764 let cli = Cli :: parse ( ) ;
5865
66+ let log_dir = resolve_log_dir ( cli. log_dir . clone ( ) ) ?;
67+ fs:: create_dir_all ( & log_dir)
68+ . with_context ( || format ! ( "failed to create log directory at {}" , log_dir. display( ) ) ) ?;
69+ let file_appender = tracing_appender:: rolling:: daily ( & log_dir, "lapdev-cli.log" ) ;
70+ let ( file_writer, _file_guard) = tracing_appender:: non_blocking ( file_appender) ;
71+
5972 // Setup logging
6073 let log_level = if cli. verbose {
6174 tracing:: Level :: DEBUG
@@ -71,11 +84,24 @@ async fn main() -> anyhow::Result<()> {
7184 . add_directive ( "tarpc=warn" . parse ( ) . unwrap ( ) )
7285 . add_directive ( "tarpc::client=warn" . parse ( ) . unwrap ( ) ) ; // Only show tarpc warnings/errors
7386
87+ let console_layer = fmt:: layer ( )
88+ . with_target ( false )
89+ . without_time ( )
90+ . with_writer ( std:: io:: stderr) ;
91+
92+ let file_layer = fmt:: layer ( )
93+ . with_target ( true )
94+ . with_ansi ( false )
95+ . with_writer ( file_writer) ;
96+
7497 tracing_subscriber:: registry ( )
7598 . with ( filter)
76- . with ( fmt:: layer ( ) . with_target ( false ) . without_time ( ) )
99+ . with ( console_layer)
100+ . with ( file_layer)
77101 . init ( ) ;
78102
103+ tracing:: debug!( "writing logs to {}" , log_dir. display( ) ) ;
104+
79105 // Handle commands
80106 match cli. command {
81107 Commands :: Login {
@@ -100,3 +126,29 @@ async fn main() -> anyhow::Result<()> {
100126
101127 Ok ( ( ) )
102128}
129+
130+ fn resolve_log_dir ( override_dir : Option < PathBuf > ) -> anyhow:: Result < PathBuf > {
131+ override_dir
132+ . or_else ( default_log_dir)
133+ . ok_or_else ( || anyhow:: anyhow!( "unable to determine log directory" ) )
134+ }
135+
136+ #[ cfg( target_os = "macos" ) ]
137+ fn default_log_dir ( ) -> Option < PathBuf > {
138+ dirs:: home_dir ( ) . map ( |home| home. join ( "Library" ) . join ( "Logs" ) . join ( "Lapdev" ) )
139+ }
140+
141+ #[ cfg( target_os = "windows" ) ]
142+ fn default_log_dir ( ) -> Option < PathBuf > {
143+ dirs:: data_local_dir ( )
144+ . or_else ( || dirs:: home_dir ( ) . map ( |home| home. join ( "AppData" ) . join ( "Local" ) ) )
145+ . map ( |base| base. join ( "Lapdev" ) . join ( "logs" ) )
146+ }
147+
148+ #[ cfg( not( any( target_os = "macos" , target_os = "windows" ) ) ) ]
149+ fn default_log_dir ( ) -> Option < PathBuf > {
150+ std:: env:: var_os ( "XDG_STATE_HOME" )
151+ . map ( PathBuf :: from)
152+ . or_else ( || dirs:: home_dir ( ) . map ( |home| home. join ( ".local" ) . join ( "state" ) ) )
153+ . map ( |base| base. join ( "lapdev" ) . join ( "logs" ) )
154+ }
0 commit comments