Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
use std::{
path::PathBuf,
sync::{Arc, Weak},
};
use async_channel::Sender;
use async_fs::OpenOptions;
use futures::AsyncWriteExt as _;
use warpui::r#async::executor::{Background, BackgroundTask};
pub mod manager;
/// Shared state for a [`SimpleLogger`].
///
/// When all [`SimpleLogger`] clones are dropped, this is dropped too, which closes
/// the logging channel and lets the background writing task finish.
///
/// We also support explicit shutdown via [`SimpleLogger::close`]. That allows a
/// caller to mark a log stream as finished immediately, even if some incidental
/// clones are still alive briefly in background tasks or callback state.
pub(crate) struct LogFileWriter {
log_tx: Sender<String>,
_logging_task: BackgroundTask,
}
impl LogFileWriter {
/// Returns true if the underlying channel has been closed.
///
/// A closed writer is logically dead even if some [`Arc`] handles still
/// exist, because it can no longer accept new log lines.
pub(crate) fn is_closed(&self) -> bool {
self.log_tx.is_closed()
}
}
/// A simple file-based logger for server stderr output.
/// Writes timestamped log entries to a file asynchronously.
#[derive(Clone)]
pub struct SimpleLogger {
// Cheaply cloneable reference to the log file writer.
writer: Arc<LogFileWriter>,
}
impl SimpleLogger {
/// Creates a new logger that writes to the specified file path.
/// The file is truncated on creation.
pub(crate) fn new(log_path: PathBuf, executor: Arc<Background>) -> Self {
let (log_tx, log_rx) = async_channel::unbounded::<String>();
if let Some(directory) = log_path.parent() {
let _ = std::fs::create_dir_all(directory);
}
let logging_task = executor.spawn(async move {
let mut log_file = match OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&log_path)
.await
{
Ok(log_file) => log_file,
Err(e) => {
log::warn!("Could not open file for logging: {:?}. {:?}", &log_path, e);
return;
}
};
loop {
match log_rx.recv().await {
Ok(log_line) => {
let _ = log_file
.write_all(
format!(
"{} | {}\n",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"),
log_line
)
.as_bytes(),
)
.await;
// Flush after each line to ensure logs are visible immediately
let _ = log_file.flush().await;
}
Err(e) => {
log::warn!("SimpleLogger: channel closed: {e}");
break;
}
}
}
// Final flush
let _ = log_file.flush().await;
});
Self {
writer: Arc::new(LogFileWriter {
log_tx,
_logging_task: logging_task,
}),
}
}
/// Log a message to the file.
pub fn log(&self, message: String) {
let _ = self.writer.log_tx.try_send(message);
}
/// Explicitly close the logger channel before all clones are dropped.
///
/// This is useful when the caller wants "this log stream is finished" to be
/// a first-class state, rather than waiting for every clone to be dropped.
/// For example, a failed connection attempt may want to write a final error
/// line, close the stream immediately, and let a later retry reclaim the
/// same log path even if some transient clones have not been dropped yet.
///
/// This is a no-op if the channel is already closed. Shutdown also happens
/// automatically when the last [`SimpleLogger`] clone is dropped.
pub fn close(&self) {
self.writer.log_tx.close();
}
/// Returns a weak reference to the shared writer, used by [`manager::LogManager`]
/// to track liveness without preventing shutdown.
pub(crate) fn downgrade(&self) -> Weak<LogFileWriter> {
Arc::downgrade(&self.writer)
}
}
+159
View File
@@ -0,0 +1,159 @@
use crate::{LogFileWriter, SimpleLogger};
use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Weak};
use thiserror::Error;
use warpui::r#async::executor::Background;
use warpui::{Entity, SingletonEntity};
#[derive(Error, Debug)]
pub enum LogManagerError {
#[error("A logger is already active for this path: {}", path.display())]
LoggerAlreadyActive { path: PathBuf },
#[error("Unknown log namespace: {namespace}")]
UnknownNamespace { namespace: String },
}
impl LogManagerError {
/// Returns a description of the error suitable for use in release-channel error reporting.
/// User-specific data (e.g. file paths) is omitted; non-sensitive details are preserved.
pub fn safe_message(&self) -> String {
match self {
Self::LoggerAlreadyActive { .. } => "logger already active for path".to_string(),
Self::UnknownNamespace { .. } => format!("{self:?}"),
}
}
}
/// Computes the log file path for a given namespace and relative path,
/// without requiring access to [`LogManager`].
///
/// This is useful for read-only path resolution (e.g. reading log files for display).
pub fn resolve_log_path(namespace: &str, relative_path: impl AsRef<Path>) -> PathBuf {
log_directory_path(namespace).join(relative_path)
}
/// Returns the base log directory for a given namespace name.
fn log_directory_path(namespace: &str) -> PathBuf {
let base_dir = warp_core::paths::secure_state_dir().unwrap_or_else(warp_core::paths::state_dir);
if cfg!(windows) {
base_dir
.join(warp_core::paths::WARP_LOGS_DIR)
.join(namespace)
} else {
base_dir.join(namespace)
}
}
/// Singleton that owns all file-based loggers in the app.
///
/// Enforces that at most one active [`SimpleLogger`] exists per log file path.
/// Stale registrations are reclaimed automatically on the next
/// [`register`](LogManager::register) call for that path.
///
/// A registration is considered stale in two cases:
/// - all [`SimpleLogger`] clones have been dropped, so the [`Weak`] entry can no
/// longer be upgraded
/// - the underlying channel has already been explicitly closed via
/// [`SimpleLogger::close`], which means the writer is logically dead even if
/// some [`Arc`] handles still exist briefly
///
/// Supporting both cases lets callers opt into eager, explicit shutdown without
/// tying path reuse strictly to the last clone being dropped.
pub struct LogManager {
namespaces: HashSet<String>,
loggers: HashMap<PathBuf, Weak<LogFileWriter>>,
}
impl Default for LogManager {
fn default() -> Self {
Self::new()
}
}
impl LogManager {
pub fn new() -> Self {
Self {
namespaces: HashSet::new(),
loggers: HashMap::new(),
}
}
/// Registers a log namespace with the given cleanup policy.
///
/// On first call for a given name, stores the namespace and purges its
/// directory if `purge_on_startup` is true. Subsequent calls for the same
/// name are no-ops.
pub fn register_namespace(&mut self, name: &str, purge_on_startup: bool) {
if self.namespaces.contains(name) {
return;
}
if purge_on_startup {
let dir = log_directory_path(name);
if let Err(e) = std::fs::remove_dir_all(&dir) {
if e.kind() != ErrorKind::NotFound {
log::warn!("Failed to purge log directory {}: {e}", dir.display());
}
}
}
self.namespaces.insert(name.to_string());
}
/// Registers a logger for a path relative to the namespace's log directory.
///
/// Returns an error if the namespace has not been registered, or if a logger
/// is already alive for that path. Stale registrations (where all clones have
/// been dropped or the channel has been explicitly closed) are reclaimed silently.
pub fn register(
&mut self,
namespace: &str,
relative_path: impl AsRef<Path>,
executor: Arc<Background>,
) -> Result<SimpleLogger, LogManagerError> {
if !self.namespaces.contains(namespace) {
return Err(LogManagerError::UnknownNamespace {
namespace: namespace.to_string(),
});
}
let path = resolve_log_path(namespace, relative_path);
self.register_resolved_path(path, executor)
}
fn register_resolved_path(
&mut self,
path: PathBuf,
executor: Arc<Background>,
) -> Result<SimpleLogger, LogManagerError> {
if let Some(existing) = self.loggers.get(&path) {
if let Some(writer) = existing.upgrade() {
// A live `Arc` alone is not enough to keep the path reserved.
// Callers may explicitly close a logger to mark the stream as
// finished before every clone has been dropped.
if !writer.is_closed() {
return Err(LogManagerError::LoggerAlreadyActive { path });
}
}
}
// In the absence of an active logger at this path, initialize and return a new logger,
// which truncates any existing log file on creation.
let logger = SimpleLogger::new(path.clone(), executor);
self.loggers.insert(path, logger.downgrade());
Ok(logger)
}
}
pub enum LogManagerEvent {}
impl Entity for LogManager {
type Event = LogManagerEvent;
}
impl SingletonEntity for LogManager {}
#[cfg(test)]
#[path = "manager_tests.rs"]
mod tests;
+87
View File
@@ -0,0 +1,87 @@
use std::{
path::{Path, PathBuf},
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use warpui::r#async::executor::Background;
use super::LogManager;
fn temp_path(name: &str) -> PathBuf {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
std::env::temp_dir().join(format!(
"simple-logger-tests-{name}-{}-{id}",
std::process::id()
))
}
fn cleanup_log_path(log_path: &Path) {
let _ = std::fs::remove_file(log_path);
if let Some(parent) = log_path.parent() {
let _ = std::fs::remove_dir_all(parent);
}
}
#[test]
fn register_resolved_path_reuses_stale_entries_after_drop() {
let mut manager = LogManager::new();
let executor = Arc::new(Background::default());
let log_path = temp_path("re-register").join("server.log");
let logger = manager
.register_resolved_path(log_path.clone(), executor.clone())
.expect("initial registration should succeed");
drop(logger);
let logger = manager
.register_resolved_path(log_path.clone(), executor)
.expect("stale entry should be reclaimed after the logger is dropped");
drop(logger);
cleanup_log_path(&log_path);
}
#[test]
fn register_resolved_path_rejects_duplicate_active_loggers() {
let mut manager = LogManager::new();
let executor = Arc::new(Background::default());
let log_path = temp_path("collision").join("server.log");
let logger = manager
.register_resolved_path(log_path.clone(), executor.clone())
.expect("initial registration should succeed");
assert!(
manager
.register_resolved_path(log_path.clone(), executor)
.is_err(),
"live logger should block duplicate registration"
);
drop(logger);
cleanup_log_path(&log_path);
}
#[test]
fn register_reclaims_closed_logger() {
let mut manager = LogManager::new();
let executor = Arc::new(Background::default());
let log_path = temp_path("close-reclaim").join("server.log");
let logger = manager
.register_resolved_path(log_path.clone(), executor.clone())
.expect("initial registration should succeed");
// Close the channel without dropping the logger — the Arc<LogFileWriter> is still alive.
logger.close();
let new_logger = manager
.register_resolved_path(log_path.clone(), executor)
.expect("closed logger should be reclaimed even when Arc is still alive");
drop(logger);
drop(new_logger);
cleanup_log_path(&log_path);
}