<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>taojay315 (Jerry)</title>
    <link>https://ruby-china.org/taojay315</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>ngx_mruby Benchmark</title>
      <description>&lt;p&gt;看来还是要简单说明一下，要不然我看所有人都要纠结到我的测试准不准上去了。&lt;/p&gt;

&lt;p&gt;这个测试并不是要搞个大新闻，哇 ruby 新出了一个性能提升 10 倍，并不是这样，没有银弹。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mruby 跟 ruby 完全不是一回事，放在一起只是对照&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mruby 跟 ruby 完全不是一回事，放在一起只是对照&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mruby 跟 ruby 完全不是一回事，放在一起只是对照&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;测试肯定不准，sinatra 和 rack 配置一下性能会更好，nginx 配置一下性能也更好，不过可以参考官方 benchmark，不在一个数量级上是肯定的。这个结果只是为高并发的工具箱里加一个工具。&lt;/p&gt;

&lt;p&gt;比如一个项目压力很大，但是可能只有那么一两个请求压力大，以前的解决方案可能是拆项目，换语言等方式解决。现在可以考虑把压力大但是不复杂的请求用 mruby 实现一下然后做个微服务出来。&lt;/p&gt;

&lt;p&gt;mruby 跟 ruby 不是一回事不仅仅是运行方式不一致，支持也不一致，至少目前用 mruby 处理复杂业务是不太现实的。每次引入新的 gem 或者有其他底层修改都需要重新编译 nginx。&lt;/p&gt;

&lt;p&gt;========================&lt;/p&gt;

&lt;p&gt;用例：在 redis set 里面 spop 一条返回 json。&lt;/p&gt;

&lt;p&gt;测试：ab -c100 -n15000 &lt;a href="http://localhost:9292/" rel="nofollow" target="_blank"&gt;http://localhost:9292/&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# sinatra

