Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ sentry = { version = "0.42.0", features = [
"anyhow",
"backtrace",
"debug-images",
"tracing",
] }
tracing = "0.1.41"
futures = "0.3.31"
Expand Down
48 changes: 46 additions & 2 deletions apps/desktop/src-tauri/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ impl ExportSettings {
_ => false,
}
}

fn format_label(&self) -> &'static str {
match self {
ExportSettings::Mp4(_) => "mp4",
ExportSettings::Gif(_) => "gif",
ExportSettings::Mov(_) => "mov",
}
}
}

fn export_project_config(
Expand Down Expand Up @@ -169,6 +177,10 @@ pub async fn export_video(

let result = do_export(&project_path, &settings, &progress, force_ffmpeg).await;

let format = settings.format_label();
let fps = settings.fps();
let cursor_only = settings.cursor_only();

match result {
Ok(path) => {
info!("Exported to {} completed", path.display());
Expand All @@ -191,18 +203,50 @@ pub async fn export_video(
Ok(path)
}
Err(retry_e) => {
sentry::capture_message(&retry_e, sentry::Level::Error);
capture_export_failure(
&retry_e,
format,
fps,
cursor_only,
true,
&project_path,
);
Err(retry_e)
}
}
}
Err(e) => {
sentry::capture_message(&e, sentry::Level::Error);
capture_export_failure(&e, format, fps, cursor_only, force_ffmpeg, &project_path);
Err(e)
}
}
}

fn capture_export_failure(
error: &str,
format: &'static str,
fps: u32,
cursor_only: bool,
force_ffmpeg: bool,
project_path: &Path,
) {
sentry::with_scope(
|scope| {
scope.set_tag("export.format", format);
scope.set_tag("export.fps", fps.to_string());
scope.set_tag("export.cursor_only", cursor_only.to_string());
scope.set_tag("export.force_ffmpeg", force_ffmpeg.to_string());
scope.set_extra(
"project_path",
serde_json::Value::String(project_path.display().to_string()),
);
},
|| {
sentry::capture_message(error, sentry::Level::Error);
},
);
}

#[derive(Debug, serde::Serialize, specta::Type)]
pub struct ExportEstimates {
pub duration_seconds: f64,
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src-tauri/src/general_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ pub struct GeneralSettingsStore {
pub enable_telemetry: bool,
#[serde(default)]
pub out_of_process_muxer: bool,
#[serde(default)]
pub verbose_logging: bool,
}

fn default_enable_native_camera_preview() -> bool {
Expand Down Expand Up @@ -254,6 +256,7 @@ impl Default for GeneralSettingsStore {
has_completed_onboarding: false,
enable_telemetry: true,
out_of_process_muxer: false,
verbose_logging: false,
}
}
}
Expand Down
31 changes: 27 additions & 4 deletions apps/desktop/src-tauri/src/gpu_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,23 @@ async fn init_gpu_inner() -> Option<SharedGpuContext> {
tracing::warn!(
"No hardware GPU adapter found, attempting software fallback for shared context"
);
let software_adapter = instance
let software_adapter = match instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::LowPower,
force_fallback_adapter: true,
compatible_surface: None,
})
.await
.ok()?;
{
Ok(a) => a,
Err(err) => {
tracing::error!(
error = %err,
"Failed to acquire any wgpu adapter (hardware and software fallback both unavailable)"
);
return None;
}
};

let adapter_info = software_adapter.get_info();

Expand All @@ -105,14 +114,28 @@ async fn init_gpu_inner() -> Option<SharedGpuContext> {
(software_adapter, true)
};

let (device, queue) = adapter
let adapter_info = adapter.get_info();

let (device, queue) = match adapter
.request_device(&wgpu::DeviceDescriptor {
label: Some("cap-shared-gpu-device"),
required_features: wgpu::Features::empty(),
..Default::default()
})
.await
.ok()?;
{
Ok(pair) => pair,
Err(err) => {
tracing::error!(
error = %err,
adapter_name = adapter_info.name,
adapter_backend = ?adapter_info.backend,
adapter_device_type = ?adapter_info.device_type,
"wgpu request_device failed for shared GPU context"
);
return None;
}
};

