Solidity 合约中,如何利用事件与日志机制实现类似printf的功能?

2026-05-29 19:336阅读0评论建站教程
  • 内容介绍
  • 文章标签
  • 相关推荐

嘿, 各位Solidity小伙伴们,你们有没有想过为什么我们在写合约时总爱用 console.log 打印日志,却又发现自己被迫跟随区块链的“黑暗规则”——不能像C++那样随便打印?别担心!今天 我就带你们走进事件与日志机制的大世界,用一份“printf”式的幽默手册,让你在合约里也能轻松打印调试信息,醉了...。

1️⃣ 事件:合约里的“幽灵打印机”

说起事件, 我常把它想象成一台古老而神秘的打印机——只要你给它输入正确格式,它就会把信息送到交易回执里让外界得以窥见内部世界。记住事件是只读的,它不会改变状态,只会产生LOG指令。

《纸上谈兵·solidity》第 9 课:Solidity 事件与日志机制 —— 合约世界的“printf”工具

1.1 语法小贴士

event DebugUint;

event DebugAddr;

在函数里调用:

1.2 indexed 的魔法

如果你想让日志更容易被筛选,就给字段加上indexed关键字吧!举个例子:

alert);

2️⃣ 用日志实现 printf 的三步曲 🎬

A.定义通用调试事件:

类型说明
string msg自定义消息内容,如“开始施行” 或 “错误码 404” 等。
uint val可选数值,比方说计数器当前值。
address adr可选地址,比方说 msg.sender 或目标地址。
注意:不要把所有字段都加上indexed,否则 Gas 成本会上升。

B.插入调试点:就在关键操作前后 emit 调试事件。比如:

function transfer public { // 步骤一:检查余额 emit DebugString; require;

// 步骤二:转账
balance -= _amount;
balance += _amount;
// 步骤三:记录成功
emit DebugString;

}

C.读取 & 分析日志:利用 eth_g 一句话。 etLogs 或前端框架监听事件。比方说:

何必呢? const filter = { address: myContractAddress }; const logs = await provider.getLogs; logs.forEach);

这样, 你就能像 C 的 printf 一样,把变量值实时打印出来。

C‑r‑a‑m‑b‑l‑e!😎 嘶嘶!😱 嘟嘟……我说真的, 我刚才真的听见了区块链里的回声,那种奇怪又酷炫的低频声音——就是 LOG 指令在 EVM 上留下来的痕迹。

3️⃣ “printf”与 Gas 成本 ⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️ ⚠️❗❗❗❗❗❗❗❗ ❌ ❌ ❌ ❌ ❌

~500 gas 请注意:一次性多条 LOG 仍然比写状态快得多, 但如果你把每个变量都单独拆成一个 event 并且全都加上 indexed,那可真是把钱包空掉啦!💸💸💸💸💸💸💸💸💸💸 💰🤑🤑🤑🤑🤑🤑 🏦🏦🏦🏦🏦🏦 🧾🧾🧾🧾🧾
方式 Gas 成本
普通状态写入 ~20000 gas
单个 LOG

🔊 噪音时间!!!🔊 嘀嗒、 嘀嗒、咔咔、咔咔……这就是区块链中 LOG 的节奏感~听着就像打字机敲击键盘一样节拍强烈,一直敲到你头顶发麻。

