virtual_console_lib/
log.rs1use crate::terminal::Terminal;
6use anyhow::Error;
7use fuchsia_async::{self as fasync, OnSignals};
8use std::io::sink;
9use term_model::ansi::Processor;
10use term_model::event::EventListener;
11
12pub trait LogClient: 'static + Clone {
13 type Listener;
14
15 fn create_terminal(&self, id: u32, title: String) -> Result<Terminal<Self::Listener>, Error>;
16 fn request_update(&self, id: u32);
17}
18
19pub struct Log;
20
21impl Log {
22 pub fn start<T: LogClient>(
23 read_only_debuglog: zx::DebugLog,
24 client: &T,
25 id: u32,
26 ) -> Result<(), Error>
27 where
28 <T as LogClient>::Listener: EventListener,
29 {
30 let client = client.clone();
31 let terminal =
32 client.create_terminal(id, "debuglog".to_string()).expect("failed to create terminal");
33 let term = terminal.clone_term();
34
35 let proc_koid =
37 fuchsia_runtime::process_self().koid().expect("failed to get koid for process");
38
39 fasync::Task::local(async move {
40 let mut sink = sink();
41 let mut parser = Processor::new();
42
43 loop {
44 let on_signal = OnSignals::new(&read_only_debuglog, zx::Signals::LOG_READABLE);
45 on_signal.await.expect("failed to wait for log readable");
46
47 loop {
48 match read_only_debuglog.read() {
49 Ok(record) => {
50 if record.pid == proc_koid {
52 continue;
53 }
54
55 let mut term = term.borrow_mut();
56
57 let prefix = format!(
59 "\u{001b}[32m{:05}.{:03}\u{001b}[39m] \u{001b}[31m{:05}.\u{001b}[36m{:05}\u{001b}[39m> ",
60 record.timestamp.into_nanos() / 1_000_000_000,
61 (record.timestamp.into_nanos() / 1_000_000) % 1_000,
62 record.pid.raw_koid(),
63 record.tid.raw_koid(),
64 );
65 for byte in prefix.as_bytes() {
66 parser.advance(&mut *term, *byte, &mut sink);
67 }
68
69 let mut record_data = record.data();
71 if record_data.last() == Some(&b'\n') {
72 record_data = &record_data[..record_data.len() - 1];
73 }
74
75 for byte in record_data.iter() {
77 parser.advance(&mut *term, *byte, &mut sink);
78 }
79
80 for byte in "\r\n".as_bytes() {
82 parser.advance(&mut *term, *byte, &mut sink);
83 }
84
85 client.request_update(id);
87 }
88 Err(status) if status == zx::Status::SHOULD_WAIT => {
89 break;
90 }
91 Err(_) => {
92 let mut term = term.borrow_mut();
93 for byte in "\r\n<<LOG ERROR>>".as_bytes() {
94 parser.advance(&mut *term, *byte, &mut sink);
95 }
96
97 client.request_update(id);
99 break;
100 }
101 }
102 }
103 }
104 })
105 .detach();
106
107 Ok(())
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::colors::ColorScheme;
115 use fuchsia_async as fasync;
116 use term_model::event::Event;
117
118 #[derive(Default)]
119 struct TestListener;
120
121 impl EventListener for TestListener {
122 fn send_event(&self, _event: Event) {}
123 }
124
125 #[derive(Default, Clone)]
126 struct TestLogClient;
127
128 impl LogClient for TestLogClient {
129 type Listener = TestListener;
130
131 fn create_terminal(
132 &self,
133 _id: u32,
134 title: String,
135 ) -> Result<Terminal<Self::Listener>, Error> {
136 Ok(Terminal::new(TestListener::default(), title, ColorScheme::default(), 1024, None))
137 }
138 fn request_update(&self, _id: u32) {}
139 }
140
141 #[fasync::run_singlethreaded(test)]
142 async fn can_start_log() -> Result<(), Error> {
143 let resource = zx::Resource::from(zx::NullableHandle::invalid());
144 let debuglog = zx::DebugLog::create(&resource, zx::DebugLogOpts::empty()).unwrap();
145 let client = TestLogClient::default();
146 let _ = Log::start(debuglog, &client, 0)?;
147 Ok(())
148 }
149}