Some(SharedGpuContext {
device: Arc::new(device),
Expand Down
129 changes: 128 additions & 1 deletion apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3509,9 +3509,89 @@ type FilteredRegistry = tracing_subscriber::layer::Layered<

pub type DynLoggingLayer = Box<dyn tracing_subscriber::Layer<FilteredRegistry> + Send + Sync>;
type LoggingHandle = tracing_subscriber::reload::Handle<Option<DynLoggingLayer>, FilteredRegistry>;
pub type LevelFilterHandle = tracing_subscriber::reload::Handle<
tracing_subscriber::filter::LevelFilter,
tracing_subscriber::layer::Layered<
tracing_subscriber::reload::Layer<Option<DynLoggingLayer>, FilteredRegistry>,
FilteredRegistry,
>,
>;

pub struct VerboseLoggingHandle {
inner: LevelFilterHandle,
default_level: tracing_subscriber::filter::LevelFilter,
}

impl VerboseLoggingHandle {
pub fn new(
inner: LevelFilterHandle,
default_level: tracing_subscriber::filter::LevelFilter,
) -> Self {
Self {
inner,
default_level,
}
}

pub fn set_verbose(&self, verbose: bool) -> Result<(), String> {
let level = if verbose {
tracing_subscriber::filter::LevelFilter::TRACE
} else {
self.default_level
};
self.inner
.reload(level)
.map_err(|e| format!("Failed to reload log level: {e}"))
}
}

#[tauri::command]
#[specta::specta]
async fn set_verbose_logging(
app: AppHandle,
handle: State<'_, Arc<VerboseLoggingHandle>>,
enabled: bool,
) -> Result<(), String> {
handle.set_verbose(enabled)?;

GeneralSettingsStore::update(&app, |s| {
s.verbose_logging = enabled;
})?;

if enabled {
tracing::info!("Verbose logging enabled (TRACE level)");
} else {
tracing::info!("Verbose logging disabled (returned to default level)");
}

Ok(())
}

#[tauri::command]
#[specta::specta]
async fn open_logs_folder(app: AppHandle) -> Result<(), String> {
let logs_dir = app
.state::<ArcLock<crate::App>>()
.read()
.await
.logs_dir
.clone();

let path_str = logs_dir
.to_str()
.ok_or_else(|| "Logs directory path is not valid UTF-8".to_string())?;

app.opener()
.open_path(path_str, None::<String>)
.map_err(|e| format!("Failed to open logs folder: {e}"))
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
pub async fn run(
recording_logging_handle: LoggingHandle,
level_filter_handle: LevelFilterHandle,
logs_dir: PathBuf,
) {
ffmpeg::init()
.map_err(|e| {
error!("Failed to initialize ffmpeg: {e}");
Expand All @@ -3528,6 +3608,8 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
set_camera_input,
recording_settings::set_recording_mode,
upload_logs,
open_logs_folder,
set_verbose_logging,
get_system_diagnostics,
recording::start_recording,
recording::stop_recording,
Expand Down Expand Up @@ -3805,6 +3887,30 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
specta_builder.mount_events(&app);
hotkeys::init(&app);
general_settings::init(&app);

#[cfg(debug_assertions)]
let default_log_level = tracing_subscriber::filter::LevelFilter::TRACE;
#[cfg(not(debug_assertions))]
let default_log_level = tracing_subscriber::filter::LevelFilter::INFO;

let verbose_logging_handle = Arc::new(VerboseLoggingHandle::new(
level_filter_handle,
default_log_level,
));

let verbose_logging_enabled = GeneralSettingsStore::get(&app)
.ok()
.flatten()
.map(|s| s.verbose_logging)
.unwrap_or(false);

if verbose_logging_enabled
&& let Err(err) = verbose_logging_handle.set_verbose(true)
{
warn!("Failed to enable verbose logging at startup: {err}");
}

app.manage(verbose_logging_handle);
fake_window::init(&app);
app.manage(target_select_overlay::WindowFocusManager::default());
app.manage(EditorWindowIds::default());
Expand Down Expand Up @@ -3877,6 +3983,27 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
});
}

if let Ok(Some(settings)) = GeneralSettingsStore::get(&app) {
sentry::configure_scope(|scope| {
scope.set_tag("os", std::env::consts::OS);
scope.set_tag("arch", std::env::consts::ARCH);
scope.set_tag("cap_version", env!("CARGO_PKG_VERSION"));
scope.set_tag("instance_id", settings.instance_id.to_string());
scope.set_tag(
"cap_backend",
if settings.server_url == "https://cap.so" {
"cloud"
} else {
"self_hosted"
},
);
scope.set_tag(
"verbose_logging",
if settings.verbose_logging { "on" } else { "off" },
);
});
}

{
let (server_url, should_update) = if cfg!(debug_assertions)
&& let Ok(url) = std::env::var("VITE_SERVER_URL")
Expand Down
23 changes: 19 additions & 4 deletions apps/desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use std::sync::Arc;

use cap_desktop_lib::DynLoggingLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use tracing_subscriber::{filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt};

fn main() {
#[cfg(debug_assertions)]
Expand Down Expand Up @@ -105,9 +105,23 @@ fn main() {
};

#[cfg(debug_assertions)]
let level_filter = tracing_subscriber::filter::LevelFilter::TRACE;
let initial_level = LevelFilter::TRACE;
#[cfg(not(debug_assertions))]
let level_filter = tracing_subscriber::filter::LevelFilter::INFO;
let initial_level = LevelFilter::INFO;

let (level_filter, level_handle) = tracing_subscriber::reload::Layer::new(initial_level);

let sentry_layer = sentry::integrations::tracing::layer().event_filter(|metadata| {
match *metadata.level() {
tracing::Level::ERROR => sentry::integrations::tracing::EventFilter::Event,
tracing::Level::WARN | tracing::Level::INFO => {
sentry::integrations::tracing::EventFilter::Breadcrumb
}
tracing::Level::DEBUG | tracing::Level::TRACE => {
sentry::integrations::tracing::EventFilter::Ignore
}
}
});

tracing_subscriber::registry()
.with(tracing_subscriber::filter::filter_fn(
Expand All @@ -116,6 +130,7 @@ fn main() {
.with(reload_layer)
.with(level_filter)
.with(otel_layer)
.with(sentry_layer)
.with(
tracing_subscriber::fmt::layer()
.with_ansi(true)
Expand All @@ -141,5 +156,5 @@ fn main() {
.enable_all()
.build()
.expect("Failed to build multi threaded tokio runtime")
.block_on(cap_desktop_lib::run(handle, logs_dir));
.block_on(cap_desktop_lib::run(handle, level_handle, logs_dir));
}
Loading
Loading