一下吧:
  • * 用 event 做 printf 就是先声明一个通用调试 event, 再 embed 在代码中,然后用前端或 RPC 读取即可。
  • * 如果要过滤, 可以给常用字段加 index;但别忘了 Gas 成本会翻倍哟~千万别把所有东西都 index 掉!* 日志不占存储空间,也不会被挖矿者打包进去;它们只是交易回执的一部分,是链下可索引的数据。* 当你真正需要持久化状态时还是靠 storage。event 用来做审计、指标收集以及实时监控。* 不要以为你可以在 event 中修改变量, 它只是一个提示,并不改变任何存储内容哦~这种误解可是最常见 bug 吧!😂😂😂😂😂😂😂😂🐛🐛🐛🐛🐛🐛🐛🐛🍃🍃🍃🍃🍃🍃🍃🍃 🍁🍁🍁🍁🍁 🍎 🍎 🍎 🍎 🍏 🍏 🍏 🍏 🌵 🌵 🌵 🌵 🌳 🌳 🌳 🌳🌴🌴🌴🌴🌲🌲🌲🌲🥑🥑🥑🥑🥭🥭🥭🥭 🐱‍👓 😈🤢🤮🤢🤮🤢🤮🤢🤮 🤯 🤯 🤯 🤯 🤯 🤯 🤯 🤯 🚨 🚨 🚨 🚨 🚨 🚨🚨🚨🚨🚨🚨 🚓 🚓 🚓🚓🚓 🚔 🚔🚔🚔

    👀 状态树 快速抢先看一眼 🔍✨✨✨✨✨✨✨✨✨ ✈✈✈✈✈ ✋✋✋✋✋ ✊✊✊✊✊ ✩★★★ ★★★★ ★★★★★ ★★★★★ ★★★★★★ ★★★★★★★ ★★★★★★★★ ★★★★★★★★★ ★★★★★★★★★★

    Mimicking printf 在 Solidity 中其实跟状态树没啥直接关系, 但了解它能帮你更好地理解为何日志很轻量 —— 主要原因是 Log 数据只会写入交易回执,而不是存储在 State Trie 上。这意味着即使你的合约每天收到千百笔交易,也不会主要原因是大量日志导致 State Trie 巨大而爆炸性增长,火候不够。。

    记住 当我们说“Event 可以轻量级记录”,这就是主要原因是它完全不影响 State Trie 的大小,只是附着在 Transaction Receipt 上,而 Transaction Receipt 又是可以被废弃/清理掉的数据结构哦!🙇‍♂️🙇‍♀️🙇‍♂️🙇‍♀️ 🙄🙄🙄🙄🙄🙄 🙆‍♂️ 🙆‍♀️ 🙆‍♂︎ 🙆‍♀︎ 💬 💬 💬 💬 💬 💬 🎤 🎤 🎤 🎤 🎤🎤🎤🎤🎤🎤 🎼 🎼🎼🎼🎼🎼 🎹  😰😱😠 😠😠😠😠 😩 😩 😩 😩 😩 😩 😖 😖😖😖😭😭😭😭😭😭😭😭😭😭         ‽‽‽ ‽‽‽ ‽‽ ‽ ‽ ‽ ‽ ‽‿         **`_/¯` `¯\__/¯` ` ¯\__/¯ ` `¯\__/¯` **​** ​ **​** **​** **​** ​ ` ¯\__/¯ ` **​** ​ **​** ​ **​** ​ ‌‌ ‌‌ ‌‌ ‌‌ ‌**​** ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^)    ***请认真阅读完此段后再继续往下看!!!***   ****   * * * * * * * * *   * # # # # # # #   * @ @ @ @ @ @ @   * $ $ $ $ $ $ $   * & & & & & & &   * ! ! ! ! ! ! !
    简易版 State Trie 对比表格
    区块号 事务数 StateTrie 大小 Log 数据大小
    1000 10k ≈80 MB ≈5 MB
                                 
     
    font-family:'Arial','Helvetica',sans-serif'; font-size:"30px"; font-weight:bold; font-style:"normal"; font-stretch:"normal"; font-variant:"small-caps"; color:#ffffff'; height:"auto"; line-height:"30px"; margin-bottom:"40px"; padding-left:"10px'; height:'auto';width:'auto';color:'black';background:'#000000'; color:'white';

嘿, 各位Solidity小伙伴们,你们有没有想过为什么我们在写合约时总爱用 console.log 打印日志,却又发现自己被迫跟随区块链的“黑暗规则”——不能像C++那样随便打印?别担心!今天 我就带你们走进事件与日志机制的大世界,用一份“printf”式的幽默手册,让你在合约里也能轻松打印调试信息,醉了...。

1️⃣ 事件:合约里的“幽灵打印机”

说起事件, 我常把它想象成一台古老而神秘的打印机——只要你给它输入正确格式,它就会把信息送到交易回执里让外界得以窥见内部世界。记住事件是只读的,它不会改变状态,只会产生LOG指令。

《纸上谈兵·solidity》第 9 课:Solidity 事件与日志机制 —— 合约世界的“printf”工具

1.1 语法小贴士

event DebugUint;

event DebugAddr;

在函数里调用:

1.2 indexed 的魔法

如果你想让日志更容易被筛选,就给字段加上indexed关键字吧!举个例子:

alert);

2️⃣ 用日志实现 printf 的三步曲 🎬

A.定义通用调试事件:

类型说明
string msg自定义消息内容,如“开始施行” 或 “错误码 404” 等。
uint val可选数值,比方说计数器当前值。
address adr可选地址,比方说 msg.sender 或目标地址。
注意:不要把所有字段都加上indexed,否则 Gas 成本会上升。

B.插入调试点:就在关键操作前后 emit 调试事件。比如:

function transfer public { // 步骤一:检查余额 emit DebugString; require;

// 步骤二:转账
balance -= _amount;
balance += _amount;
// 步骤三:记录成功
emit DebugString;

}

C.读取 & 分析日志:利用 eth_g 一句话。 etLogs 或前端框架监听事件。比方说:

何必呢? const filter = { address: myContractAddress }; const logs = await provider.getLogs; logs.forEach);

这样, 你就能像 C 的 printf 一样,把变量值实时打印出来。

C‑r‑a‑m‑b‑l‑e!😎 嘶嘶!😱 嘟嘟……我说真的, 我刚才真的听见了区块链里的回声,那种奇怪又酷炫的低频声音——就是 LOG 指令在 EVM 上留下来的痕迹。

3️⃣ “printf”与 Gas 成本 ⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️ ⚠️❗❗❗❗❗❗❗❗ ❌ ❌ ❌ ❌ ❌