Requests per second:    1020.89 [#/sec] (mean)
Time per request:       97.953 [ms] (mean)

# Only rack

Requests per second:    1020.89 [#/sec] (mean)
Time per request:       97.953 [ms] (mean)

#  ngx_mruby

Requests per second:    14339.08 [#/sec] (mean)
Time per request:       6.974 [ms] (mean)
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;不准确，只有参照价值。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;mruby 跟 ruby 完全不是一回事，放在一起只是做个对照，mruby 现阶段的直面对手应该是 lua.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>taojay315</author>
      <pubDate>Fri, 18 Mar 2016 12:06:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/29381</link>
      <guid>https://ruby-china.org/topics/29381</guid>
    </item>
    <item>
      <title>Ruby Hook Methods</title>
      <description>&lt;p&gt;Hook Methods 一般译为钩子方法，可以理解为当触发了某个条件执行的方法。例如我们常见的 method_missing。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method 相关&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;respond_to_missing?: 当尝试查看一个 missing 的方法时执行。&lt;/li&gt;
&lt;li&gt;method_missing: 当调用一个不存在的方法时执行。&lt;/li&gt;
&lt;li&gt;method_added: 当定义一个方法时执行&lt;/li&gt;
&lt;li&gt;method_removed: 当一个方法被移除时执行&lt;/li&gt;
&lt;li&gt;singleton_method_added: 当添加一个 singleton 方法时执行。&lt;/li&gt;
&lt;li&gt;singleton_method_removed: 当一个 singleton 方法被移除时执行。&lt;/li&gt;
&lt;li&gt;method_undefined: 当一个方法被 undefined 的时候执行，undef 和 remove 的区别在于当你在子类中你可以 undef 掉从父类继承而来的方法，而 remove 则不可以删除定义在父类中的方法。在父类中无论是使用 undef 和 remove 子类都无法继续使用这些方法。&lt;/li&gt;
&lt;li&gt;singleton_method_undefined: 当一个 singleton 方法被 undefined 时刻执行。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;示例代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# respond_to_missing?&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;respond_to_missing?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:missing_method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 这个跟重写respond_to?的区别主要在于使用下面这个方法&lt;/span&gt;
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:missing_method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; #&amp;lt;Method: Base#missing_method&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;# method_missing&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;missing_method&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method&lt;/span&gt;

&lt;span class="c1"&gt;# method_added&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method_added&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;missing_method&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method&lt;/span&gt;
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;missing_method2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method2&lt;/span&gt;

&lt;span class="c1"&gt;# method_removed&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method_removed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;    
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;remove_method&lt;/span&gt; &lt;span class="ss"&gt;:missing_method&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method&lt;/span&gt;

&lt;span class="c1"&gt;# method_undefined&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method_undefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;    
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;undef_method&lt;/span&gt; &lt;span class="ss"&gt;:missing_method&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method&lt;/span&gt;

&lt;span class="c1"&gt;# singleton_method_added/removed/undefined&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_method_added&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;missing_method&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;missing_method1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;missing_method2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# =&amp;gt; singleton_method_added&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method1&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; missing_method2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Class/Object相关&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inherited: 当被继承时执行。&lt;/li&gt;
&lt;li&gt;initialize_copy: 当调用 initialize_clone 和 initialize_dup 时执行。&lt;/li&gt;
&lt;li&gt;initialize_dup: 当调用 dup，返回 dup 结果前时执行。&lt;/li&gt;
&lt;li&gt;initialize_clone: 当调用 clone，进行 frozen 判断和返回 clone 结果之前执行。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;介绍这几个方法之前我们先要了解一下 dup 和 clone 的区别，主要有以下两点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dup 不会复制 frozen 状态，clone 会。&lt;/li&gt;
&lt;li&gt;dup 不会复制 singleton class，clone 会。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# inherited&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inherited&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sub&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Base&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# =&amp;gt; Sub&lt;/span&gt;

&lt;span class="c1"&gt;# initialize_*&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'initialize_copy'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_dup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'initialize_dup'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_clone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'initialize_clone'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;    
&lt;span class="c1"&gt;# =&amp;gt; initialize_dup                  &lt;/span&gt;
&lt;span class="c1"&gt;# ＝&amp;gt; initialize_copy&lt;/span&gt;
&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;                      
&lt;span class="c1"&gt;# =&amp;gt; initialize_copy&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; initialize_clone&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Modules 相关&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;included: 当 include 时执行。&lt;/li&gt;
&lt;li&gt;append_features: 当 include 时执行。&lt;/li&gt;
&lt;li&gt;prepended: 2.0 新增 prepend 的时候执行。&lt;/li&gt;
&lt;li&gt;prepend_features：跟上面的 append_feature 可以一同理解。&lt;/li&gt;
&lt;li&gt;extend_object: 当被 extend 时执行。&lt;/li&gt;
&lt;li&gt;extended: 当被 extend 时执行。&lt;/li&gt;
&lt;li&gt;const_missing: 当有常量丢失时执行。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;首先来说，对于 include，prepend 这两者和 extend 的关系很容易区分。include 和 prepend 的区别在于对祖先链的修改方式，进一步影响方法的查找流程。&lt;/p&gt;

&lt;p&gt;参考下面的例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;M&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Hello from M'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;N&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Hello from N'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;C&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;N&lt;/span&gt;
  &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'hello from C'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; Hello from M&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; hello from C&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; Hello from N&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 included 和 append_features 是一组，prepended 和 prepend_features 是一组，extended 和 extend_object 是一组。&lt;/p&gt;

&lt;p&gt;以 included 和 append_features 来说，append_features 这个方法是真正把 module 里的代码混入到 include 此 module 的地方，而 included 仅仅是在完成混入之后执行的一个回调，下面是一个伪示例：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;M&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_features&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_method&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'say hi!'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'included'&lt;/span&gt;
    &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_method&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;const_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 当常量丢失时执行&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;const_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;MissingConst&lt;/span&gt; &lt;span class="c1"&gt;# 访问不存在常量&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; MissingConst&lt;/span&gt;
&lt;span class="no"&gt;M&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MissingPerson&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; MissingPerson # 访问不存在常量&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于 prepend 和 extend 两组来说也是如此。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marshal 相关&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;marshal_dump: 当 Marshal.dump(&lt;a href="/obj" class="user-mention" title="@obj"&gt;&lt;i&gt;@&lt;/i&gt;obj&lt;/a&gt;) 时执行。&lt;/li&gt;
&lt;li&gt;marshal_load: 当 Marshal.load(&lt;a href="/obj" class="user-mention" title="@obj"&gt;&lt;i&gt;@&lt;/i&gt;obj&lt;/a&gt;) 时执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;C&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:arrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:d_arrs&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;marshal_dump&lt;/span&gt;
    &lt;span class="no"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;marshal_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;d_attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;d_arrs&lt;/span&gt; 
&lt;span class="c1"&gt;# =&amp;gt; [1,2,3,4,5]&lt;/span&gt;
&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrs&lt;/span&gt; 
&lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Coercion 相关&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coerce: 当使用一个二元操作符的时候会在第二个参数上触发，当我们重载运算符的时候很有用。&lt;/li&gt;
&lt;li&gt;to_*系列方法：这些方法虽然看起来不像，但其实很多时候都会被触发：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# coerce&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;C&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_s&lt;/span&gt;
    &lt;span class="s2"&gt;"CCCC"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_int&lt;/span&gt;
    &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_proc&lt;/span&gt;
    &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; 3&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; 15&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; CCCC 会触发to_s&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                                               
&lt;span class="c1"&gt;# =&amp;gt; 4 触发to_int&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# to_proc&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 1&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 2&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 3&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 4&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>taojay315</author>
      <pubDate>Thu, 14 Jan 2016 00:37:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/28727</link>
      <guid>https://ruby-china.org/topics/28727</guid>
    </item>
    <item>
      <title>Ruby 常量查找 (译)</title>
      <description>&lt;h2 id="不好意思没注意到，跟这篇重复了，管理员看到顺手帮我删了吧。 https://ruby-china.org/topics/26890"&gt;不好意思没注意到，跟这篇重复了，管理员看到顺手帮我删了吧。 &lt;a href="https://ruby-china.org/topics/26890" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/26890&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;注：此文翻译整理自&lt;a href="https://cirw.in/blog/constant-lookup" rel="nofollow" target="_blank"&gt;https://cirw.in/blog/constant-lookup&lt;/a&gt;        ，推荐关注，pry 主要贡献人&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;翻译的难免有错，请见谅。&lt;/p&gt;

&lt;p&gt;使用常量很简单，理解如何查找常量的很麻烦。&lt;/p&gt;

&lt;p&gt;虽然一句话就可以说清楚常量查找，&lt;strong&gt;&lt;em&gt;依次在 Module.nesting, Module.nesting.first.ancestors 中查找，如果 Module.nesting.first 是 nil，则在 Object.ancestors 中查找。&lt;/em&gt;&lt;/strong&gt;，但是为了更好的理解，我们还是举几个例子吧。&lt;/p&gt;
&lt;h3 id="Module.nesting"&gt;Module.nesting&lt;/h3&gt;
&lt;p&gt;Ruby 首先会根据词法作用域 (lexical scope) 周围的 module 或者 classes 的关联的常量来查找：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A
  module B; end
  module C
    module D
      B == A::B
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的例子里在 D 中的 B 首先会查找：A::C::D::B (不存在), 然后 A::C::B (不存在), 最后 A::B (找到了)。&lt;/p&gt;

&lt;p&gt;跟 ruby 里的其他东西一样，查找常量的 namespace chains 也是在运行时通过 Module.nesting 可以实现自省的（运行时知道自己的 Module.nesting）：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A
  module C
    module D
      Module.nesting == [A::C::D, A::C, A]
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你尝试过用简写的声明方式来重打开一个 module，你可能已经注意到不使用完整的 namespace 是无法访问其他常量的。这是因为外面的 namespace 并没有添加到 Module.nesting 当中：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A
  module B; end
end

# 错误
module A::C
  B
end

# 正确
module A
  module D
    B
  end
end
# NameError: uninitialized constant A::C::B
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面错误的例子中，如果要使用 B，那么 Module.nesting 里必须包含 A, 但是上面的 nesting 里只有 [A::C]&lt;/p&gt;
&lt;h3 id="Ancestors"&gt;Ancestors&lt;/h3&gt;
&lt;p&gt;如果在 Module.nesting 中找不到，Ruby 就会在当前打开 class 或者 module 的祖先中查找。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  module B; end
end

class C &amp;lt; A
  B == A::B
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当前打开是指最里面的 class 或者 module，例如上面查找 B 的 C。一个常见的误解就是常量查找会使用 self.class，例如上面也许觉得 B 应该查找 C::B，但实际上并不会这样查找，参考下面的例子。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  def get_c; C; end
end

class B &amp;lt; A
  module C; end
end

B.new.get_c #self.class是B，但是并不会查找B::C 当前打开类是A，
# NameError: uninitialized constant A::C
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Object::"&gt;Object::&lt;/h3&gt;
&lt;p&gt;在顶级作用域的时候，Module.nesting 是 []，所以常量直接在当前打开的 class 和他的祖先里查找，虽然你看不到，但是顶级作用域里的当前打开类是 Object。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Object
  module C; end
end
C == Object::C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;虽然还没有说，但是你也应该注意到，新定义的常量也会在当前打开类里进行定义，所以顶级作用域里定义的常量也会在 Object 里进行定义：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module C; end
Object::C == C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个就解释了为什么在顶级作用域里定义的常量在哪里都可以使用，因为在 ruby 里大部分类都是 Object 的子类，所以 Object 总是在其他类的祖先链里，所以在顶级作用域里定义的常量在其他地方都可以使用。&lt;/p&gt;

&lt;p&gt;所以如果你使用过 BasicObject(不是 Objet 的子类)，你会注意到你在 BasicObject 内部无法访问顶级作用域的常量。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Foo &amp;lt; BasicObject
  Kernel
end
# NameError: uninitialized constant Foo::Kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你想在这种情况下使用，可以使用::Kernel 来访问 Object::Kernel。&lt;/p&gt;

&lt;p&gt;Ruby 假设你会把 module 混入到继承自 Object 的 class 之中，所以如果当前打开的是一个 module，也会把 Object 添加到祖先链之中。所以可以在 module 中使用顶级作用域的常量。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module A; end
module B;
  A == Object::A
end
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="class_eval"&gt;class_eval&lt;/h3&gt;
&lt;p&gt;正如上面提到的，常量会查找当前打开的类或者 module，当前打开取决于周围的 class 或者 module 关键字，所以，如果你使用 class_eval 或者 module_eval 来执行 block，这个并不会修改当前打开类。还是会在 block 定义的作用域内查找。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  module B; end
end

class C
  module B; end
  A.class_eval{ B } == C::B
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过略复杂的事实是，如果你使用字符串来 eval，在 class_eval 的情况下，Module.nesting 中会包含自己的 class，在 instance_eval 的情况下，Module.nesting 中则会包含这个 object 的 singleton class。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  module B; end
end

class C
  module B; end
  A.class_eval("B") == A::B
end
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="其他细节"&gt;其他细节&lt;/h3&gt;
&lt;p&gt;最后我想指出，你在一个 class 的 singleton 里是无法访问在 class 内定义的常量。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  module B; end
end
class &amp;lt;&amp;lt; A
  B
end
# NameError: uninitialized constant Class::B
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是因为 singleton class 的祖先链中并不包含自身的 class，直接从 Class 开始。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  module B; end
end
class &amp;lt;&amp;lt; A; ancestors; end
[Class, Module, Object, Kernel, BasicObject]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相似的情况下，还需要指出已经在 Module.nesting 里的 class 的父类并不会出现在 Module.nesting 之中，如下面的 A。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A
  module B; end
end

class C &amp;lt; A
  class D
    B
  end
end
# NameError: uninitialized constant C::D::B
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="总结"&gt;总结&lt;/h3&gt;
&lt;p&gt;常量查找并不难，只需要记住查找词法包围的 class 和 module，你可以使用 Module.nesting 里的第一个值来当作当前打开类或者模块，如果这个值为空，则当前打开类是 Object。&lt;/p&gt;

&lt;p&gt;下面的代码模拟了 ruby 内部的常量查找方式，你会注意到我使用字符串作为 binding.eval 的变量所以 Module.nesting 是 binding 的 object 而不是 block。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Binding
  def const(name)
    eval &amp;lt;&amp;lt;-CODE, __FILE__, __LINE__ + 1
      modules = Module.nesting + (Module.nesting.first || Object).ancestors
      modules += Object.ancestors if Module.nesting.first.class == Module
      found = nil

      modules.detect do |mod|
        found = mod.const_get(#{name.inspect}, false) rescue nil
      end
      found or const_missing(#{name.inspect})
    CODE
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(翻译完)&lt;/p&gt;
&lt;h3 id="其他"&gt;其他&lt;/h3&gt;
&lt;p&gt;实际上常量查找是很好玩的一个过程，可以参考一下 Rails 实现 const_missing 和 const_get 的方法。&lt;/p&gt;

&lt;p&gt;可以解决诸多疑惑，例如：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Person
end

String.const_get(:Person) # Person
String::Person # Person
String::Person::String # String  
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>taojay315</author>
      <pubDate>Mon, 11 Jan 2016 23:56:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/28696</link>
      <guid>https://ruby-china.org/topics/28696</guid>
    </item>
    <item>
      <title> Rails Log Process</title>
      <description>&lt;p&gt;最近研究了一下整理 Rails 的日志，简单整理下。&lt;/p&gt;

&lt;p&gt;使用工具包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logstash&lt;/li&gt;
&lt;li&gt;lograge&lt;/li&gt;
&lt;li&gt;elasticsearch&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="0. 准备"&gt;0. 准备&lt;/h2&gt;
&lt;p&gt;如果没有修改过任何配置，rails 的日志大概是这样的&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Started POST &lt;span class="s2"&gt;"/users"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; ::1 at 2015-09-28 15:46:22 +0800
Processing by Devise::RegistrationsController#create as HTML
  Parameters: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"utf8"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"✓"&lt;/span&gt;, &lt;span class="s2"&gt;"authenticity_token"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"m/JixAv0ZmXTRLn1mmFGKNF/1MQgb5iXqPThmc3S23B7eiYfCmzwDXALr5yrw9v0dUWdkGVEkGzt8Qa2Q9vlFQ=="&lt;/span&gt;, &lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;{&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;, &lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"[FILTERED]"&lt;/span&gt;, &lt;span class="s2"&gt;"password_confirmation"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"[FILTERED]"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="s2"&gt;"commit"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Sign up"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;(&lt;/span&gt;0.1ms&lt;span class="o"&gt;)&lt;/span&gt;  begin transaction
  User Exists &lt;span class="o"&gt;(&lt;/span&gt;0.1ms&lt;span class="o"&gt;)&lt;/span&gt;  SELECT  1 AS one FROM &lt;span class="s2"&gt;"users"&lt;/span&gt; WHERE &lt;span class="s2"&gt;"users"&lt;/span&gt;.&lt;span class="s2"&gt;"email"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user@example.com'&lt;/span&gt; LIMIT 1
   &lt;span class="o"&gt;(&lt;/span&gt;0.1ms&lt;span class="o"&gt;)&lt;/span&gt;  rollback transaction
  Rendered /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/devise-3.4.1/app/views/devise/shared/_links.html.erb &lt;span class="o"&gt;(&lt;/span&gt;1.6ms&lt;span class="o"&gt;)&lt;/span&gt;
  Rendered /usr/local/var/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/devise-3.4.1/app/views/devise/registrations/new.html.erb within layouts/application &lt;span class="o"&gt;(&lt;/span&gt;11.1ms&lt;span class="o"&gt;)&lt;/span&gt;
Completed 200 OK &lt;span class="k"&gt;in &lt;/span&gt;148ms &lt;span class="o"&gt;(&lt;/span&gt;Views: 51.5ms | ActiveRecord: 0.3ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个时候使用的 logger 是 ActiveSupport 里提供的默认 logger。&lt;/p&gt;

&lt;p&gt;Rails log 机制是通过 pub/sub 来实现的，比如 ActiveRecord::LogSubscriber，还是很方便重新改写的。&lt;/p&gt;

&lt;p&gt;（关于 Notification 可以翻看以前的一篇文章。&lt;a href="http://blog.jerry-tao.com/sources/2015/02/23/active_support_notifications.html" rel="nofollow" target="_blank" title=""&gt;ActiveSupport Notifications&lt;/a&gt;）&lt;/p&gt;

&lt;p&gt;了解这些基础之后让我们假设一个我们需要的数据格式（伪 model）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SQL&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:request&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt; &lt;span class="c1"&gt;# 用来跟request关联的id&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:session_id&lt;/span&gt; &lt;span class="c1"&gt;# 用来跟request关联的id&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:duration&lt;/span&gt; &lt;span class="c1"&gt;# 时间&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="c1"&gt;# 存储对应的模型名称&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt; &lt;span class="c1"&gt;# 存储对应的SQL语句&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="c1"&gt;# 区别其他两种类型&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExceptionStack&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:request&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt; &lt;span class="c1"&gt;# 用来跟request关联的id&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:session_id&lt;/span&gt; &lt;span class="c1"&gt;# 用来跟request关联的id&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt; &lt;span class="c1"&gt;# 错误信息&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:error&lt;/span&gt; &lt;span class="c1"&gt;# 错误类&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:backtrace&lt;/span&gt; &lt;span class="c1"&gt;# 异常栈&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt; &lt;span class="c1"&gt;# 返回的状态&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:handler&lt;/span&gt; &lt;span class="c1"&gt;# 处理的方法&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="c1"&gt;# 区别其他两种类型&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt; &lt;span class="c1"&gt;# 每一次请求的唯一标志&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:session_id&lt;/span&gt; &lt;span class="c1"&gt;# 一次session的id&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:method&lt;/span&gt; &lt;span class="c1"&gt;# HTTP Verb&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt; &lt;span class="c1"&gt;# 请求地址&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:format&lt;/span&gt; &lt;span class="c1"&gt;# 请求格式&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:controller&lt;/span&gt; &lt;span class="c1"&gt;# 对应controller&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:action&lt;/span&gt; &lt;span class="c1"&gt;# 对应action&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt; &lt;span class="c1"&gt;# 返回状态&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:duration&lt;/span&gt; &lt;span class="c1"&gt;# 时间&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:view&lt;/span&gt; &lt;span class="c1"&gt;# 渲染页面时间&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:db&lt;/span&gt; &lt;span class="c1"&gt;# 处理数据库时间&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="c1"&gt;# 区别其他两种类型&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:host&lt;/span&gt; &lt;span class="c1"&gt;# 主机地址&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:remote_ip&lt;/span&gt; &lt;span class="c1"&gt;# 请求IP&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:origin&lt;/span&gt; &lt;span class="c1"&gt;# 来源请求的页面&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:user_agent&lt;/span&gt; &lt;span class="c1"&gt;# 用户浏览器信息&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:@timestamp&lt;/span&gt; &lt;span class="c1"&gt;# 时间&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt; &lt;span class="c1"&gt;# 请求概述&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="1. 格式化Rails日志"&gt;1. 格式化 Rails 日志&lt;/h2&gt;&lt;h3 id="1.1 Request日志"&gt;1.1 Request 日志&lt;/h3&gt;
&lt;p&gt;先加 lograge 和 logstash-event 到 Gemfile&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'lograge'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'logstash-event'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在 application.rb 里先简单配置一下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lograge&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Formatters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Logstash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;colorize_logging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;# 关闭彩色显示，会产生很多不适合阅读的字符。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个时候输出的结构大概是：&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo/auth/sessions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"controller"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo/auth/sessions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;27.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"db"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;11.32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2015-09-29T10:53:14.766Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[401] POST demo/auth/sessions (demo/auth/sessions#create)"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;距离我们要的数据还缺很多信息，根据 lograge 的文档，需要先在 application 把需要的数据加进来&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;append_info_to_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookie_jar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_session_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:host&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:remote_ip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remote_ip&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:origin&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_ORIGIN'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ORIGINAL_FULLPATH'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user_agent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_USER_AGENT'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以在 application.rb 自定义一下这些内容了：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#application.rb&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;custom_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;uuid: &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;session_id: &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:session_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'request'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:host&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;remote_ip: &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:remote_ip&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;origin: &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:origin&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;user_agent: &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user_agent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样请求的格式就正确了。&lt;/p&gt;
&lt;h3 id="1.2 SQL日志"&gt;1.2 SQL 日志&lt;/h3&gt;
&lt;p&gt;SQL 的日志默认是通过 ActiveRecord::LogSubscriber 来记录的，这个会包含格式化 SQL 语句等功能，但是我们想把他转换成 json，适合通过 logstash 收集进 elasticsearch。&lt;/p&gt;

&lt;p&gt;我用了一种很取巧的手段：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#config/initializers/sql_log.rb&lt;/span&gt;
&lt;span class="c1"&gt;# 先把默认的subscriber去掉。&lt;/span&gt;
&lt;span class="no"&gt;Lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LogSubscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LogSubscriber&lt;/span&gt;
      &lt;span class="n"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:active_record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 自己实现一个subscriber&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;SQLLog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LogSubscriber&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LogSubscriber&lt;/span&gt;
    &lt;span class="no"&gt;IGNORE_PAYLOAD_NAMES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SCHEMA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"EXPLAIN"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RuntimeRegistry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sql_runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runtime&lt;/span&gt;
      &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RuntimeRegistry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sql_runtime&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_runtime&lt;/span&gt;
      &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="n"&gt;rt&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;binary?&lt;/span&gt;
          &lt;span class="c1"&gt;# This specifically deals with the PG adapter that casts bytea columns into a Hash.&lt;/span&gt;
          &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes of binary data&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;NULL binary data&amp;gt;"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;duration&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug?&lt;/span&gt;

      &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;IGNORE_PAYLOAD_NAMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binds&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
        &lt;span class="n"&gt;binds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"  "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binds&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="n"&gt;render_bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:log_uuid_session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;duration: &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="ss"&gt;binds: &lt;/span&gt;&lt;span class="n"&gt;binds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:sql&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'sql'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;uuid: &lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;session_id: &lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
      &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logger&lt;/span&gt;
      &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# attach 到active_record上&lt;/span&gt;
&lt;span class="no"&gt;SQLLog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LogSubscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach_to&lt;/span&gt; &lt;span class="ss"&gt;:active_record&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为在这里面我想存储一下关联的 request 和 session，所以才有了&lt;code&gt;ids = Thread.current[:log_uuid_session_id] || Array.new(2)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果不需要直接去掉就好，如果需要还需要在 application_controller 里记录一下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application_controller&lt;/span&gt;
&lt;span class="n"&gt;before_filter&lt;/span&gt; &lt;span class="ss"&gt;:set_uuid_session_id&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_uuid_session_id&lt;/span&gt;
  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:log_uuid_session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookie_jar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_session_id'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样可以很方便的看到一次请求产生了几条 db 请求。&lt;/p&gt;
&lt;h3 id="1.3 异常日志"&gt;1.3 异常日志&lt;/h3&gt;
&lt;p&gt;首先，rescue 所有的异常是很有争议的做法，不过我还是想对用户尽量屏蔽这些异常情况。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application_controller.rb&lt;/span&gt;
&lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: :generic_exception&lt;/span&gt; &lt;span class="c1"&gt;# 可能还有很多其他的异常rescue&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generic_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:log_uuid_session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt; &lt;span class="c1"&gt;# 这里是因为有些时候会捕捉到例如RuntimeError这种情况。&lt;/span&gt;
    &lt;span class="n"&gt;error_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;error_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;backtrace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;error_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;error_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
    &lt;span class="n"&gt;backtrace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;error: &lt;/span&gt;&lt;span class="n"&gt;error_class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;backtrace: &lt;/span&gt;&lt;span class="n"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'exception'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="n"&gt;error_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;handler: &lt;/span&gt;&lt;span class="s1"&gt;'generic_exception'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uuid: &lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="ss"&gt;session_id: &lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;nothing: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过上面的处理，适合输入到 logstash 的 json 格式日志算是准备好了。&lt;/p&gt;
&lt;h2 id="2. 使用logstash收集log"&gt;2. 使用 logstash 收集 log&lt;/h2&gt;
&lt;p&gt;关于 logstash，还有多个 rails server 的情况，可以去参考 logstash 的文档，我就简单贴一下基本的配置：&lt;/p&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;input&lt;/span&gt; {
    &lt;span class="n"&gt;stdin&lt;/span&gt; {}
  &lt;span class="n"&gt;file&lt;/span&gt; {
    &lt;span class="n"&gt;type&lt;/span&gt;=&amp;gt; &lt;span class="s1"&gt;'application'&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;=&amp;gt;&lt;span class="s1"&gt;'~/Workspaces/demo/log/development.log'&lt;/span&gt;
    &lt;span class="n"&gt;codec&lt;/span&gt;=&amp;gt;&lt;span class="s1"&gt;'json'&lt;/span&gt;
  }
}



&lt;span class="n"&gt;output&lt;/span&gt; {
  &lt;span class="n"&gt;stdout&lt;/span&gt; {}
  &lt;span class="n"&gt;elasticsearch&lt;/span&gt; {
    &lt;span class="n"&gt;cluster&lt;/span&gt;=&amp;gt; &lt;span class="s1"&gt;'logstash'&lt;/span&gt;
    &lt;span class="n"&gt;protocol&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;http&lt;/span&gt;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不适合生产环境，如果使用多个集群还需要考虑使用 redis 来做中转，根据具体情况具体修改吧。&lt;/p&gt;

&lt;p&gt;然后测试下：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;elasticsearch &lt;span class="nt"&gt;-d&lt;/span&gt;
logstash &lt;span class="nt"&gt;-f&lt;/span&gt; sample.conf
rails s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;随便访问几个网址，然后看一下 ES 里的数据：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-XGET&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:9200/logstash-2015.09.29/_search"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'
{
  "sort": [
    {
      "@timestamp": {
        "order": "desc"
      }
    }
  ]
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认情况下会使用 logstash-日期 这种格式的 index。&lt;/p&gt;

&lt;p&gt;至于如何显示如何处理，就是另一个 topic 了。&lt;/p&gt;

&lt;p&gt;有很多细节地方还可以优化，例如把异常的 log 也使用 notification，把自己写的 notification 集成进 lograge 等，不过大体的思路还是这样的。&lt;/p&gt;</description>
      <author>taojay315</author>
      <pubDate>Tue, 29 Sep 2015 19:20:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/27523</link>
      <guid>https://ruby-china.org/topics/27523</guid>
    </item>
  </channel>
</rss>
