diff --git a/docs/utils/fmt.md b/docs/utils/fmt.md
new file mode 100644
index 0000000000000000000000000000000000000000..28a488c4bb613c40077a2008117141868caf91f5
--- /dev/null
+++ b/docs/utils/fmt.md
@@ -0,0 +1,63 @@
+# 格式化输出
+
+## Writer 虚类
+
+```cpp
+class Writer
+{
+public:
+    virtual void write(Slice<const char> s) = 0;
+    void write(char c) {
+        write(Slice<const char>(&c, 1));
+    }
+};
+```
+
+我们定义了一个虚类 `Writer` , 只要实现了相应的接口 `write` 就能使用格式化输出的功能,利用 C++ 的模板,类似于 `printf, sprintf, fprintf` 的功能均可实现。目前默认的输出使用的是 `Sbi::putchar` 未来可能替换为 `Uart::putchar` .
+
+## fmt::print
+
+```cpp
+template <typename T, typename ...Ts>
+void print(Writer *writer, Slice<const char> fmt, const T &arg, const Ts &...args)
+```
+
+`print` 函数接受一个 `Writer` , 一个格式化字符串 `fmt` , 以及相应参数。当在格式化字符串中遇到 `{` 时,若还有一个 `{` 紧跟着,则输出一个 `{` ,否则扫描到 `}` 为止,根据参数的类型调用相应的格式化函数。
+
+对每种需要格式化的类型应当实例化 `fmt::format` 并实现静态成员函数
+
+```cpp
+static void parse(const T &value, Slice<const char> fmt, format_options options, Writer *writer);
+```
+
+此处的 `fmt` 是 `{}` 之间的内容,`format_options` 目前为空结构,未来可能用于类型无关的格式化字符串解析(例如对齐)。
+
+## fmt::log
+
+```cpp
+enum class LogLevel {
+    Verbose,
+    Debug,
+    Info,
+    Warn,
+    Err,
+    Never,
+};
+
+
+template <typename ...Ts>
+static inline void log(LogLevel level, Writer *writer, Slice<const char> fmt, const Ts &...args)
+{
+    if (level >= log_level) {
+        if (level <= LogLevel::Debug) writer->write("\033[1;30m");
+        if (level == LogLevel::Warn) writer->write("\033[1;35m");
+        if (level == LogLevel::Err) writer->write("\033[1;31m");
+        print(writer, fmt, args...);
+        if (level != LogLevel::Info) writer->write("\033[0m");
+    }
+}
+```
+
+`log` 函数在 `print` 函数的基础上增加了根据 `LogLevel` 决定是否输出,并自动设置颜色以便快速找到更为重要的信息。
+
+