forked from M-Labs/artiq
Allow changing runtime log level without recompilation.
This shouldn't affect performance much, as the log crate guards every log statement with a branch, adding just two instructions.
This commit is contained in:
parent
fe77fcc45f
commit
e8c093deb3
|
@ -18,6 +18,7 @@ logger = logging.getLogger(__name__)
|
||||||
class _H2DMsgType(Enum):
|
class _H2DMsgType(Enum):
|
||||||
LOG_REQUEST = 1
|
LOG_REQUEST = 1
|
||||||
LOG_CLEAR = 2
|
LOG_CLEAR = 2
|
||||||
|
LOG_FILTER = 13
|
||||||
|
|
||||||
SYSTEM_INFO_REQUEST = 3
|
SYSTEM_INFO_REQUEST = 3
|
||||||
SWITCH_CLOCK = 4
|
SWITCH_CLOCK = 4
|
||||||
|
@ -58,6 +59,15 @@ class _D2HMsgType(Enum):
|
||||||
CLOCK_FAILURE = 15
|
CLOCK_FAILURE = 15
|
||||||
|
|
||||||
|
|
||||||
|
class _LogLevel(Enum):
|
||||||
|
OFF = 0
|
||||||
|
ERROR = 1
|
||||||
|
WARN = 2
|
||||||
|
INFO = 3
|
||||||
|
DEBUG = 4
|
||||||
|
TRACE = 5
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedDevice(Exception):
|
class UnsupportedDevice(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -279,6 +289,13 @@ class CommKernel:
|
||||||
|
|
||||||
self._read_empty(_D2HMsgType.LOG_REPLY)
|
self._read_empty(_D2HMsgType.LOG_REPLY)
|
||||||
|
|
||||||
|
def set_log_level(self, level):
|
||||||
|
if level not in _LogLevel.__members__:
|
||||||
|
raise ValueError("invalid log level {}".format(level))
|
||||||
|
|
||||||
|
self._write_header(_H2DMsgType.LOG_FILTER)
|
||||||
|
self._write_int8(getattr(_LogLevel, level).value)
|
||||||
|
|
||||||
def flash_storage_read(self, key):
|
def flash_storage_read(self, key):
|
||||||
self._write_header(_H2DMsgType.FLASH_READ_REQUEST)
|
self._write_header(_H2DMsgType.FLASH_READ_REQUEST)
|
||||||
self._write_string(key)
|
self._write_string(key)
|
||||||
|
|
|
@ -8,12 +8,13 @@ extern crate board;
|
||||||
use core::{mem, ptr};
|
use core::{mem, ptr};
|
||||||
use core::cell::{Cell, RefCell};
|
use core::cell::{Cell, RefCell};
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use log::{Log, LogLevel, LogMetadata, LogRecord, LogLevelFilter};
|
use log::{Log, LogLevel, LogMetadata, LogRecord, LogLevelFilter, MaxLogLevelFilter};
|
||||||
use log_buffer::LogBuffer;
|
use log_buffer::LogBuffer;
|
||||||
use board::{Console, clock};
|
use board::{Console, clock};
|
||||||
|
|
||||||
pub struct BufferLogger {
|
pub struct BufferLogger {
|
||||||
buffer: RefCell<LogBuffer<&'static mut [u8]>>,
|
buffer: RefCell<LogBuffer<&'static mut [u8]>>,
|
||||||
|
filter: RefCell<Option<MaxLogLevelFilter>>,
|
||||||
trace_to_uart: Cell<bool>
|
trace_to_uart: Cell<bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ impl BufferLogger {
|
||||||
pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
|
pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
|
||||||
BufferLogger {
|
BufferLogger {
|
||||||
buffer: RefCell::new(LogBuffer::new(buffer)),
|
buffer: RefCell::new(LogBuffer::new(buffer)),
|
||||||
|
filter: RefCell::new(None),
|
||||||
trace_to_uart: Cell::new(true)
|
trace_to_uart: Cell::new(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +35,8 @@ impl BufferLogger {
|
||||||
// before log::shutdown_logger_raw is called).
|
// before log::shutdown_logger_raw is called).
|
||||||
unsafe {
|
unsafe {
|
||||||
log::set_logger_raw(|max_log_level| {
|
log::set_logger_raw(|max_log_level| {
|
||||||
max_log_level.set(LogLevelFilter::Trace);
|
max_log_level.set(LogLevelFilter::Info);
|
||||||
|
*self.filter.borrow_mut() = Some(max_log_level);
|
||||||
self as *const Log
|
self as *const Log
|
||||||
}).expect("global logger can only be initialized once");
|
}).expect("global logger can only be initialized once");
|
||||||
LOGGER = self;
|
LOGGER = self;
|
||||||
|
@ -57,6 +60,14 @@ impl BufferLogger {
|
||||||
f(self.buffer.borrow_mut().extract())
|
f(self.buffer.borrow_mut().extract())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_max_log_level(&self, max_level: LogLevelFilter) {
|
||||||
|
self.filter
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.expect("register the logger before setting maximum log level")
|
||||||
|
.set(max_level)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disable_trace_to_uart(&self) {
|
pub fn disable_trace_to_uart(&self) {
|
||||||
if self.trace_to_uart.get() {
|
if self.trace_to_uart.get() {
|
||||||
trace!("disabling tracing to UART; all further trace messages \
|
trace!("disabling tracing to UART; all further trace messages \
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
use log::LogLevelFilter;
|
||||||
use {ReadExt, WriteExt};
|
use {ReadExt, WriteExt};
|
||||||
|
|
||||||
fn read_sync(reader: &mut Read) -> io::Result<()> {
|
fn read_sync(reader: &mut Read) -> io::Result<()> {
|
||||||
|
@ -20,6 +22,8 @@ fn write_sync(writer: &mut Write) -> io::Result<()> {
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
Log,
|
Log,
|
||||||
LogClear,
|
LogClear,
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
LogFilter(LogLevelFilter),
|
||||||
|
|
||||||
SystemInfo,
|
SystemInfo,
|
||||||
SwitchClock(u8),
|
SwitchClock(u8),
|
||||||
|
@ -50,6 +54,20 @@ impl Request {
|
||||||
Ok(match reader.read_u8()? {
|
Ok(match reader.read_u8()? {
|
||||||
1 => Request::Log,
|
1 => Request::Log,
|
||||||
2 => Request::LogClear,
|
2 => Request::LogClear,
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
13 => {
|
||||||
|
let level = match reader.read_u8()? {
|
||||||
|
0 => LogLevelFilter::Off,
|
||||||
|
1 => LogLevelFilter::Error,
|
||||||
|
2 => LogLevelFilter::Warn,
|
||||||
|
3 => LogLevelFilter::Info,
|
||||||
|
4 => LogLevelFilter::Debug,
|
||||||
|
5 => LogLevelFilter::Trace,
|
||||||
|
_ => return Err(io::Error::new(io::ErrorKind::InvalidData,
|
||||||
|
"invalid log level"))
|
||||||
|
};
|
||||||
|
Request::LogFilter(level)
|
||||||
|
}
|
||||||
3 => Request::SystemInfo,
|
3 => Request::SystemInfo,
|
||||||
4 => Request::SwitchClock(reader.read_u8()?),
|
4 => Request::SwitchClock(reader.read_u8()?),
|
||||||
5 => Request::LoadKernel(reader.read_bytes()?),
|
5 => Request::LoadKernel(reader.read_bytes()?),
|
||||||
|
|
|
@ -15,7 +15,7 @@ build_artiq = { path = "../libbuild_artiq" }
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = { version = "1.0", default-features = false }
|
byteorder = { version = "1.0", default-features = false }
|
||||||
cslice = { version = "0.3" }
|
cslice = { version = "0.3" }
|
||||||
log = { version = "0.3", default-features = false, features = ["max_level_debug"] }
|
log = { version = "0.3", default-features = false }
|
||||||
fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] }
|
fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] }
|
||||||
alloc_artiq = { path = "../liballoc_artiq" }
|
alloc_artiq = { path = "../liballoc_artiq" }
|
||||||
std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
|
std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
|
||||||
|
|
|
@ -226,11 +226,15 @@ fn process_host_message(io: &Io,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
host::Request::LogClear => {
|
host::Request::LogClear => {
|
||||||
BufferLogger::with_instance(|logger| logger.clear());
|
BufferLogger::with_instance(|logger| logger.clear());
|
||||||
host_write(stream, host::Reply::Log(""))
|
host_write(stream, host::Reply::Log(""))
|
||||||
}
|
}
|
||||||
|
host::Request::LogFilter(filter) => {
|
||||||
|
info!("changing log level to {}", filter);
|
||||||
|
BufferLogger::with_instance(|logger| logger.set_max_log_level(filter));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// artiq_coreconfig
|
// artiq_coreconfig
|
||||||
host::Request::FlashRead { ref key } => {
|
host::Request::FlashRead { ref key } => {
|
||||||
|
|
|
@ -13,6 +13,14 @@ def get_argparser():
|
||||||
verbosity_args(parser)
|
verbosity_args(parser)
|
||||||
parser.add_argument("--device-db", default="device_db.pyon",
|
parser.add_argument("--device-db", default="device_db.pyon",
|
||||||
help="device database file (default: '%(default)s')")
|
help="device database file (default: '%(default)s')")
|
||||||
|
|
||||||
|
subparsers = parser.add_subparsers(dest="action")
|
||||||
|
|
||||||
|
p_set_level = subparsers.add_parser("set_level",
|
||||||
|
help="set minimum level for messages to be logged")
|
||||||
|
p_set_level.add_argument("level", metavar="LEVEL", type=str,
|
||||||
|
help="log level (one of: OFF ERROR WARN INFO DEBUG TRACE)")
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +31,9 @@ def main():
|
||||||
try:
|
try:
|
||||||
comm = device_mgr.get("comm")
|
comm = device_mgr.get("comm")
|
||||||
comm.check_system_info()
|
comm.check_system_info()
|
||||||
|
if args.action == "set_level":
|
||||||
|
comm.set_log_level(args.level)
|
||||||
|
else:
|
||||||
print(comm.get_log(), end="")
|
print(comm.get_log(), end="")
|
||||||
finally:
|
finally:
|
||||||
device_mgr.close_devices()
|
device_mgr.close_devices()
|
||||||
|
|
Loading…
Reference in New Issue