Skip to content

Commit 1edd2d5

Browse files
committed
feat: truncates log files on restart
- added flag for clearing log files at restart Signed-off-by: Aminu 'Seun Joshua <seun.aminujoshua@gmail.com> - refactor: renaming + login Signed-off-by: Aminu 'Seun Joshua <seun.aminujoshua@gmail.com> - refactor: creates instead of delete Signed-off-by: Aminu 'Seun Joshua <seun.aminujoshua@gmail.com> - warn of error Signed-off-by: Aminu 'Seun Joshua <seun.aminujoshua@gmail.com> - revert format Signed-off-by: Aminu 'Seun Joshua <seun.aminujoshua@gmail.com> - some renaming + cleaner code Signed-off-by: Aminu 'Seun Joshua <seun.aminujoshua@gmail.com> - clean up Signed-off-by: Aminu 'Seun Joshua <seun.aminujoshua@gmail.com>
1 parent 2860166 commit 1edd2d5

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

crates/runtime-factors/src/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ impl RuntimeFactorsBuilder for FactorsBuilder {
5050
executor.add_hooks(StdioLoggingExecutorHooks::new(
5151
config.follow_components.clone(),
5252
runtime_config.log_dir(),
53+
config.truncate_logs,
5354
));
5455
executor.add_hooks(SqlStatementExecutorHook::new(
5556
args.sqlite_statements.clone(),

crates/trigger/src/cli.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub use stdio::StdioLoggingExecutorHooks;
2727
pub use summary::{KeyValueDefaultStoreSummaryHook, SqliteDefaultStoreSummaryHook};
2828

2929
pub const APP_LOG_DIR: &str = "APP_LOG_DIR";
30+
pub const SPIN_TRUNCATE_LOGS: &str = "SPIN_TRUNCATE_LOGS";
3031
pub const DISABLE_WASMTIME_CACHE: &str = "DISABLE_WASMTIME_CACHE";
3132
pub const FOLLOW_LOG_OPT: &str = "FOLLOW_ID";
3233
pub const WASMTIME_CACHE_FILE: &str = "WASMTIME_CACHE_FILE";
@@ -54,6 +55,13 @@ pub struct FactorsTriggerCommand<T: Trigger<B::Factors>, B: RuntimeFactorsBuilde
5455
)]
5556
pub log: Option<PathBuf>,
5657

58+
/// If set, Spin truncates the log files before starting the application.
59+
#[clap(
60+
name = SPIN_TRUNCATE_LOGS,
61+
long = "truncate-logs",
62+
)]
63+
pub truncate_logs: bool,
64+
5765
/// Disable Wasmtime cache.
5866
#[clap(
5967
name = DISABLE_WASMTIME_CACHE,
@@ -139,6 +147,8 @@ pub struct FactorsConfig {
139147
pub follow_components: FollowComponents,
140148
/// Log directory for component stdout/stderr.
141149
pub log_dir: UserProvidedPath,
150+
/// If set, Spin truncates the log files before starting the application.
151+
pub truncate_logs: bool,
142152
}
143153

144154
/// An empty implementation of clap::Args to be used as TriggerExecutor::RunConfig
@@ -220,6 +230,7 @@ impl<T: Trigger<B::Factors>, B: RuntimeFactorsBuilder> FactorsTriggerCommand<T,
220230
local_app_dir: local_app_dir.clone(),
221231
follow_components,
222232
log_dir,
233+
truncate_logs: self.truncate_logs,
223234
};
224235

225236
let run_fut = builder

crates/trigger/src/cli/stdio.rs

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use spin_factors::RuntimeFactors;
1212
use spin_factors_executor::ExecutorHooks;
1313
use tokio::io::AsyncWrite;
1414

