<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>lgcjwt (刘国春)</title>
    <link>https://ruby-china.org/lgcjwt</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Ruby on Rails 项目实现日志等级实时变更</title>
      <description>&lt;p&gt;对于一个 rails 项目，一般是项目进程启动的时候会根据环境不同来设置一个日志等级。&lt;br&gt;
但如果想要实现日志等级的实时变更并不容易，可能存在多个更加优雅的做法，而这里分享一种实践过的简 r 有效的办法。&lt;/p&gt;
&lt;h3 id="起源"&gt;起源&lt;/h3&gt;
&lt;p&gt;某天，Bill 怀着一个复杂的心情看了看你，“有个特殊的需求可能需要你调研一下，我们需要对一个 rails 项目实现&lt;code&gt;实时变更日志等级&lt;/code&gt;。”。&lt;br&gt;
你思考了一下，需求倒是很明确，可是……&lt;br&gt;
“当然，你只是研究一下，不是非要实现，我的意思是，当然，能实现就最好不过了”&lt;br&gt;
你一定受不了这个的，但这可能并不是一件容易的事情。&lt;br&gt;&lt;/p&gt;
&lt;h3 id="思路"&gt;思路&lt;/h3&gt;
&lt;p&gt;其实改变日志等级很容易：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'INFO'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尝试通过&lt;strong&gt;定时任务&lt;/strong&gt;的方式进入进程修改变量值&lt;br&gt;&lt;/p&gt;

&lt;p&gt;但其实，&lt;strong&gt;rake 是一个新的进程&lt;/strong&gt;，他无法进入到 puma 或者 unicorn 或者 sidekiq 的进程中修改进程的变量。&lt;br&gt;&lt;/p&gt;

&lt;p&gt;很自然的就想到通过定时任务进入进程内部，修改相应的日志等级变量值。&lt;br&gt;&lt;/p&gt;

&lt;p&gt;我相信，这样一定是能达到目标的&lt;br&gt;&lt;/p&gt;

&lt;p&gt;按照这个思路，尝试了用&lt;code&gt;rufus-scheduler&lt;/code&gt;去替代&lt;code&gt;whenever&lt;/code&gt;。因为  &lt;code&gt;Rufus-scheduler (out of the box) is an in-process, in-memory scheduler.&lt;/code&gt;。&lt;br&gt;&lt;/p&gt;

&lt;p&gt;其实这个方法确实是能部分实现日志等级变更的，但是&lt;code&gt;sidekiq&lt;/code&gt;进程的日志等级就需要别的方法来控制。&lt;br&gt;&lt;/p&gt;

&lt;p&gt;“&lt;strong&gt;而且，这并不优雅&lt;/strong&gt;”，当你告诉 Bill 这个未成型的方案的时候，他总结道。&lt;br&gt;&lt;/p&gt;

&lt;p&gt;“我或许可以帮你了解一下这个问题的 &lt;em&gt;症结&lt;/em&gt; 所在”&lt;br&gt;&lt;/p&gt;
&lt;h3 id="症结与解决办法"&gt;症结与解决办法&lt;/h3&gt;
&lt;p&gt;ruby 项目的所有日志添加都要通过 Logger 类的实例方法：add，是这样定义的&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&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;progname&lt;/span&gt; &lt;span class="o"&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;severity&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;UNKNOWN&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@logdev.nil&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="vi"&gt;@level&lt;/span&gt;
      &lt;span class="k"&gt;return&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;if&lt;/span&gt; &lt;span class="n"&gt;progname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="n"&gt;progname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@progname&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;progname&lt;/span&gt;
        &lt;span class="n"&gt;progname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@progname&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@logdev.write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;format_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format_severity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;progname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&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="c1"&gt;#add方法的调用：比如logger.info&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;progname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;INFO&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;progname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&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;code&gt;@level&lt;/code&gt;的附值&lt;br&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# +severity+:: The Severity of the log message.&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;severity&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;severity&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;Integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;severity&lt;/span&gt;
   &lt;span class="k"&gt;else&lt;/span&gt;
     &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'debug'&lt;/span&gt;
       &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DEBUG&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'info'&lt;/span&gt;
       &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;INFO&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'warn'&lt;/span&gt;
       &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;WARN&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt;
       &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ERROR&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'fatal'&lt;/span&gt;
       &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FATAL&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'unknown'&lt;/span&gt;
       &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UNKNOWN&lt;/span&gt;
     &lt;span class="k"&gt;else&lt;/span&gt;
       &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"invalid log level: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;一般是项目进程启动的时候会根据环境不同来设置一个日志等级，但无法在进程运行过程中实时变更。&lt;br&gt;&lt;/p&gt;
&lt;h3 id="打开类"&gt;打开类&lt;/h3&gt;
&lt;p&gt;很明显，如果有一个&lt;code&gt;Monkeypatch&lt;/code&gt;，将 add 方法重写，logger 就可以任我们宰割了。&lt;/p&gt;

