505 错误演示:课堂讲义

🎯 本课目标

学生将理解:


📋 演示概要 (15-20 分钟)

第一部分:引入 (2 分钟)

问题启发:

“想象你是运维工程师。一个开发人员说:’我的网站返回 500 错误了,不知道什么原因。能帮我看看吗?’ 你需要怎么做?”

关键点:


🔴 第二部分:制造错误 (3 分钟)

背景知识

基本流程:
用户请求 → Web 服务器 → 应用逻辑 → 数据库
              ↓ 
          如果任何一步失败 → 500 Error

对于 Jekyll:

git commit → 代码变更 → Jekyll 构建 → 渲染模板
                        ↓
                    Liquid 引擎失败 → 500 Error Tell

演示操作

# 我们将修改模板文件,引入一个语法错误
vim _layouts/ec440.html

# 找到这一行:
# {\% endif \%}

# 注释掉(或删除)→ Liquid 模板不完整
# 这会导致构建失败

讲解重点:


🔍 第三部分:观察症状 (2 分钟)

错误显示

学生看到的:

浏览器:

500 Internal Server Error
The server encountered an unexpected condition that prevented it from fulfilling the request.

服务器日志:

Configuration file: /path/_config.yml
Generating...
  Liquid Exception: Syntax Error in 'ec440.html'  
                    line 42: 'endif' expected
                    
jekyll 4.2.0 | Error: Syntax Error

讨论点

  1. 用户看到什么?
    • 只看到 500 错误
    • 不知道发生了什么
  2. 运维人员看到什么?
    • 详细的日志
    • 具体的错误位置

🔎 第四部分:诊断 (5 分钟)

第一步:获取完整日志

# 使用 --trace 标志
bundle exec jekyll build --trace

日志输出示例:

Generating...
  Liquid Exception: Syntax Error in '_layouts/ec440.html', line 42
    Expected 'endif' in 'if' block started on line 38
    
Traceback (most recent call last):
  ...liquid/tags/if.rb:29:in `end_tag'
  ...liquid/lexer.rb:125:in `parse'
  ...

关键信息:

第二步:检查文件

$ cat _layouts/ec440.html | head -45 | tail -10
36  {\% if page.toc \%}
37    <div class="toc">
38      <h1 id="ec528-课堂---505-错误演示参考卡打印用">EC528 课堂 - 505 错误演示参考卡(打印用)</h1>

<h2 id="演示操作流程单页版本">【演示操作流程】单页版本</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──────────────────────────────────────────────────────────────┐
│  第一步:制造错误 (2 min)                                     │
│  ─────────────────────────────────────────────────────────── │
│  $ vim _layouts/ec440.html                                   │
│  找到最后一行的 {\% endif \%}                                  │
│  ❌ 改成: {\% comment \%} missing {\% endcomment \%}             │
│  保存: :wq                                                    │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  第二步:观察错误 (2 min)                                     │
│  ─────────────────────────────────────────────────────────── │
│  $ bundle exec jekyll serve                                  │
│  看到: ❌ Liquid Exception: ... 'endif' expected             │
│  按 Ctrl+C 停止                                              │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  第三步:诊断问题 (3 min)                                     │
│  ─────────────────────────────────────────────────────────── │
│  $ bundle exec jekyll build --trace                          │
│  查看输出中的三个关键信息:                                   │
│    1️⃣  错误类型: Liquid Exception                            │
│    2️⃣  文件位置: _layouts/ec440.html, line 42               │
│    3️⃣  问题描述: Expected 'endif'                           │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  第四步:查看代码上下文 (2 min)                               │
│  ─────────────────────────────────────────────────────────── │
│  $ sed -n '38,45p' _layouts/ec440.html                       │
│  输出:                                                        │
│    38  {\% if page.toc \%}                                     │
│    39    &lt;div&gt;                                               │
│    40      ...                                               │
│    41    &lt;/div&gt;                                              │
│    42  {\% comment \%} ERROR HERE {\% endcomment \%} ❌          │
│    43                                                         │
│    44  &lt;footer&gt;                                              │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  第五步:修复错误 (2 min)                                     │
│  ─────────────────────────────────────────────────────────── │
│  $ vim _layouts/ec440.html                                   │
│  改回: {\% endif \%}                                           │
│  保存: :wq                                                    │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│  第六步:验证修复 (2 min)                                     │
│  ─────────────────────────────────────────────────────────── │
│  $ bundle exec jekyll build                                  │
│  看到: ✅ ... done in 2.345 seconds                          │
│  没有错误 = 成功!                                            │
│  访问 http://localhost:4000 确认网站正常                    │
└──────────────────────────────────────────────────────────────┘
</code></pre></div></div>