15+
pub const STDOUT_LOG_FILE_SUFFIX: &str = "stdout";
16+
pub const STDERR_LOG_FILE_SUFFIX: &str = "stderr";
17+
1518
/// Which components should have their logs followed on stdout/stderr.
1619
#[derive(Clone, Debug, Default)]
1720
pub enum FollowComponents {
@@ -39,13 +42,19 @@ impl FollowComponents {
3942
pub struct StdioLoggingExecutorHooks {
4043
follow_components: FollowComponents,
4144
log_dir: Option<PathBuf>,
45+
truncate_log: bool,
4246
}
4347

4448
impl StdioLoggingExecutorHooks {
45-
pub fn new(follow_components: FollowComponents, log_dir: Option<PathBuf>) -> Self {
49+
pub fn new(
50+
follow_components: FollowComponents,
51+
log_dir: Option<PathBuf>,
52+
truncate_log: bool,
53+
) -> Self {
4654
Self {
4755
follow_components,
4856
log_dir,
57+
truncate_log,
4958
}
5059
}
5160

@@ -56,8 +65,7 @@ impl StdioLoggingExecutorHooks {
5665
log_dir: Option<&Path>,
5766
) -> Result<ComponentStdioWriter> {
5867
let sanitized_component_id = sanitize_filename::sanitize(component_id);
59-
let log_path = log_dir
60-
.map(|log_dir| log_dir.join(format!("{sanitized_component_id}_{log_suffix}.txt",)));
68+
let log_path = sanitized_log_path(log_dir, &sanitized_component_id, log_suffix);
6169
let log_path = log_path.as_deref();
6270

6371
let follow = self.follow_components.should_follow(component_id);
@@ -86,6 +94,35 @@ impl StdioLoggingExecutorHooks {
8694
_ => Ok(()),
8795
}
8896
}
97+
98+
fn truncate_log_files<F: RuntimeFactors>(
99+
&self,
100+
configured_app: &spin_factors::ConfiguredApp<F>,
101+
) {
102+
let sanitized_component_ids: Vec<String> = configured_app
103+
.app()
104+
.components()
105+
.map(|c| sanitize_filename::sanitize(c.locked.id.clone()))
106+
.collect();
107+
108+
for sanitized_component_id in sanitized_component_ids {
109+
if let Some(stdout_log_path) = sanitized_log_path(
110+
self.log_dir.as_deref(),
111+
&sanitized_component_id,
112+
STDOUT_LOG_FILE_SUFFIX,
113+
) {
114+
_ = std::fs::File::create(stdout_log_path);
115+
}
116+
117+
if let Some(stderr_log_path) = sanitized_log_path(
118+
self.log_dir.as_deref(),
119+
&sanitized_component_id,
120+
STDERR_LOG_FILE_SUFFIX,
121+
) {
122+
_ = std::fs::File::create(stderr_log_path);
123+
}
124+
}
125+
}
89126
}
90127

91128
#[async_trait]
@@ -95,11 +132,16 @@ impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for StdioLoggingExecutorHooks {
95132
configured_app: &spin_factors::ConfiguredApp<F>,
96133
) -> anyhow::Result<()> {
97134
self.validate_follows(configured_app.app())?;
135+
98136
if let Some(dir) = &self.log_dir {
99137
// Ensure log dir exists if set
100138
std::fs::create_dir_all(dir)
101139
.with_context(|| format!("Failed to create log dir {}", quoted_path(dir)))?;
102140

141+
if self.truncate_log {
142+
self.truncate_log_files(configured_app);
143+
}
144+
103145
println!("Logging component stdio to {}", quoted_path(dir.join("")))
104146
}
105147
Ok(())
@@ -115,12 +157,12 @@ impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for StdioLoggingExecutorHooks {
115157
};
116158
wasi_builder.stdout_pipe(self.component_stdio_writer(
117159
&component_id,
118-
"stdout",
160+
STDOUT_LOG_FILE_SUFFIX,
119161
self.log_dir.as_deref(),
120162
)?);
121163
wasi_builder.stderr_pipe(self.component_stdio_writer(
122164
&component_id,
123-
"stderr",
165+
STDERR_LOG_FILE_SUFFIX,
124166
self.log_dir.as_deref(),
125167
)?);
126168
Ok(())
@@ -328,3 +370,11 @@ fn bullet_list<S: std::fmt::Display>(items: impl IntoIterator<Item = S>) -> Stri
328370
.collect::<Vec<_>>()
329371
.join("\n")
330372
}
373+
374+
fn sanitized_log_path(
375+
log_dir: Option<&Path>,
376+
sanitized_component_id: &str,
377+
log_suffix: &str,
378+
) -> Option<PathBuf> {
379+
log_dir.map(|log_dir| log_dir.join(format!("{sanitized_component_id}_{log_suffix}.txt",)))
380+
}

0 commit comments

Comments
 (0)