&lt;p&gt;最终这样实现：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Logger&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&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;progname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tmp/level.yml"&lt;/span&gt;
    &lt;span class="vi"&gt;@new_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="vi"&gt;@last_effect_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@last_effect_time&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; 

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@new_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@last_effect_time&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;log_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;log_level&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"INFO"&lt;/span&gt;
          &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"DEBUG"&lt;/span&gt;
          &lt;span class="vg"&gt;$logger_level&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;when&lt;/span&gt; &lt;span class="s2"&gt;"WARN"&lt;/span&gt;
          &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"ERROR"&lt;/span&gt;
          &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"FATAL"&lt;/span&gt;
          &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"UNKNOWN"&lt;/span&gt;
          &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="vi"&gt;@last_effect_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@new_time&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;UNKNOWN&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@logdev.nil&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="vi"&gt;@level&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;progname&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="vi"&gt;@progname&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;progname&lt;/span&gt;
        &lt;span class="n"&gt;progname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@progname&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@logdev.write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;format_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format_severity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;progname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键在于：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tmp/level.yml"&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@new_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@last_effect_time&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;log_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
   &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;log_level&lt;/span&gt;
   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"INFO"&lt;/span&gt;
       &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"DEBUG"&lt;/span&gt;
       &lt;span class="vg"&gt;$logger_level&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;when&lt;/span&gt; &lt;span class="s2"&gt;"WARN"&lt;/span&gt;
       &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"ERROR"&lt;/span&gt;
       &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"FATAL"&lt;/span&gt;
       &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"UNKNOWN"&lt;/span&gt;
       &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="vi"&gt;@last_effect_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@new_time&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="vi"&gt;@level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$logger_level&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里通过在项目的 tmp 目录下新建 level.yml 来实时控制日志等级。这样做有两个好处：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.不需要通过修改数据库的值，只需要修改一个文件就可以实现日志等级切换&lt;/li&gt;
&lt;li&gt;2.设置时间间隔，可以降低 io 频率。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实测可以完美实现日志等级实时变更。如果你发现其中可能存在的问题，&lt;strong&gt;欢迎拍砖&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;git push&lt;/code&gt;&lt;br&gt;
&lt;code&gt;end~&lt;/code&gt;&lt;img title=":sunglasses:" alt="😎" src="https://twemoji.ruby-china.com/2/svg/1f60e.svg" class="twemoji"&gt;&lt;/p&gt;</description>
      <author>lgcjwt</author>
      <pubDate>Fri, 28 Jun 2019 23:00:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/38760</link>
      <guid>https://ruby-china.org/topics/38760</guid>
    </item>
    <item>
      <title>Ruby 原生日志分割已经解决多进程问题？</title>
      <description>&lt;h2 id="Ruby原生日志分割存在多进程问题？"&gt;Ruby 原生日志分割存在多进程问题？&lt;/h2&gt;
&lt;p&gt;今天星期一，你刚到公司，Bill 就来到你的办公桌旁说：“有个本地部署的客户，希望日志分割不用 logrotata，没办法，甲方有进程洁癖。”，经过商量，我决定先调研三种主流的日志分割方案（logrotata、ruby 原生、log4r），然后再开始工作。 &lt;/p&gt;
&lt;h5 id="而Bill更希望我用ruby原生的日志分割，经过一番搜索，发现@user1的这篇帖子"&gt;而 Bill 更希望我用 ruby 原生的日志分割，经过一番搜索，发现&lt;a href="/huacnlee" class="user-mention" title="@huacnlee"&gt;&lt;i&gt;@&lt;/i&gt;huacnlee&lt;/a&gt;的这篇帖子&lt;/h5&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/3704" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/3704&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="本着初生牛犊不怕虎，进一步查阅ruby的源码：logger.rb，发现问题可能在这里："&gt;本着初生牛犊不怕虎，进一步查阅 ruby 的源码：logger.rb，发现问题可能在这里：&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shift_log_age&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@shift_age-3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;downto&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="k"&gt;do&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="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;FileTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&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="vi"&gt;@dev.close&lt;/span&gt; &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_logfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里只粘出来按照日期分割的部分，多线程情况下显然会有问题。如果能改善这段代码，显然能很 ruby 哲学地运用 ruby 原生的日志分割&lt;/p&gt;
&lt;h5 id="又本着不怕虎，我试图打开类重写这个方法来解决这个问题"&gt;又本着不怕虎，我试图打开类重写这个方法来解决这个问题&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shift_log_age&lt;/span&gt;
  &lt;span class="vi"&gt;@dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;open_logfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%Y%m%d"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@dev.stat.size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="vi"&gt;@shift_size&lt;/span&gt;
  &lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="完成这个动作之后，我迫不及待的将代码布上服务器"&gt;完成这个动作之后，我迫不及待的将代码布上服务器&lt;/h5&gt;