<hr />

<h2 id="日志分析秘诀关键词检查法">【日志分析秘诀】关键词检查法</h2>

<table>
  <thead>
    <tr>
      <th>寻找这些…</th>
      <th>意思</th>
      <th>怎么做</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Liquid Exception</code></td>
      <td>模板出错</td>
      <td>检查 Liquid 标签</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">line XXX</code></td>
      <td>在哪一行</td>
      <td>打开文件 +XXX</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">in 'xxx.html'</code></td>
      <td>在哪个文件</td>
      <td>vim xxx.html</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">YAML error</code></td>
      <td>配置错误</td>
      <td>检查缩进</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Gem::LoadError</code></td>
      <td>依赖错误</td>
      <td>bundle install</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Traceback</code></td>
      <td>完整错误链</td>
      <td>从下往上读</td>
    </tr>
  </tbody>
</table>

<p><strong>黄金法则:</strong></p>
<ul>
  <li>最后一行 = 具体错误</li>
  <li>倒数第二行 = 在哪里</li>
  <li>之前的 = 背景信息</li>
</ul>

<hr />

<h2 id="常见-jekyll-错误速查表">【常见 Jekyll 错误】速查表</h2>

<h3 id="-liquid-exception-syntax-error-in--line-x">❌ Liquid Exception: Syntax Error [in ‘…’ line X]</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>原因: Liquid 模板标签未闭合或拼写错误
检查:  ✓ {\% if \%} 对应 {\% endif \%} 吗?
      ✓ {\% for \%} 对应 {\% endfor \%} 吗?
      ✓ 过滤器名称对吗?
修复: vim [文件] +[行号]
</code></pre></div></div>

