[rCore学习笔记 012]彩色化LOG
实验要求
- 实现分支:ch1
- 完成实验指导书中的内容并在裸机上实现
hello world
输出。 - 实现彩色输出宏(只要求可以彩色输出,不要求 log 等级控制,不要求多种颜色)
- 隐形要求
可以关闭内核所有输出。从 lab2 开始要求关闭内核所有输出(如果实现了 log 等级控制,那么这一点自然就实现了)。 - 利用彩色输出宏输出 os 内存空间布局
输出.text
、.data
、.rodata
、.bss
各段位置,输出等级为INFO
。
ANSI 转义字符
echo -e "\x1b[31mhello world\x1b[0m"
参考资源
官方给出如下资源:对于 Rust, 可以使用 crate log ,推荐参考 rCore
crate log 的应用
打开提供的参考链接,可以了解到应用log
这个特质的方式,看Use这一节,
The basic use of the log crate is through the five logging macros: error!
, warn!
, info!
, debug!
and trace!
where error!
represents the highest-priority log messages and trace!
the lowest. The log messages are filtered by configuring the log level to exclude messages with a lower priority. Each of these macros accept format strings similarly to println!
.
在In libraries里可以看到,我们如果要使用log
,则需要使用crate log
,
Libraries should link only to the log
crate, and use the provided macros to log whatever information will be useful to downstream consumers.
例子
参考Examples,创建一个新的工程来测试它,在/homework/homework-1-log
下用cargo new test_log
创建一个测试工程,在test_log/src
下,万万没想到,这个例子竟然成为了最猛的一集,完美展示了不懂一门语言硬学导致的问题.
Rust
的Crate log
的例子中为:
use log::{info, warn};
pub fn shave_the_yak(yak: &mut Yak) {
info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
loop {
match find_a_razor() {
Ok(razor) => {
info!("Razor located: {}", razor);
yak.shave(razor);
break;
}
Err(err) => {
warn!("Unable to locate a razor: {}, retrying", err);
}
}
}
}
但是这一段是伪代码,运行的时候是会报错的:
Compiling test_log v0.1.0 (/home/winddevil/workspace/homework/homework-1-log/test_log)
error[E0432]: unresolved import `log`
--> src/main.rs:2:5
|
2 | use log::{info, warn};
| ^^^ use of undeclared crate or module `log`
error[E0412]: cannot find type `Yak` in this scope
--> src/main.rs:3:32
|
3 | pub fn shave_the_yak(yak: &mut Yak)
| ^^^ not found in this scope
error[E0425]: cannot find function `find_a_razor` in this scope
--> src/main.rs:9:15
|
9 | match find_a_razor()
| ^^^^^^^^^^^^ not found in this scope
Some errors have detailed explanations: E0412, E0425, E0432.
For more information about an error, try `rustc --explain E0412`.
error: could not compile `test_log` (bin "test_log") due to 3 previous errors
这就是我们对于Rust
的一种比丁真还要纯真的认识导致的,这时候我们把这个报错error[E0432]: unresolved import 'log'
喂给GPT
,人猿相揖别。只几个石头磨过,小儿时节。
善于使用工具正是一个懒狗所需要的,
遇到错误 error[E0432]: unresolved import 'log'
表示Rust编译器无法找到你尝试引入的log
库。这通常是因为以下几个原因:
- 未添加
log
库为依赖:确保你的Cargo.toml
文件中已经添加了log
库作为依赖。如果没有,你需要在[dependencies]
部分添加它。对于最常见的使用场景,添加如下内容:[dependencies]
log = "0.4" # 或者其它适用的版本号
然后在项目目录下运行 cargo update
来下载依赖。
2. Cargo特性(gated features):如果你的项目或某个依赖启用了特定的Cargo特性,并且这些特性影响了log
的使用,确保正确配置了这些特性。检查Cargo.toml
中的[features]
部分和相关依赖的特性要求。
3. 路径问题:如果你的项目结构比较特殊,可能需要使用相对路径或绝对路径来正确引入log
。但这通常不是导致上述错误的原因,因为log
通常作为crate直接引入。
4. 编译目标或环境问题:确保你的编译目标和环境配置没有排除log
库的编译或链接。这在交叉编译时可能会成为问题。
5. Cargo.lock的干扰:有时候,旧的Cargo.lock
文件可能锁定了不兼容的依赖版本,导致新添加的依赖无法正确解析。尝试删除Cargo.lock
文件(如果有),然后重新运行cargo build
或cargo run
让Cargo重新生成它。
解决方法通常是先检查并修正Cargo.toml
,确保log
库已经被正确添加为依赖,然后更新依赖并重新编译项目。如果问题依旧存在,检查是否有其他依赖或项目配置干扰了log
的正确解析。
What can I say?
答案是Cargo
没学会,这时候我们修改Cargo.toml
,访问log包的文档,可以看到当前的最新版本为0.4.21
,在文件中添加这一段,
[dependencies]
log = "0.4.21"
接下来我们解决error[E0412]: cannot find type 'Yak' in this scope
的问题,这个问题说明Yak
并不是std
里原本有的一个东西,当然也倒逼我们一定要看清楚源代码,这次我们直接读和理解这个报错,说的是在当前scope
即作用域中找不到Yak
,考虑到这个幺蛾子文档的习惯,我们直接在刚才文档的右上角搜索Yak
,
![[Pasted image 20240628015106.png]]
这里我们看到shave it
意为给它剃毛
,考虑原本的代码中的find_a_razor
函数即寻找剃刀
也报错了error[E0425]: cannot find function
find_a_razor in this scope
,那么就很好理解了,Yak
可能是被制造出来的一个例子库,我们尝试在Cargo.toml
中依赖它,
[dependencies]
yak = "0.1.0"
但是很悲剧的是,这个函数并不存在,也就是这段代码纯是一段伪代码,那么我们直接看这段伪代码:
use log::{info, warn};
pub fn shave_the_yak(yak: &mut Yak) {
info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
loop {
match find_a_razor() {
Ok(razor) => {
info!("Razor located: {}", razor);
yak.shave(razor);
break;
}
Err(err) => {
warn!("Unable to locate a razor: {}, retrying", err);
}
}
}
}
假设Yak
是一个struct
,可以它有一个对应的impl
,是shave
,而还有一个函数find_a_razor
是可以返回一个razor
类型或者一个err
类型的函数.那么我们可以手动补全这段代码:
use std::error::Error;
use std::fmt;
use std::sync::Mutex;
#[derive(Debug)]
pub struct Yak
{
yak_name: String,
is_shaved: bool
}
pub struct razor
{
used_time: u32
}
#[derive(Debug)]
pub struct err
{
context: String
}
impl Error for err
{
}
impl fmt::Display for err
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
write!(f, "Error: {}", self.context)
}
}
impl Yak {
pub fn new(yak_name: String) -> Self {
Yak {
yak_name,
is_shaved: false,
}
}
pub fn get_yak_name(&self) -> &str {
&self.yak_name
}
pub fn set_yak_name(&mut self, new_name: String) {
self.yak_name = new_name;
}
pub fn is_shaved(&self) -> bool {
self.is_shaved
}
pub fn shave(&mut self, mut razor: razor)
{
self.is_shaved = true;
razor.used_time+=1;
}
}
impl fmt::Display for razor
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
write!(f, "Razor used time: {}", self.used_time)
}
}
pub fn find_a_razor() -> Result<razor,err>
{
let mut one_razor = razor{used_time:0};
if one_razor.used_time<100
{
one_razor.used_time+=1;
Ok(one_razor)
}
else
{
let the_err:err = err{context:"No razor found".to_string()};
Err(the_err)
}
}
那么这段代码里就含有几个概念没有搞定,补全过程中极尽折磨:
- 枚举类型的泛型
- 分支结构
- Debug输出
枚举类型
这段代码中,Result<razor,err>
是用到了枚举和泛型.
首先Rust
的枚举更像是C
的枚举的增强版本,C
的枚举的几个成员是一个整数,可以按照默认的0-N
来用,一般和switch
结合增强状态机的可读性.但是很明显这样的成员很难满足Rust
的灵活性的需求,尤其是这个场景,又要返回Ok(razor)
类型,又要返回Err(err)
类型.
那么可以理解Rust
的枚举,它不只是为了用一系列代号代表这个类型的所有的可能,而且这个类型可能对应很多种基础类型,也可以被代表.
这样这一段可以理解了,Result<T,E>
是一个枚举类型,源代码我们可以在这里找到:
pub enum Result<T, E> {
Ok(T),
Err(E),
}
可能还是比较难理解,因为这里掺杂了泛型:
pub enum OBJ
{
A(u32),
B(u64)
}
上边这段代码代表OBJ
只可能是A
或者B
,两种情况,但是A
实际上包裹
---可以理解为A
是u32
类型的一个代号,但是不是直接的u32
而是A
携带了这样一个变量,B
也同理.
考虑到使用了泛型,这就意味着我们可以自定义Ok
和Err
各自包裹着什么.
分支结构
这里主要卡住人的是一点,rust
的if
后边的条件是没有括号的.
Debug输出
如果使用了{:?}
这种输出,则需要为这个需要输出的类实现一个Debug
特性,或者使用#[derive(Debug)]
.
Cargo的输出等级
编辑main.rs
之后发现不能输出:
mod yak;
use log::{info, warn};
use yak::{Yak,find_a_razor};
pub fn shave_the_yak(yak: &mut Yak)
{
info!(target: "yak_events", "Commencing yak shaving for {:?}", yak);
loop
{
match find_a_razor()
{
Ok(razor) =>
{
info!("Razor located: {}", razor);
yak.shave(razor);
break;
}
Err(err) =>
{
warn!("Unable to locate a razor: {}, retrying", err);
}
}
}
}
fn main()
{
env_logger::init();
let mut yak = Yak::new("Fred".to_string());
shave_the_yak(&mut yak);
println!("Hello, world!");
}
这里是存在两个问题:
- 错误信息没有输出到工作台
Cargo
的输出没有到info
这个级别
输出到工作台
编辑Cargo.toml
增加一个依赖:
[package]
name = "test_log"
version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4.21"
env_logger = "0.8"
编辑main.rs
,使用env_logger::init();
即可.
修改cargo
的debug
等级
使用这个方法即可:
RUST_LOG=info cargo run
给os
工程适配log
我们首先看题目中给的参考代码:
use core::fmt;
use lazy_static::lazy_static;
use log::{self, Level, LevelFilter, Log, Metadata, Record};
use crate::sync::SpinNoIrqLock as Mutex;
lazy_static! {
static ref LOG_LOCK: Mutex<()> = Mutex::new(());
}
pub fn init() {
static LOGGER: SimpleLogger = SimpleLogger;
log::set_logger(&LOGGER).unwrap();
log::set_max_level(match option_env!("LOG") {
Some("error") => LevelFilter::Error,
Some("warn") => LevelFilter::Warn,
Some("info") => LevelFilter::Info,
Some("debug") => LevelFilter::Debug,
Some("trace") => LevelFilter::Trace,
_ => LevelFilter::Off,
});
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
$crate::logging::print(format_args!($($arg)*));
});
}
#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
/// Add escape sequence to print with color in Linux console
macro_rules! with_color {
($args: ident, $color_code: ident) => {{
format_args!("\u{1B}[{}m{}\u{1B}[0m", $color_code as u8, $args)
}};
}
fn print_in_color(args: fmt::Arguments, color_code: u8) {
use crate::arch::io;
let _guard = LOG_LOCK.lock();
io::putfmt(with_color!(args, color_code));
}
pub fn print(args: fmt::Arguments) {
use crate::arch::io;
let _guard = LOG_LOCK.lock();
io::putfmt(args);
}
struct SimpleLogger;
impl Log for SimpleLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
/*
if let Some(tid) = processor().tid_option() {
print_in_color(
format_args!(
"[{:>5}][{},{}] {}\n",
record.level(),
crate::arch::cpu::id(),
tid,
record.args()
),
level_to_color_code(record.level()),
);
} else {
*/
print_in_color(
format_args!(
"[{:>5}][{},-] {}\n",
record.level(),
crate::arch::cpu::id(),
record.args()
),
level_to_color_code(record.level()),
);
//}
}
fn flush(&self) {}
}
fn level_to_color_code(level: Level) -> u8 {
match level {
Level::Error => 31, // Red
Level::Warn => 93, // BrightYellow
Level::Info => 34, // Blue
Level::Debug => 32, // Green
Level::Trace => 90, // BrightBlack
}
}
我们可以看到参考代码自己实现了一个SimpleLogger
类型,并且给它实现了Log
这个特性,这是起初很让人难以理解的,但是这让我们想到一个细节,即我们实际上使用了env_logger
这个库,用一句看似简单的初始化完成了我们的任务,即把info
等宏和硬件输出联系在一起,所以因此我们也需要自己去实现一个带有Log
特性的SimpleLogger
类,由于我们有了参考源码,实际上的工作反而没有那么困难.
[[012 彩色化LOG#^bf99b4|这里]]实际上又进行了一个简单的Log
特质的实现,因为思路先后问题,写到了下一节.
简单的Log特质的实现
^bf99b4
可以在这里看到Log
特质实现的时候需要实现的方法为enabled
,log
,flush
.并且可以在这里看到一个小的样例,
use log::{Record, Level, Metadata};
struct SimpleLogger;
impl log::Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
可以根据在在这里看到Log
特质实现的时候需要实现的方法,
enabled
方法是传入一个Metadata
的引用返回一个bool
的函数,用于判断当前的这个数据是不是应该被logger
给记录下来,样例里简单地写成了只需要<=Level::Info
即可.log
方法是传入一个Record
,因为Rcord
里存在很多信息,然后对它进行判定,然后按照我们需要的方式来进行日志记录.这里就是直接判断等级是不是够了,然后输出其level
和args
flush
方法:- 在Rust的日志记录系统中,特别是使用
log
库和相关的宏如info!
,warn!
,error!
等进行日志记录时,这些宏会调用日志框架来记录消息。通常情况下,当你的应用程序正常退出时,日志框架会自动处理缓冲区中的日志记录,确保所有未提交的日志条目都被写入到日志目的地,比如控制台或文件。 - 然而,有时候应用程序可能在异常情况下终止,或者日志框架的默认行为可能不足以满足你的需求。在这种情况下,日志框架通常会提供一个方法,允许你显式地触发日志的刷新,以确保所有日志记录都被正确处理。这个方法通常称为
flush
。 - 在这个例子里,因为我们的日志是直接输出到命令行的,因此只需要实现为空即可.
- 在Rust的日志记录系统中,特别是使用
为一个Logger
类实现Log
特质之后,我们可以直接使用info!
等宏来实现输出.
参照参考代码写出来的Logger
如下:
use core::fmt;
use log::{self,Level,LevelFilter,Metadata,Log,Record};
struct SimpleLogger;
impl Log for SimpleLogger
{
fn enabled(&self, metadata: &Metadata) -> bool
{
true
}
fn log(&self, record: &Record)
{
if !self.enabled(record.metadata())
{
return;
}
print_in_color(
format_args!(
"[{:>5}] {}\n",
record.level(),
record.args()
),
level_to_color_code(record.level()),
);
}
fn flush(&self) {}
}
fn print_in_color(args: fmt::Arguments, color_code: u8)
{
println!("\u{1B}[{}m{}\u{1B}[0m", color_code, args);
}
fn level_to_color_code(level: Level) -> u8 {
match level {
Level::Error => 31, // Red
Level::Warn => 93, // BrightYellow
Level::Info => 34, // Blue
Level::Debug => 32, // Green
Level::Trace => 90, // BrightBlack
}
}
pub fn init()
{
static LOGGER: SimpleLogger = SimpleLogger;
log::set_logger(&LOGGER).unwrap();
// log::set_max_level(LevelFilter::Trace);
log::set_max_level(match option_env!("LOG") {
Some("error") => LevelFilter::Error,
Some("warn") => LevelFilter::Warn,
Some("info") => LevelFilter::Info,
Some("debug") => LevelFilter::Debug,
Some("trace") => LevelFilter::Trace,
_ => LevelFilter::Off,
});
}
写出来的main.rs
如下:
// os/src/main.rs
#![no_std]
#![no_main]
#![feature(panic_info_message)]
use core::{arch::global_asm};
use delay::sleep;
use log::{debug, info, error};
#[macro_use]
mod console;
mod sbi;
mod lang_items;
mod delay;
mod logging;
//use sbi::shutdown;
global_asm!(include_str!("entry.asm"));
#[no_mangle]
pub fn rust_main() -> ! {
extern "C" {
fn stext(); // begin addr of text segment
fn etext(); // end addr of text segment
fn srodata(); // start addr of Read-Only data segment
fn erodata(); // end addr of Read-Only data ssegment
fn sdata(); // start addr of data segment
fn edata(); // end addr of data segment
}
clear_bss();
logging::init();
println!("Hello World");
info!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
debug!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
error!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
panic!("Shutdown machine!");
}
fn clear_bss() {
extern "C" {
fn sbss();
fn ebss();
}
(sbss as usize..ebss as usize).for_each(|a| {
unsafe { (a as *mut u8).write_volatile(0) }
});
}
这里只讲关键点:
\u{1B}[{}m{}\u{1B}[0m
里的{}
为占位符,第一个{}
对应的是输出的颜色,第二个是输出的内容format_args!
这个宏是一个非常神奇的宏,可以实现传入一个字符串,然后把占位符用后边传入的参数实现,就像C
里边printf
的格式化和python
里边f"string{var}".
- 在运行
cargo
时,增加env LOG="info"
,可以有效传入环境变量参数,注意不要搞错大小写什么的以至于贻笑大方,这里注意不是在运行qemu
的时候这么做.
[rCore学习笔记 012]彩色化LOG的更多相关文章
- java学习笔记12--国际化
java学习笔记12--国际化 国际化的操作就是指一个程序可以同时适应多门语言,即:如果现在程序者是中国人,则会以中文为显示文字,如果现在程序的使用者是英国人,则会以英语为显示的文字,也就是说可以通过 ...
- CNN学习笔记:池化层
CNN学习笔记:池化层 池化 池化(Pooling)是卷积神经网络中另一个重要的概念,它实际上是一种形式的降采样.有多种不同形式的非线性池化函数,而其中“最大池化(Max pooling)”是最为常见 ...
- Python学习笔记012——装饰器
1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...
- React学习笔记-5-初始化阶段介绍
初始化阶段可以使用的函数:getDefaultProps:只调用一次,实例之间共享引用.只有在组件的第一个实例被初始化的时候,才会调用他,然后react会把这个函数的返回结果保存起来,从第二个实例开始 ...
- Yii2学习笔记:汉化yii,设置表单的描述(属性标签attributeLabels)
一:汉化框架 框架汉化在这里设置,如果不生效,前台后台的配置文件都设置下应该就可以了 二:汉化表单 汉化表单,直接在模型层设置,例如: 原来的联系我们表单 汉化后: ] 这种汉化在哪里修改呢?其实是设 ...
- python自动化学习笔记11-自动化测试UTP框架
前面基本的unittest及ddt已经学过了,现在我们系统把这些知识结合起来,写一个简单的UTP自动化测试框架: 我们先来建基础目录,首先新建一个项目,项目下建父目录UTP,conf目录,用来存放配置 ...
- SAS学习笔记63 如何导出Log
如上,将Log输出,然后又恢复到SAS系统里面的Log,把需要运行的程序放到他们中间就可以了.这种方法不会出现Log打印满了的情况 这种是先输出在SAS系统里面,然后在输出,在SAS里面Log的行是有 ...
- Vue.js官方文档学习笔记(二)组件化应用的构建
组件化应用的构建 组件化应用允许我们使用小型.独立和通常可复用的组件构建大型应用. Vue注册组件 Vue.component('todo-item',{template:'<li>这是个 ...
- Selenium学习笔记之外部化相关测试数据---xml
我们也可以用xml来定义一个信息更为丰富的UIMap.xml文件,比如,额外还添加控件所属的页,控件的类型,然后解析构建一个XMLParser类来读取相应的值. <?xml version=&q ...
- ASP.NET本质论第一章网站应用程序学习笔记3-对象化的Http
在.NET环境下,万物皆对象,在HttpRuntime收到请求之后,立即将通过HttpWorkerRequest传递的参数进行分析和分解,创建方便用于网站应用程序处理用的对象,其中主要涉及到两个对象类 ...
随机推荐
- 最小生成树Kruskal算法的实现原理
到底什么是最小生成树 最小生成树算法应用范围比较广,例如在网络的铺设当中难免会出现环路,需要要生成树算法来取出网络中的环,防止网络风暴的发生.那到底什么是最小生成树呢?我这里就不给严谨的定义了,这种定 ...
- ubuntu18.04最小化安装
ubuntu 18.04虚拟机安装 镜像下载地址: https://releases.ubuntu.com/18.04/ubuntu-18.04.6-live-server-amd64.iso 创建虚 ...
- redis RDB AOF数据持久化
目录 redis RDB持久化[手工持久化]: redis RDB持久化条件配置[适合用于备份]redis rdb持久化策略 redis AOF持久化 redis AOF持久化配置 redis RDB ...
- 7.20考试总结(NOIP模拟21)[Median·Game·Park]
雨滴降落的速度是每秒十米,我该用怎么样的速度,才能将你挽留? 前言 关于语文素养如何限制OI水平2,正好现在文化课巨佬们正在考语文(那我走???) T1 我以为整数是不用输出 .0 的,然后喜挂 30 ...
- Grafana监控系统的构建与实践
本文深入探讨了Grafana的核心技术.数据源集成.仪表盘与可视化构建以及监控与告警配置,旨在为专业从业者提供全面的Grafana技术指南. 关注[TechLeadCloud],分享互联网架构.云服务 ...
- koishi常用插件推荐
今天给大家做一个常用插件的推荐 以下将插件归为几个大类,按类型推荐 1. 日常相关 点歌 插件名:koishi-plugin-music-downloadvoice-api 功能介绍: 语音点歌 - ...
- (二)requests-爬取国家药监局生产许可证数据
首先访问这个页面 url = 'http://125.35.6.84:81/xk/' 我们的目标是抓取这里的每一个企业的详情页数据,但是可以发现这里只有企业的简介信息,所以这就意味着我们要发送两次ge ...
- PasteSpider的集群组件PasteCluster(让你的项目快速支持集群模式)的思路及实现(含源码)
PasteSpider是什么? 一款使用.net编写的开源的Linux容器部署助手,支持一键发布,平滑升级,自动伸缩, Key-Value配置,项目网关,环境隔离,运行报表,差量升级,私有仓库,集群部 ...
- Task - lmdeploy
基础作业: 使用 LMDeploy 以本地对话.网页Gradio.API服务中的一种方式部署 InternLM-Chat-7B 模型,生成 300 字的小故事(需截图
- VMware 虚拟机下载
VMware破解版下载地址: https://www.aliyundrive.com/s/CUr4eaMgxGa 提取码:e0c8 激活码: ZF3R0-FHED2-M80TY-8QYGC-NPKYF ...