&lt;p&gt;设置分割大小为 1M，开始用脚本开两个 worker 不停的写日志，发现完全没有问题。我将这个结果兴奋的告诉 Bill，Bill 却很淡定，甚至有些疑惑，指出了这里面几个明显的问题：&lt;/p&gt;
&lt;h6 id="1. 存在临界域问题"&gt;1. 存在临界域问题&lt;/h6&gt;&lt;h6 id="2. 每次都要重新open，费力又不能解决问题"&gt;2. 每次都要重新 open，费力又不能解决问题&lt;/h6&gt;
&lt;p&gt;Bill 的疑惑在于，这样的代码不可能解决临界域的问题，但是测试的结果却是没有问题的。Bill 决定拒接我的 PR，让我继续研究&lt;/p&gt;
&lt;h5 id="至此，我已经决定放弃ruby原生日志分割，转而去研究log4r，直到我在丧气中刷完Facebook，偶然发现了这个："&gt;至此，我已经决定放弃 ruby 原生日志分割，转而去研究 log4r，直到我在丧气中刷完 Facebook，偶然发现了这个：&lt;/h5&gt;&lt;h6 id="https://www.slideshare.net/sonots/rubylogger-rubyconf-20131109"&gt;&lt;a href="https://www.slideshare.net/sonots/rubylogger-rubyconf-20131109" rel="nofollow" target="_blank"&gt;https://www.slideshare.net/sonots/rubylogger-rubyconf-20131109&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/6bf17760-9de8-4003-bd8b-1567381785d5.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2018/3d7c4975-412a-420d-b4d2-71e8f2d4cdfa.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2018/22577c08-d07f-44a2-be9f-15932ab1225b.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2018/9591a2eb-56bc-4655-a440-67ac921bb461.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2018/e8e5935b-43e6-447b-b955-97d80a98e16f.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2018/d93ce7e5-1c5d-43c5-858b-c2f69f596fee.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2018/14cfb8de-5f5a-470b-a4cb-d0675ddbe247.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2018/2edf4820-cce1-455e-b6b4-c0e873bcc06c.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h6 id="这个需要翻墙，为了省事，直接截图粘贴过来。感受一下作者Naotoshi Seo的兴奋之情。"&gt;这个需要翻墙，为了省事，直接截图粘贴过来。感受一下作者 Naotoshi Seo 的兴奋之情。&lt;/h6&gt;&lt;h5 id="再去ruby源码的github发现"&gt;再去 ruby 源码的 github 发现&lt;/h5&gt;&lt;h6 id="https://github.com/ruby/ruby/pull/428"&gt;&lt;a href="https://github.com/ruby/ruby/pull/428" rel="nofollow" target="_blank"&gt;https://github.com/ruby/ruby/pull/428&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/5ae2f300-1f82-4662-b0a6-3ba1d761bba1.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h5 id="至此看来，ruby原生日志分割，可以完美使用了，而我的方法之所以能成功，看来完全是因为ruby日志分割原本就支持多进程了"&gt;至此看来，ruby 原生日志分割，可以完美使用了，而我的方法之所以能成功，看来完全是因为 ruby 日志分割原本就支持多进程了&lt;/h5&gt;
&lt;p&gt;在 Bill 的帮助下，发现解决这一问题的关键在于&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WRONLY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;APPEND&lt;/span&gt;&lt;span class="p"&gt;)&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;lock&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LOCK_EX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# inter-process locking. will be unlocked at closing file&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;identical?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;identical?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@dev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="c1"&gt;# log shifting&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="c1"&gt;# log shifted by another process (i-node before locking and i-node after locking are different)&lt;/span&gt;
    &lt;span class="vi"&gt;@dev.close&lt;/span&gt; &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="vi"&gt;@dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;open_logfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@dev.sync&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;&lt;h3 id="也就是说：在ruby-2.1.0之后的版本，日志分割都不再有多进程的问题"&gt;也就是说：在 ruby-2.1.0 之后的版本，日志分割都不再有多进程的问题&lt;/h3&gt;&lt;h5 id="但是，Bill依然觉得不能乐观"&gt;但是，Bill 依然觉得不能乐观&lt;/h5&gt;&lt;h6 id="1.虽然看上去已经完美支持多进程，但是调研结果发现工业上大部分仍然在用logrotata或log4r"&gt;1.虽然看上去已经完美支持多进程，但是调研结果发现工业上大部分仍然在用 logrotata 或 log4r&lt;/h6&gt;&lt;h6 id="2.没有看到有使用原生日志分割的例子，甚至没有人来声明原生日志分割已经解决了多进程问题"&gt;2.没有看到有使用原生日志分割的例子，甚至没有人来声明原生日志分割已经解决了多进程问题&lt;/h6&gt;&lt;h6 id="3.是不是任然存在一些已知的或者未知的问题，导致ruby原生日志分割依旧被大家放在角落里？"&gt;3.是不是任然存在一些已知的或者未知的问题，导致 ruby 原生日志分割依旧被大家放在角落里？&lt;/h6&gt;
&lt;p&gt;所以有关原生日志分割的问题，是否可以放心的在存在多进程的工业环境中使用，请大佬们不吝赐教&lt;/p&gt;</description>
      <author>lgcjwt</author>
      <pubDate>Fri, 01 Jun 2018 22:49:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/36880</link>
      <guid>https://ruby-china.org/topics/36880</guid>
    </item>
  </channel>
</rss>