~500 gas 请注意:一次性多条 LOG 仍然比写状态快得多, 但如果你把每个变量都单独拆成一个 event 并且全都加上 indexed,那可真是把钱包空掉啦!💸💸💸💸💸💸💸💸💸💸 💰🤑🤑🤑🤑🤑🤑 🏦🏦🏦🏦🏦🏦 🧾🧾🧾🧾🧾
方式 Gas 成本
普通状态写入 ~20000 gas
单个 LOG

🔊 噪音时间!!!🔊 嘀嗒、 嘀嗒、咔咔、咔咔……这就是区块链中 LOG 的节奏感~听着就像打字机敲击键盘一样节拍强烈,一直敲到你头顶发麻。

一下吧:
  • * 用 event 做 printf 就是先声明一个通用调试 event, 再 embed 在代码中,然后用前端或 RPC 读取即可。
  • * 如果要过滤, 可以给常用字段加 index;但别忘了 Gas 成本会翻倍哟~千万别把所有东西都 index 掉!* 日志不占存储空间,也不会被挖矿者打包进去;它们只是交易回执的一部分,是链下可索引的数据。* 当你真正需要持久化状态时还是靠 storage。event 用来做审计、指标收集以及实时监控。* 不要以为你可以在 event 中修改变量, 它只是一个提示,并不改变任何存储内容哦~这种误解可是最常见 bug 吧!😂😂😂😂😂😂😂😂🐛🐛🐛🐛🐛🐛🐛🐛🍃🍃🍃🍃🍃🍃🍃🍃 🍁🍁🍁🍁🍁 🍎 🍎 🍎 🍎 🍏 🍏 🍏 🍏 🌵 🌵 🌵 🌵 🌳 🌳 🌳 🌳🌴🌴🌴🌴🌲🌲🌲🌲🥑🥑🥑🥑🥭🥭🥭🥭 🐱‍👓 😈🤢🤮🤢🤮🤢🤮🤢🤮 🤯 🤯 🤯 🤯 🤯 🤯 🤯 🤯 🚨 🚨 🚨 🚨 🚨 🚨🚨🚨🚨🚨🚨 🚓 🚓 🚓🚓🚓 🚔 🚔🚔🚔

    👀 状态树 快速抢先看一眼 🔍✨✨✨✨✨✨✨✨✨ ✈✈✈✈✈ ✋✋✋✋✋ ✊✊✊✊✊ ✩★★★ ★★★★ ★★★★★ ★★★★★ ★★★★★★ ★★★★★★★ ★★★★★★★★ ★★★★★★★★★ ★★★★★★★★★★

    Mimicking printf 在 Solidity 中其实跟状态树没啥直接关系, 但了解它能帮你更好地理解为何日志很轻量 —— 主要原因是 Log 数据只会写入交易回执,而不是存储在 State Trie 上。这意味着即使你的合约每天收到千百笔交易,也不会主要原因是大量日志导致 State Trie 巨大而爆炸性增长,火候不够。。

    记住 当我们说“Event 可以轻量级记录”,这就是主要原因是它完全不影响 State Trie 的大小,只是附着在 Transaction Receipt 上,而 Transaction Receipt 又是可以被废弃/清理掉的数据结构哦!🙇‍♂️🙇‍♀️🙇‍♂️🙇‍♀️ 🙄🙄🙄🙄🙄🙄 🙆‍♂️ 🙆‍♀️ 🙆‍♂︎ 🙆‍♀︎ 💬 💬 💬 💬 💬 💬 🎤 🎤 🎤 🎤 🎤🎤🎤🎤🎤🎤 🎼 🎼🎼🎼🎼🎼 🎹  😰😱😠 😠😠😠😠 😩 😩 😩 😩 😩 😩 😖 😖😖😖😭😭😭😭😭😭😭😭😭😭         ‽‽‽ ‽‽‽ ‽‽ ‽ ‽ ‽ ‽ ‽‿         **`_/¯` `¯\__/¯` ` ¯\__/¯ ` `¯\__/¯` **​** ​ **​** **​** **​** ​ ` ¯\__/¯ ` **​** ​ **​** ​ **​** ​ ‌‌ ‌‌ ‌‌ ‌‌ ‌**​** ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^)    ***请认真阅读完此段后再继续往下看!!!***   ****   * * * * * * * * *   * # # # # # # #   * @ @ @ @ @ @ @   * $ $ $ $ $ $ $   * & & & & & & &   * ! ! ! ! ! ! !
    简易版 State Trie 对比表格
    区块号 事务数 StateTrie 大小 Log 数据大小
    1000 10k ≈80 MB ≈5 MB
                                 
     
    font-family:'Arial','Helvetica',sans-serif'; font-size:"30px"; font-weight:bold; font-style:"normal"; font-stretch:"normal"; font-variant:"small-caps"; color:#ffffff'; height:"auto"; line-height:"30px"; margin-bottom:"40px"; padding-left:"10px'; height:'auto';width:'auto';color:'black';background:'#000000'; color:'white';