<h3 id="-yaml-syntax-error-in-_configyml">❌ YAML syntax error in _config.yml</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>原因: YAML 格式错误(缺引号、缩进、特殊字符)
检查: ✓ 引号成对了吗? ("..."  或 '...')
     ✓ 缩进统一了吗? (都用 2 个空格)
     ✓ 有特殊字符吗? (冒号、#...需要加引号)
修复: vim _config.yml
</code></pre></div></div>

<h3 id="-gemloaderror--bundler-command-not-found">❌ Gem::LoadError / bundler: command not found</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>原因: 缺少或冲突的 Ruby 包
检查: ✓ bundle check
     ✓ bundle install
修复: bundle install &amp;&amp; bundle update
</code></pre></div></div>

<h3 id="-errnoenoent-no-such-file-or-directory">❌ Errno::ENOENT: No such file or directory</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>原因: 配置中的路径不存在
检查: ✓ ls -la [路径]
     ✓ _config.yml 中什么路径出错了?
修复: 修改配置或创建目录
</code></pre></div></div>

<hr />

<h2 id="快速命令速查">【快速命令速查】</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安全地尝试构建(显示所有错误)</span>
bundle <span class="nb">exec </span>jekyll build <span class="nt">--trace</span> 2&gt;&amp;1 | <span class="nb">head</span> <span class="nt">-50</span>

<span class="c"># 启动本地服务(用于测试)</span>
bundle <span class="nb">exec </span>jekyll serve <span class="nt">--force-polling</span>

<span class="c"># 清理并重新构建(解决诡异问题)</span>
bundle <span class="nb">exec </span>jekyll clean <span class="o">&amp;&amp;</span> bundle <span class="nb">exec </span>jekyll build <span class="nt">--trace</span>

<span class="c"># 只看错误信息</span>
bundle <span class="nb">exec </span>jekyll build 2&gt;&amp;1 | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"Error|Exception"</span>

<span class="c"># 验证 YAML 文件</span>
ruby <span class="nt">-ryaml</span> <span class="nt">-e</span> <span class="s1">'YAML.load_file("_config.yml")'</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"✅ OK"</span>

<span class="c"># 看某个文件的特定行</span>
<span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'38,45p'</span> _layouts/ec440.html

<span class="c"># 查看最近的改动</span>
git diff HEAD~1 _layouts/ec440.html

<span class="c"># 恢复文件到上一个版本</span>
git checkout _layouts/ec440.html
</code></pre></div></div>

<hr />

<h2 id="学生自检清单遇到错误时">【学生自检清单】遇到错误时</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />1. 复制完整错误消息</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />2. 找到 “Error:” 或 “Exception:” 这一行</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />3. 记住文件名和行号</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />4. 打开文件看那一行周围</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />5. 问自己:”最近我改了什么?”</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />6. 用 git diff 查看改动</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />7. 根据错误类型查表</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />8. 尝试修复</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />9. 重新构建验证</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />10. 成功后思考:”为什么会这样?”</li>
</ul>

<hr />

<h2 id="讲师笔记演示时间表">【讲师笔记】演示时间表</h2>

<table>
  <thead>
    <tr>
      <th>时间</th>
      <th>活动</th>
      <th>注意</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0:00-1:00</td>
      <td>问题启发</td>
      <td>“遇到 500 怎么办?”</td>
    </tr>
    <tr>
      <td>1:00-4:00</td>
      <td>制造错误</td>
      <td>让学生看着</td>
    </tr>
    <tr>
      <td>4:00-6:00</td>
      <td>启动服务看错误</td>
      <td>指出 Exception 关键词</td>
    </tr>
    <tr>
      <td>6:00-9:00</td>
      <td>–trace 诊断</td>
      <td>讲解三个关键信息</td>
    </tr>
    <tr>
      <td>9:00-11:00</td>
      <td>查看代码</td>
      <td>定位问题行</td>
    </tr>
    <tr>
      <td>11:00-14:00</td>
      <td>修复并验证</td>
      <td>让学生看到修复过程</td>
    </tr>
    <tr>
      <td>14:00-18:00</td>
      <td>讨论和Q&amp;A</td>
      <td>思考题</td>
    </tr>
  </tbody>
</table>

<p><strong>总时长: 18 分钟</strong> (可调整)</p>

<hr />

<h2 id="思考题课堂讨论用">【思考题】课堂讨论用</h2>

<ol>
  <li>如果这个错误在生产环境,用户会看到什么?</li>
  <li>日志中最有用的一条信息是?</li>
  <li>怎样才能预防这个错误?</li>
  <li>CI/CD 怎么帮助我们提早发现这个问题?</li>
  <li>如果有 10 000 个访问者同时遇到这个错误?</li>
</ol>

<hr />

<h2 id="黄金法则">【黄金法则】</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃  永远首先查看日志,日志里有所有答案!   ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

诊断错误的 4 步:
  1️⃣  获取完整日志信息
  2️⃣  定位文件和行号
  3️⃣  查看代码上下文
  4️⃣  对比之前的版本

修复问题的 3 步:
  1️⃣  理解问题原因
  2️⃣  做出改动
  3️⃣  验证修复有效
</code></pre></div></div>

<hr />

<table>
  <tbody>
    <tr>
      <td><strong>⏰ 打印这页贴在办公室!</strong></td>
      <td><strong>💾 保存 DEBUGGING-CHEATSHEET.md 供以后参考</strong></td>
    </tr>
  </tbody>
</table>

39    </div>
40  {\% comment \%} missing endif {\% endcomment \%}  ← 问题在这里
41  
42  <footer>

分析:

第三步:对比检查

# 查看 git diff
git diff _layouts/ec440.html

# 或者与上一个工作版本比较
diff _layouts/ec440.html.backup _layouts/ec440.html

结果:

- {\% endif \%}
+ {\% comment \%} missing endif {\% endcomment \%}

🔧 第五部分:修复 (3 分钟)

修复步骤

# 1. 编辑文件
vim _layouts/ec440.html

# 2. 找到问题行
# 把这行:
# {\% comment \%} missing endif {\% endcomment \%}
# 改回:
# {\% endif \%}

# 3. 保存文件
# :wq

验证修复

# 重新构建
bundle exec jekyll build

# 预期输出:
#   Generating...
#   done in 2.345 seconds.
#
# 没有错误消息 = 成功!

✅ 第六部分:恢复服务 (2 分钟)

# 重启服务器
bundle exec jekyll serve

# 访问网站
# http://localhost:4000

验证:


📊 关键学习成果

1. 错误诊断流程

遇见 500 错误
      ↓
查看服务器日志
      ↓
识别错误类型(Liquid/YAML/Gem/etc)
      ↓
定位文件和行号
      ↓
检查代码上下文
      ↓
想到可能的原因
      ↓
修复
      ↓
测试验证

2. 常见的 500 错误原因

原因 症状 诊断 修复
Liquid 模板错误 Liquid Exception: Syntax Error 查看行号 修复标签
YAML 配置错误 YAML syntax error 验证缩进 修改格式
缺失的 gem Gem::LoadError 检查 Gemfile 运行 bundle install
权限错误 Permission denied 检查文件权限 chmod 644
磁盘满 Write error 检查磁盘 清理空间

3. 日志阅读技能

关键词扫描:

信息优先级:

  1. 最后一行(具体错误信息)
  2. 行号(精确位置)
  3. 堆栈跟踪(连锁反应)

🎓 课后思考题

给学生的讨论问题

  1. 如果在生产环境遇到这个错误,用户会看到什么?
    • 404 页面?
    • 错误页面?
    • 空白页面?
  2. 如何防止这个错误上线?
    • CI/CD 测试?
    • 代码审查?
    • 本地测试?
  3. 如果有 1000 个访问者同时遇到 500 错误,会发生什么?
    • 服务器是否会崩溃?
    • 日志是否会爆炸?
    • 如何恢复?
  4. 在 Django/Flask/Rails 中,错误诊断会不同吗?
    • 日志格式是否不同?
    • 调试工具有哪些?
  5. CDN 会如何影响这个错误?
    • CDN 是否会缓存 500 错误?
    • 用户会看到多久的旧页面?

💡 进阶扩展

变体演示(可选)

演示 2:YAML 配置错误

# 在 _config.yml 中引入错误
title: "EC528  # 缺少闭合引号

演示 3:Gem 依赖冲突

# 在 Gemfile 中
gem "jekyll", "3.9"  # 太旧
gem "minimal-mistakes-jekyll", "4.24"  # 需要 Jekyll 4.x

📺 课堂演示检查清单


🚀 配合的命令速度表

# 快速制造错误
sed -i '' 's/{\% endif \%}/<!-- BROKEN -->/g' _layouts/ec440.html

# 快速查看错误
bundle exec jekyll build 2>&1 | head -20

# 快速恢复备份
git checkout _layouts/ec440.html

# 快速验证修复
bundle exec jekyll build && echo "✅ Success"

📚 参考资源


讲师贴士: 这个演示最强大的部分是让学生 亲自 经历错误诊断过程,而不仅仅是看你操作。考虑让每个学生修复一个故意制造的不同错误。