mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-02-06 11:22:04 +00:00
refactor(core): improve iOS log messages from stdout/stderr (#14385)
* refactor(core): improve iOS log messages from stdout/stderr move the stdout/stderr forward logic to Swift so it does not consume a Rust thread and never deadlocks on the simulator I had to work on this because i'm facing #12172 again on latest Xcode (26.1) * patch
This commit is contained in:
parent
8e3bd63db9
commit
4b00130b86
6
.changes/refactor-ios-stdout-stderr.md
Normal file
6
.changes/refactor-ios-stdout-stderr.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"tauri": patch:bug
|
||||
"tauri-macros": patch:bug
|
||||
---
|
||||
|
||||
Fix iOS deadlock when running on the simulator from Xcode by properly piping stdout/stderr messages through the Xcode console and OSLog.
|
||||
@ -4,6 +4,86 @@
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Foundation
|
||||
|
||||
class StdoutRedirector {
|
||||
private var originalStdout: Int32 = -1
|
||||
private var originalStderr: Int32 = -1
|
||||
private var stdoutPipe: [Int32] = [-1, -1]
|
||||
private var stderrPipe: [Int32] = [-1, -1]
|
||||
private var stdoutReadSource: DispatchSourceRead?
|
||||
private var stderrReadSource: DispatchSourceRead?
|
||||
|
||||
func start() {
|
||||
originalStdout = dup(STDOUT_FILENO)
|
||||
originalStderr = dup(STDERR_FILENO)
|
||||
|
||||
guard Darwin.pipe(&stdoutPipe) == 0,
|
||||
Darwin.pipe(&stderrPipe) == 0 else {
|
||||
Logger.error("Failed to create stdout/stderr pipes")
|
||||
return
|
||||
}
|
||||
|
||||
dup2(stdoutPipe[1], STDOUT_FILENO)
|
||||
dup2(stderrPipe[1], STDERR_FILENO)
|
||||
close(stdoutPipe[1])
|
||||
close(stderrPipe[1])
|
||||
|
||||
stdoutReadSource = createReader(
|
||||
readPipe: stdoutPipe[0],
|
||||
writeToOriginal: originalStdout,
|
||||
label: "stdout"
|
||||
)
|
||||
|
||||
stderrReadSource = createReader(
|
||||
readPipe: stderrPipe[0],
|
||||
writeToOriginal: originalStderr,
|
||||
label: "stderr"
|
||||
)
|
||||
}
|
||||
|
||||
private func createReader(
|
||||
readPipe: Int32,
|
||||
writeToOriginal: Int32,
|
||||
label: String
|
||||
) -> DispatchSourceRead {
|
||||
let source = DispatchSource.makeReadSource(
|
||||
fileDescriptor: readPipe,
|
||||
queue: .global(qos: .utility)
|
||||
)
|
||||
|
||||
source.setEventHandler {
|
||||
let bufferSize = 4096
|
||||
var buffer = [UInt8](repeating: 0, count: bufferSize)
|
||||
let bytesRead = read(readPipe, &buffer, bufferSize)
|
||||
|
||||
if bytesRead > 0 {
|
||||
let output = String(
|
||||
bytes: buffer[0..<bytesRead],
|
||||
encoding: .utf8
|
||||
) ?? ""
|
||||
|
||||
let trimmed = output.trimmingCharacters(in: .newlines)
|
||||
if !trimmed.isEmpty {
|
||||
// we're sending stderr to oslog, so we need to avoid recursive calls
|
||||
if trimmed.hasPrefix("OSLOG-") {
|
||||
// make sure the system can parse the oslogs
|
||||
write(writeToOriginal, &buffer, bytesRead)
|
||||
} else {
|
||||
Logger.info("[\(label)] \(trimmed)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source.setCancelHandler {
|
||||
close(readPipe)
|
||||
}
|
||||
|
||||
source.resume()
|
||||
return source
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper class for os_log function
|
||||
public class Logger {
|
||||
@ -21,7 +101,7 @@ public class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
static func log(_ items: Any..., category: String, type: OSLogType) {
|
||||
static func log(_ items: [Any], category: String, type: OSLogType) {
|
||||
if Logger.enabled {
|
||||
var message = ""
|
||||
let last = items.count - 1
|
||||
@ -31,6 +111,7 @@ public class Logger {
|
||||
message += " "
|
||||
}
|
||||
}
|
||||
|
||||
let log = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "-", category: category)
|
||||
os_log("%{public}@", log: log, type: type, String(message.prefix(4068)))
|
||||
}
|
||||
|
||||
@ -109,6 +109,14 @@ extension PluginManager: NSCopying {
|
||||
}
|
||||
}
|
||||
|
||||
private var stdoutRedirector: StdoutRedirector?
|
||||
|
||||
@_cdecl("log_stdout")
|
||||
func logStdout() {
|
||||
stdoutRedirector = StdoutRedirector()
|
||||
stdoutRedirector!.start()
|
||||
}
|
||||
|
||||
@_cdecl("register_plugin")
|
||||
func registerPlugin(name: SRString, plugin: NSObject, config: SRString, webview: WKWebView?) {
|
||||
PluginManager.shared.load(
|
||||
|
||||
@ -46,3 +46,4 @@ swift!(pub fn register_plugin(
|
||||
webview: *const c_void
|
||||
));
|
||||
swift!(pub fn on_webview_created(webview: *const c_void, controller: *const c_void));
|
||||
swift!(pub fn log_stdout());
|
||||
|
||||
@ -248,38 +248,10 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
#[cfg(target_os = "ios")]
|
||||
#[doc(hidden)]
|
||||
pub fn log_stdout() {
|
||||
use std::{
|
||||
ffi::CString,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
os::unix::prelude::*,
|
||||
thread,
|
||||
};
|
||||
|
||||
let mut logpipe: [RawFd; 2] = Default::default();
|
||||
#[cfg(target_os = "ios")]
|
||||
unsafe {
|
||||
libc::pipe(logpipe.as_mut_ptr());
|
||||
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
|
||||
libc::dup2(logpipe[1], libc::STDERR_FILENO);
|
||||
crate::ios::log_stdout();
|
||||
}
|
||||
thread::spawn(move || unsafe {
|
||||
let file = File::from_raw_fd(logpipe[0]);
|
||||
let mut reader = BufReader::new(file);
|
||||
let mut buffer = String::new();
|
||||
loop {
|
||||
buffer.clear();
|
||||
if let Ok(len) = reader.read_line(&mut buffer) {
|
||||
if len == 0 {
|
||||
break;
|
||||
} else if let Ok(msg) = CString::new(buffer.as_bytes())
|
||||
.map_err(|_| ())
|
||||
.and_then(|c| c.into_string().map_err(|_| ()))
|
||||
{
|
||||
log::info!("{}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// The user event type.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user