Skip to content

Commit

Permalink
Add random 6-character suffix to log file names (#1041)
Browse files Browse the repository at this point in the history
* Add PID to log file names if log file already exists

Signed-off-by: Daniel Carl Jones <djonesoa@amazon.com>

* Update log filenames to always include some random string following the timestamp

Signed-off-by: Daniel Carl Jones <djonesoa@amazon.com>

* Rename logging_config fn to make_logging_config

Signed-off-by: Daniel Carl Jones <djonesoa@amazon.com>

* Move make_logging_config back to method of CliArgs

Signed-off-by: Daniel Carl Jones <djonesoa@amazon.com>

---------

Signed-off-by: Daniel Carl Jones <djonesoa@amazon.com>
  • Loading branch information
dannycjones authored Oct 3, 2024
1 parent b749a3e commit 8c14475
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 26 deletions.
3 changes: 3 additions & 0 deletions doc/LOGGING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ You can direct logs to a file instead of syslog by providing a destination direc

The directory will be created if it doesn't exist.
A new log file will be created for each execution of `mount-s3`.

Log file names are not considered stable and may change in the future.

Both the directory and log files are created with read/write access for the process owner and read access for the process owner's group.
Log files are not automatically rotated or cleaned up.

Expand Down
6 changes: 6 additions & 0 deletions mountpoint-s3/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## Unreleased

### Breaking changes

* When configured to log to a directory, Mountpoint now includes a random string following the timestamp in the file name.
Previously, multiple Mountpoint processes would write to the same log file causing log entries to be interleaved.
([#1041](https://github.com/awslabs/mountpoint-s3/pull/1041))

## v1.9.1 (September 19, 2024)

### Other changes
Expand Down
1 change: 1 addition & 0 deletions mountpoint-s3/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ linked-hash-map = "0.5.6"
metrics = "0.22.1"
nix = { version = "0.29.0", default-features = false, features = ["fs", "process", "signal", "user"] }
owo-colors = { version = "4.0.0", features = ["supports-colors"] }
rand = "0.8.5"
regex = "1.7.1"
serde = { version = "1.0.190", features = ["derive"] }
serde_json = "1.0.95"
Expand Down
25 changes: 16 additions & 9 deletions mountpoint-s3/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::data_cache::{CacheLimit, DiskDataCache, DiskDataCacheConfig, ExpressD
use crate::fs::{CacheConfig, S3FilesystemConfig, ServerSideEncryption, TimeToLive};
use crate::fuse::session::FuseSession;
use crate::fuse::S3FuseFilesystem;
use crate::logging::{init_logging, LoggingConfig};
use crate::logging::{init_logging, prepare_log_file_name, LoggingConfig};
use crate::mem_limiter::MINIMUM_MEM_LIMIT;
use crate::prefetch::{caching_prefetch, default_prefetch, Prefetch};
use crate::prefix::Prefix;
Expand Down Expand Up @@ -438,7 +438,11 @@ impl CliArgs {
None
}

fn logging_config(&self) -> LoggingConfig {
/// Generates a logging configuration based on the CLI arguments.
///
/// This includes random string generation which can change with each invocation,
/// so once created the [LoggingConfig] should be cloned if another owned copy is required.
fn make_logging_config(&self) -> LoggingConfig {
let default_filter = if self.no_log {
String::from("off")
} else {
Expand All @@ -455,8 +459,10 @@ impl CliArgs {
filter
};

let log_file = self.log_directory.as_ref().map(|dir| prepare_log_file_name(dir));

LoggingConfig {
log_directory: self.log_directory.clone(),
log_file,
log_to_stdout: self.foreground,
default_filter,
}
Expand Down Expand Up @@ -515,7 +521,7 @@ where
);

if args.foreground {
init_logging(args.logging_config()).context("failed to initialize logging")?;
init_logging(args.make_logging_config()).context("failed to initialize logging")?;

let _metrics = metrics::install();

Expand All @@ -532,17 +538,20 @@ where
// child process will report its status via this pipe.
let (read_fd, write_fd) = nix::unistd::pipe().context("Failed to create a pipe")?;

// Prepare logging configuration up front, so the values are shared between the two processes.
let logging_config = args.make_logging_config();

// Don't share args across the fork. It should just be plain data, so probably fine to be
// copy-on-write, but just in case we ever add something more fancy to the struct.
drop(args);

// SAFETY: Child process has full ownership of its resources.
// There is no shared data between parent and child processes.
// There is no shared data between parent and child processes other than logging configuration.
let pid = unsafe { nix::unistd::fork() };
match pid.expect("Failed to fork mount process") {
ForkResult::Child => {
let args = CliArgs::parse();
init_logging(args.logging_config()).context("failed to initialize logging")?;
init_logging(logging_config).context("failed to initialize logging")?;

let _metrics = metrics::install();

Expand Down Expand Up @@ -584,9 +593,7 @@ where
}
}
ForkResult::Parent { child } => {
let args = CliArgs::parse();

init_logging(args.logging_config()).context("failed to initialize logging")?;
init_logging(logging_config).context("failed to initialize logging")?;

// close unused file descriptor, we only read from this end.
drop(write_fd);
Expand Down
53 changes: 36 additions & 17 deletions mountpoint-s3/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ use std::fs::{DirBuilder, OpenOptions};
use std::os::unix::fs::DirBuilderExt;
use std::os::unix::prelude::OpenOptionsExt;
use std::panic::{self, PanicInfo};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::thread;

use crate::metrics::metrics_tracing_span_layer;
use anyhow::Context;
use mountpoint_s3_crt::common::rust_log_adapter::RustLogAdapter;
use rand::Rng;
use time::format_description::FormatItem;
use time::macros;
use time::OffsetDateTime;
Expand All @@ -21,11 +22,13 @@ use tracing_subscriber::{Layer, Registry};
mod syslog;
use self::syslog::SyslogLayer;

/// Configuration for Mountpoint logging
/// Configuration for Mountpoint logging.
///
/// This configuration struct is safe to use across forks.
#[derive(Debug)]
pub struct LoggingConfig {
/// A directory to create log files in. If unspecified, logs will be routed to syslog.
pub log_directory: Option<PathBuf>,
/// File to write logs into. If unspecified, logs will be routed to syslog.
pub log_file: Option<PathBuf>,
/// Whether to duplicate logs to stdout in addition to syslog or the log directory.
pub log_to_stdout: bool,
/// The default filter directive (in the sense of [tracing_subscriber::filter::EnvFilter]) to
Expand All @@ -45,6 +48,28 @@ pub fn init_logging(config: LoggingConfig) -> anyhow::Result<()> {
Ok(())
}

/// For a given log directory, prepare a file name for this Mountpoint.
///
/// This may include a randomly generated component and return different results between invocations.
pub fn prepare_log_file_name(log_directory: &Path) -> PathBuf {
let timestamp = {
const TIMESTAMP_FORMAT: &[FormatItem<'static>] =
macros::format_description!("[year]-[month]-[day]T[hour]-[minute]-[second]Z");
OffsetDateTime::now_utc()
.format(TIMESTAMP_FORMAT)
.expect("couldn't format timestamp for log file name")
};

let random_suffix: String = rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(6)
.map(char::from)
.collect();
let file_name = format!("mountpoint-s3-{timestamp}.{random_suffix}.log");

log_directory.join(file_name)
}

fn tracing_panic_hook(panic_info: &PanicInfo) {
let location = panic_info
.location()
Expand Down Expand Up @@ -92,23 +117,17 @@ fn init_tracing_subscriber(config: LoggingConfig) -> anyhow::Result<()> {

RustLogAdapter::try_init().context("failed to initialize CRT logger")?;

let file_layer = if let Some(path) = &config.log_directory {
const LOG_FILE_NAME_FORMAT: &[FormatItem<'static>] =
macros::format_description!("mountpoint-s3-[year]-[month]-[day]T[hour]-[minute]-[second]Z.log");
let filename = OffsetDateTime::now_utc()
.format(LOG_FILE_NAME_FORMAT)
.context("couldn't format log file name")?;

// log directories and files created by Mountpoint should not be accessible by other users
let file_layer = if let Some(log_file_path) = &config.log_file {
// log directories and files created by Mountpoint should not be writable by other users
let mut dir_builder = DirBuilder::new();
dir_builder.recursive(true).mode(0o750);
let mut file_options = OpenOptions::new();
file_options.mode(0o640).append(true).create(true);

dir_builder.create(path).context("failed to create log folder")?;
let file = file_options
.open(path.join(filename))
.context("failed to create log file")?;
if let Some(parent_dir) = log_file_path.parent() {
dir_builder.create(parent_dir).context("failed to create log folder")?;
}
let file = file_options.open(log_file_path).context("failed to create log file")?;

let file_layer = tracing_subscriber::fmt::layer()
.with_ansi(false)
Expand All @@ -119,7 +138,7 @@ fn init_tracing_subscriber(config: LoggingConfig) -> anyhow::Result<()> {
None
};

let syslog_layer: Option<Filtered<_, _, Registry>> = if config.log_directory.is_none() {
let syslog_layer: Option<Filtered<_, _, Registry>> = if config.log_file.is_none() {
// TODO decide how to configure the filter for syslog
let env_filter = create_env_filter(&config.default_filter);
// Don't fail if syslog isn't available on the system, since it's a default
Expand Down

1 comment on commit 8c14475

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.

Benchmark suite Current: 8c14475 Previous: b749a3e Ratio
time_to_first_byte_read 77.2158929 milliseconds 30.9619425 milliseconds 2.49

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.