2018年以降の記事はGitHub Pagesに移行しました

Pygmentsを使ってJekyll内記事のコードハイライトを実現する

あらすじ

Jekyllではデフォルトでコードにハイライトをつける事はできないようなので、Pygmentsという拡張を入れる。

環境


  • Python 2.7.3 (2.6以上が必要)

    • easy_install



easy_installはhttp://peak.telecommunity.com/dist/ez_setup.pyからDLし $ (sudo) python ez_setup.py でインストールする。

※ 後に以下のようなエラーが出るかもしれない。


Liquid error: undefined method ‘Py_IsInitialized’ for RubyPython::Python:Module


これはRubyからPythonを呼びに行くRubypythonというライブラリの中で、libpython2.7.soというファイルを探しに行くが、見つからないとすぐあきらめるようなので? --enable-shared オプションつけてのインストールが吉。

また、以下の様なエラーが出た場合、libpython2.7.so.1.0が見つからなくてpythonコマンドが実行できなくなった。

$ python
> python: error while loading shared libraries: libpython2.7.so.1.0: cannot open shared object file: No such file or directory

$ ldd python
linux-vdso.so.1 => (0x00007fff9cf94000)
libpython2.7.so.1.0 => not found
libpthread.so.0 => /lib64/libpthread.so.0 (0x000000343d600000)
libdl.so.2 => /lib64/libdl.so.2 (0x000000343ce00000)
libutil.so.1 => /lib64/libutil.so.1 (0x0000003440200000)
libm.so.6 => /lib64/libm.so.6 (0x0000003665600000)
libc.so.6 => /lib64/libc.so.6 (0x000000343d200000)
/lib64/ld-linux-x86-64.so.2 (0x000000343ca00000)

/usr/libとか共有ライブラリが検索するように設定しているパスにシンボリックリンクを貼るか、LD_LIBRARY_PATHにパスを追加するか/etc/ld.so.conf.d/hogehoge.confを作ってパスを追加するかldconfigコマンドでパスを追加してからもう一回Pythonインストールする。

参考サイト

手順

いきなりeasy_install。

$ easy_install Pygments
Searching for Pygments
Best match: pygments 1.5
Processing pygments-1.5-py2.7.egg
pygments 1.5 is already the active version in easy-install.pth
Installing pygmentize-script.py script to C:\Pythons\Python27\Scripts
Installing pygmentize.exe script to C:\Pythons\Python27\Scripts
Installing pygmentize.exe.manifest script to C:\Pythons\Python27\Scripts

Using c:\pythons\python27\lib\site-packages\pygments-1.5-py2.7.egg
Processing dependencies for Pygments
Finished processing dependencies for Pygments

highlight hogeタグをテストするが、以下のようなエラーが表示されてしまった。

Liquid error: Bad file descriptor

Windowsでpygmentsを使ってコードハイライト によると、


C:\Ruby193\lib\ruby\gems\1.9.1\gems\albino-1.3.3\lib\albino.rbにパッチを当てる。


という事で albino.rb にパッチをあてると解決するらしい。

確かに、Bundlerでインストールしたgemの中にしっかりと albino-1.3.3 がある。

……が。このalbinoはhighlightがあるたびにPygmentsを呼ぶようなのでこのまま使っていくと超重くなるらしい。


今現在gemでインストールできるjekyllはコードハイライトにalbinoっていう
モジュールを使ってみるみたいで、こいつはハイライトするコードブロックが
あるあるたびにpygamentsプロセスを立ち上げるらしく、それが原因で超重くなってたみたい。


それはできれば避けたい……。

Jekyllの修正

Swap out albino for pygments.rbを見ながら albinopygments.rb に差し替えてやる。

差し替えが完了したらpygments.rbとその依存gemを取りに行くためbundle update。

$ bundle update
Fetching source index for http://rubygems.org/
Fetching source index for http://rubygems.org/
Using RedCloth (4.2.9)
Installing blankslate (3.1.2)
Using fast-stemmer (1.0.1)
Using classifier (1.3.3)
Using directory_watcher (1.4.1)
Installing ffi (1.0.11) with native extensions
Installing kramdown (0.14.0)
Using liquid (2.4.1)
Using syntax (1.0.0)
Installing maruku (0.6.1)
Installing rubypython (0.5.3)
Installing pygments.rb (0.2.13)
Using jekyll (0.11.2)
Using bundler (1.0.21)
Your bundle is updated! Use `bundle show [gemname]` to see where a bundled gem is installed.

これでまずは

 {% highlight ruby  %}
puts "hello world"
{% endhighlight %}

こういう表記が一応、liquid errorがでずに出力されるようになった。

puts "hello world"
world.each do |w|
w = "hoge"
end

色づけ

最後にハイライト。(記事上はもう色がついちゃってるんだけど、本来は全部黒いはず)これはtpw / css / syntax.cssからCSSを持ってくれば良い。このsyntax.cssの内容を JEKYLL_HOME\assets\themes\twitter\bootstrap\css\bootstrap.min.css に追記する。

これでOK!

JekyllをGitHub Pagesに上げるための準備

GitHub Pagesに登録

GitHubより New Repository を選択。

img

Repository NameGitHubのID.github.com と入力しリポジトリ作成。

img

前回の記事で作成していたJekyll Bootstrapのプッシュ先を追加し、プッシュ。

$ git remote add origin git@github.com:gosyujin/gosyujin.github.com.git
$ git push -u origin master

http://gosyujin.github.com にアクセスしても多分404なので。気長に待つ。

img

その間にSite Urlに上のUrlでも入れとく。

img

そんなこんなでサイトが表示されるようになっているはず。(メールが来たら?)

色々な設定

まずは _config.yml

デフォルトのmarkdownパーサの maruku は日本語をうまくパースしてくれないみたいなので(日本語でリスト表記ができなかった)、 kramdown に変更する。以下の記述を追加。

markdown: kramdown

他、タイトルや名前、TwitterIDなどを編集。

title : kk_Atakaの日記
tagline:
author :
name : kk_Ataka
email : kk_ataka@ring.skr.jp
github : gosyujin
twitter : kk_Ataka
feedburner : feedname

production_urlをGitHub Pagesに。

production_url : http://gosyujin.github.com

ここから先はJekyll Bootstrapでインストールした時に入ったコメント機能とかアクセス解析の設定。

コメント機能

デフォルトでは DISQUS というツールを使用している。まずDISQUS - Elevating the discussionでユーザ登録をする必要がある。(TwitterID等でも可能)

img

img

アカウントを作ったら自分のサイトを登録。

img

ここで入力する shortname を控えておく。

img

_config.yml にさっきの shortname を記述する。 providerDISQUSになっている事を確認する。

comments :
provider : disqus
disqus :
short_name : kkataka
(略)

これでいけるはず。(だめならUniversalコードを _includes/JB/comments-providers/disqus に貼り付けてみる?)
img

自分のサイトにコメントが書き込まれると、こんな感じでDISQUSに表示される。

img

img

現時点で、DISQUSのコメント欄を日本語にするには Admin => Settings => Disqus 2012 のチェックを外す。DISQUS 2012テーマ(?)はまだ日本語に対応していないらしい。また、 Language はJapaneseにしておくこと。

アクセス解析

デフォルトでは Google Analytics を使用している。トラッキング ID(UA-xxxxxxxx-x)を控えて _config.yml に記述する。

analytics :
provider : google
google :
tracking_id : 'UA-xxxxxxxx-x'

次はレイアウトの変更とかツイートボタン設置とか。

JekyllとJekyll Bootstrapでかんたん静的サイト生成…するための準備

あらすじ

  • はてな記法、綺麗に出力できて今までお気に入りだったんだけど、欠点として、はてなでしか使わないという問題が
  • 他にも文章書く時にreSTとかMarkdownを使うと捗るが、結局アウトプットするのは個人でははてなが多い……*1
  • そもそもはてな記法は基本はてなじゃなきゃ見れない
  • なら最初から…?

うーん。他の記法使う時がきたのか。

Jekyllとは

Jekyll is a simple, blog aware, static site generator.

https://github.com/mojombo/jekyll/wiki

静的サイトのジェネレーターなのね。 HTML, Markdown, textile なんでもござれ。

環境

  • CentOS 6.2 と、WindowsXP
  • Ruby 1.9.2
    • Bundler 1.1.4
  • Git 1.7.9.6
  • Jekyll 0.11.2
  • RedCloth 4.2.9

いきなり動かす

本来は決まったディレクトリを作ったり、コンフィグファイルを作ったり、レイアウトファイルを作ったりしていく……のだけど、Jekyll Bootstrapを使えばいきなりフルセットで動かす事ができる。

取得はGitで。

$ git clone http://github.com/plusjade/jekyll-bootstrap.git JEKYLL_HOME

JekyllはBundlerでインストールするので移動してGemfileを作成。そしてjekyllコマンドで実行!

$ cd JEKYLL_HOME
$ vi Gemfile
$ cat Gemfile
source :rubygems
source "http://rubygems.org"

gem 'jekyll'
gem 'RedCloth'

$ bundle install --path vendor/bundle
$ bundle exec jekyll --server

これで、http://localhost:4000 にアクセスしてみると超カッコいいサンプルページが出来上がっている。CentOSは以上。

ハマり for Windows

ところで、Windows XPで同じ事やっても_site下に静的ファイルが全然できなかった。……まあ、そもそもXP使うなって話?

結果としては、RedCloth-4.2.9-x86-mingw32直下のspecディレクトリを削除したら生成されるようになった。静的ファイル生成の流れを追っていくと、

  • jekyll/bin/jekyll内でsite.processメソッドが呼ばれている
  • jekyll/lib/jekyll/site.rbのprocessメソッドからread,readからread_directoriesメソッドが呼ばれている
  • read_directoriesメソッドは以下のようになっている
     def read_directories(dir = '')
       base = File.join(self.source, dir)
       entries = Dir.chdir(base) { filter_entries(Dir['*']) }
       self.read_posts(dir)

       entries.each do |f|
         f_abs = File.join(base, f)
         f_rel = File.join(dir, f)
         if File.directory?(f_abs)
           next if self.dest.sub(/\/$/, '') == f_abs
           read_directories(f_rel)
         elsif !File.symlink?(f_abs)
           first3 = File.open(f_abs) { |fd| fd.read(3) }
           if first3 == "---"
#\yaml!/
             # file appears to have a YAML header so process it as a page
             pages << Page.new(self, self.source, dir, f)
           else
             # otherwise treat it as a static file
             static_files << StaticFile.new(self, self.source, dir, f)
           end
         end
       end
     end
  • 読み込むファイルをputs して眺めていると、\yaml!/のところでRedCloth-4.2.9-x86-mingw32\spec\fixtures\basic.ymlを呼んだ瞬間止まっている模様……
  • まあ、specファイルだしなくてもいいよね? 削除、で、動いたという事で*2

で、動かす(Windows)。

$ be jekyll --server
Configuration from C:/HOGE/jekyll/_config.yml
Auto-regenerating enabled: C:/HOGE/project/jekyll -> C:/HOGE/project/jekyll/_site
[2012-09-12 23:50:51] regeneration: 983 files changed
[2012-09-12 23:50:54] INFO  WEBrick 1.3.1
[2012-09-12 23:50:54] INFO  ruby 1.9.2 (2011-07-09) [i386-mingw32]
[2012-09-12 23:50:54] INFO  WEBrick::HTTPServer#start: pid=5944 port=4000

 ___________________________________________________________________________
| Maruku tells you:
+---------------------------------------------------------------------------
| Unclosed span (waiting for ["_"])
| ---------------------------------------------------------------------------
| Included file 'sig.markdown' not found in _includes directoryEOF
| -------------------------------------------------------------|--------------
|                                                              +--- Byte 61
| Shown bytes [0 to 61] of 61:
| >Included file 'sig.markdown' not found in _includes directory
|
| At line 2
|       text     |Included file 'sig.markdown' not found in _includes directory|
|      empty --> ||
|       text     |This _is_ cool|
|
|
| Elements read in span:
|
| Current string:
|   "includes directory"
+---------------------------------------------------------------------------
!C:/HOGE/project/jekyll/vendor/bundle/ruby/1.9.1/gems/maruku-0.6.0/lib/maruku/errors_management.rb:49:in `maruku_error'
!C:/HOGE/project/jekyll/vendor/bundle/ruby/1.9.1/gems/maruku-0.6.0/lib/maruku/input/parse_span_better.rb:222:in `read_span'
!C:/HOGE/project/jekyll/vendor/bundle/ruby/1.9.1/gems/maruku-0.6.0/lib/maruku/input/parse_span_better.rb:423:in `read_em'
!C:/HOGE/project/jekyll/vendor/bundle/ruby/1.9.1/gems/maruku-0.6.0/lib/maruku/input/parse_span_better.rb:202:in `read_span'
!C:/HOGE/project/jekyll/vendor/bundle/ruby/1.9.1/gems/maruku-0.6.0/lib/maruku/input/parse_span_better.rb:46:in `parse_span_better'
\___________________________________________________________________________

エラーは出てるけど、これで http://localhost:4000にアクセスするとページが見れる!
sig ... 署名? あんまりよくないけど、空っぽでも存在さえすれば怒られないっぽい。

*1:個人でない場合はExcelとか

*2:なんでbasic.ymlを読もうとして止まったかまでは調べてないです……

Fluentdの自作プラグインがロードできないのでソースの中身を追ってみる…

あらすじ

前回までに簡単なプラグインを作成する事はできた。
次はプラグインをGem化しようかなと思って色々いじってたら、実行時になんかうまくプラグインが読めない……。なんで?

今できてる事とできてない事

BundlerでFluentdをインストールし、

$ bundle exec fluentd -c /hoge/fluent.conf -p /hoge/plugin 

を実行した時……

  • gem installで入れたfluentdのプラグインはちゃんとロードできる
    • Gemにしちゃえばロードできる
  • プラグインディレクトリ直下(-p /hoge/plugin)にササッと作ってみたプラグインもロードでき、動いた
    • 直下に放り込めばロードできる
/hoge/plugin
└in_hoge.rb              # これはロードできる
  • gem未満、ササッと作った以上のプラグインがロードできない
    • fluent-pluginっぽいディレクトリ構成にした程度のもの。まだローカルに置いてあるだけでgemには成り切れていない
/hoge/plugin
├in_hoge.rb              # これはロードできる
└fluent-plugin-hogehoge
 ├lib
 │└fluent
 │ └plugin
 │  └in_hogehoge.rb  # これがロードできない
 ├fluent-plugin-hogehoge.gemspec
 └他

調査結果と(暫定)解決策

fluentdのプラグインロード順番

fluentdコマンドを実行すると、 なんやかんやあって プラグインをロードしにいく。ソースは$FLUENTD_HOME/lib/fluent/plugin.rb

  1. $FLUENTD_HOME/lib/fluent/plugin/直下のrbファイルをロードする
    • in_exec.rbとか
  2. gem_pluginをロードする
    • これは今回空だった
  3. /etc/fluent/plugin/直下のrbファイルをロードする
    • デフォルトのプラグイン置き場
    • デフォルトの設定は$FLUENTD_HOME/lib/fluent/env.rbに定義されている
  4. オプションで-p /hoge/pluginを指定していた場合、/hoge/plugin直下のrbファイルもロードする
    • また、-pオプションは複数指定できる
  5. fluent.confに定義されている type hogeが今までロードしたファイルにあるか確認。あった場合はそのままロードできる
  • なかった場合はもう少し色々な場所の探索にチャレンジする
    • $LOAD_PATHと、gem installしたディレクトリ直下のどこかにlib/fluent/plugin/_.rbが存在するか

解決策

  1. fluentd自体の$LOAD_PATHに追加
    • 本体に毎回修正かけるの?
  2. in_hogehoge.rbをロードしてくれる場所へコピー(デフォルトプラグイン(/etc/fluent/plugin)など)
    • コピペ?
  3. さっさと作ってgem installできるようにもっていく
    • うーん…
  4. オプション-pを複数指定する
    • 上の場合は -p /hoge/plugin -p /hoge/plugin/fluent-plugin-hogehoge/lib/fluent/plugin としてやればよい
      • fluentd本体でわざわざlib/fluent/plugin下を探すようにコーディングされてるのにこんな指定の仕方するものかな? 何か違う気がする……下みたいに未完成のプラグインが増えていくとオプションガンガン長くなっていく?
/hoge/plugin
├in_hoge.rb
├fluent-plugin-hogehoge
│└lib
│ └fluent
│  └plugin
│   └in_hogehoge.rb
├fluent-plugin-hogehoge2
│└lib
│ └fluent
│  └plugin
│   └in_hogehoge2.rb
└fluent-plugin-hogehoge3
 └lib
  └fluent
   └plugin
    └in_hogehoge3.rb

以下、調査ログ。

調査ログ

セットアップをすると指定したディレクトリにconfigファイルとpluginディレクトリができる。

$ fluentd -s fluentd-hoge
Installed fluentd-hoge/fluent.conf.
$ ls fluentd-hoge/
fluent.conf  plugin

pluginディレクトリにそのままプラグイン(rbファイル)を入れておくと、起動時に読み込まれる。

bundle gemコマンドを使ってプラグインを作っていく

$ cd plugin # プラグインを作るからとりあえずpluginディレクトリに移動する
$ bundle gem fluent-plugin-hogehoge
      create  fluent-plugin-hogehoge/Gemfile
      create  fluent-plugin-hogehoge/Rakefile
      create  fluent-plugin-hogehoge/LICENSE
      create  fluent-plugin-hogehoge/README.md
      create  fluent-plugin-hogehoge/.gitignore
      create  fluent-plugin-hogehoge/fluent-plugin-hogehoge.gemspec
      create  fluent-plugin-hogehoge/lib/fluent-plugin-hogehoge.rb
      create  fluent-plugin-hogehoge/lib/fluent-plugin-hogehoge/version.rb
Initializating git repo in /略/fluent-hoge/plugin/fluent-plugin-hogehoge

なんかいろいろ出来たがちょっとルールがあるようなので整形していく。公式サイトより

Installing custom plugins

To install a plugin, put a ruby script to /etc/fluent/plugin directory.

Or you can create gem package that includes lib/fluent/plugin/_.rb file.
TYPE is in for input plugins, out for output plugins and buf for buffer plugins.
It’s like lib/fluent/plugin/out_mail.rb.
The packaged gem can be distributed and installed using RubyGems.
See Searching plugins.

http://fluentd.org/doc/devel.html
  • プラグインは/etc/fluent/pluginにインストールするといいよ
    • また、-pオプションを指定すればfluent-hoge/plugin下も呼んでくれる
  • Gem packageを作る場合はパッケージの下にlib/fluent/plugin/_.rbって名前にしておいてね
    • 他の方が作ったプラグイン@GitHubを見ていると確かにそうなっていた!
  • はinputプラグインならin、outputプラグインならoutといったように
    • このルールは後ほど効いてくる

ってわけでディレクトリはこんな構成に。

fluent-hoge/
├(略)
├fluent.conf
└plugin/ … (1)
 └fluent-plugin-hogehoge
  ├Gemfile                      # この辺はほぼいじってない
  ├LICENSE                      # この辺はほぼいじってない
  ├README.md                    # この辺はほぼいじってない
  ├Rakefile                     # この辺はほぼいじってない
  ├fluent-plugin-inputs.gemspec # この辺はほぼいじってない
  └lib/
   └fluent/
    └plugin/
     └in_inputs.rb

プラグインも作成。ただ{"plugin"=>"yes"}を出力しまくるだけのソース。

class FluPluGem < Fluent::Input
  Fluent::Plugin.register_input("inputs", self)
  def initialize
    super
  end

  def configure(conf)
    super
  end

  def start
    puts "a starts"
    @thread = Thread.new(&method(:run))
  end

  def run
    loop do
      Fluent::Engine.emit("debugx.debug", Fluent::Engine.now, {"plugin" => "yes"})
      sleep(2)
    end
  end
end

fluent.confにプラグインを追加。

<source>
  type inputs
</source>

この状態で起動!

/usr/local/bin/fluentd -c /略/fluent-hoge/fluent.conf -p /略/fluent-hoge/plugin/
2012-09-07 19:57:48 +0900: starting fluentd-0.10.25
2012-09-07 19:57:48 +0900: reading config file path="/略/fluent-hoge/fluent.conf"
2012-09-07 19:57:48 +0900: adding source type="forward"
2012-09-07 19:57:48 +0900: adding source type="inputs"
2012-09-07 19:57:48 +0900: config error file="/略/fluent-hoge/fluent.conf" error="Unknown input plugin 'inputs'. Run 'gem search -rd fluent-plugin' to find plugins"
2012-09-07 19:57:48 +0900: process finished code=256
2012-09-07 19:57:48 +0900: process died within 1 second. exit.

失敗した…。inputsっていうプラグインなぞないって言われた。でも、プラグイン本体(in_inputs.rb)をplugin直下(ディレクトリ構成の(1)の部分)に持ってくると…

/usr/local/bin/fluentd -c /略/fluent-hoge/fluent.conf -p /略/fluent-hoge/plugin/
2012-09-07 20:02:28 +0900: starting fluentd-0.10.25
2012-09-07 20:02:28 +0900: reading config file path="/略/fluent-hoge/fluent.conf"
2012-09-07 20:02:28 +0900: adding source type="forward"
2012-09-07 20:02:28 +0900: adding source type="inputs"
2012-09-07 20:02:28 +0900: adding source type="thread"
2012-09-07 20:02:28 +0900: adding source type="tail"
2012-09-07 20:02:28 +0900: 'pos_file PATH' parameter is not set to a 'tail' source.
2012-09-07 20:02:28 +0900: this parameter is highly recommended to save the position to resume tailing.
2012-09-07 20:02:28 +0900: adding match pattern="stdd.std" type="file"
2012-09-07 20:02:28 +0900: adding match pattern="devid.devid" type="file"
2012-09-07 20:02:28 +0900: adding match pattern="jenkins.**" type="file"
2012-09-07 20:02:28 +0900: adding match pattern="debug.**" type="stdout"
2012-09-07 20:02:28 +0900: listening fluent socket on 0.0.0.0:24224

起動した…。

Or you can create gem package that includes lib/fluent/plugin/_.rb file.

この文の認識が間違ってるのかなー?

よくわからないので本丸を攻めてみる。pluginの処理を記述しているのはそのものplugin.rb、また、それを呼ぼうとしているはsupervisor.rb,engine.rbあたりっぽい。

ロードしているのはplugin内だろうと思い、いろいろ出力してみる。

  def load_plugins
    dir = File.join(File.dirname(__FILE__), "plugin")
puts "!load_plugins:#{dir}"
    load_plugin_dir(dir)
    load_gem_plugins
  end
  def load_plugin_dir(dir)
    dir = File.expand_path(dir)
puts "!load_plugin_dir:#{dir}"
    Dir.entries(dir).sort.each {|fname|
puts "!load_plugin_dir:#{fname}"
      if fname =~ /\.rb$/
        require File.join(dir, fname)
      end
    }
    nil
  end
  private
  def load_gem_plugins
    return unless defined? Gem
    plugins = Gem.find_files('fluent_plugin')
puts "!load_gem_plugins:#{plugins}"
    plugins.each {|plugin|
      begin
        load plugin
      rescue ::Exception => e
        msg = "#{plugin.inspect}: #{e.message} (#{e.class})"
        $log.warn "Error loading Fluent plugin #{msg}"
      end
    }
  end
  def try_load_plugin(name, type)
puts "!try_load_plugin:#{name}, #{type}"
    case name
    when 'input'
      path = "fluent/plugin/in_#{type}"
    when 'output'
      path = "fluent/plugin/out_#{type}"
    when 'buffer'
      path = "fluent/plugin/buf_#{type}"
    else
      return
    end

    # prefer LOAD_PATH than gems
    files = $LOAD_PATH.map {|lp|
puts "!try_load_plugin:#{lp}"
      lpath = File.join(lp, "#{path}.rb")
      File.exist?(lpath) ? lpath : nil
    }.compact
    unless files.empty?
      # prefer newer version
      require files.sort.last
      return
    end

    # search gems
    if defined?(::Gem::Specification) && ::Gem::Specification.respond_to?(:find_all)
      specs = Gem::Specification.find_all {|spec|
        spec.contains_requirable_file? path
      }

      # prefer newer version
      specs = specs.sort_by {|spec| spec.version }
      if spec = specs.last
        spec.require_paths.each {|lib|
          file = "#{spec.full_gem_path}/#{lib}/#{path}"
          require file
        }
      end

実行結果はこうなる。

$ /usr/local/bin/fluentd -c /略/fluent-hoge/fluent.conf -p /略/fluent-hoge/plugin/
2012-09-07 19:37:32 +0900: starting fluentd-0.10.25
2012-09-07 19:37:32 +0900: reading config file path="/略/fluent-hoge/fluent.conf"

→はじめはfluent本体のpluginを呼んでる。

!load_plugins:/usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.25/lib/fluent/plugin
!load_plugin_dir:/usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.25/lib/fluent/plugin
!load_plugin_dir:.
!load_plugin_dir:..
!load_plugin_dir:buf_file.rb
!load_plugin_dir:buf_memory.rb
!load_plugin_dir:buf_zfile.rb
!load_plugin_dir:in_exec.rb
!load_plugin_dir:in_forward.rb
!load_plugin_dir:in_gc_stat.rb
!load_plugin_dir:in_http.rb
!load_plugin_dir:in_object_space.rb
!load_plugin_dir:in_status.rb
!load_plugin_dir:in_stream.rb
!load_plugin_dir:in_syslog.rb
!load_plugin_dir:in_tail.rb
!load_plugin_dir:out_copy.rb
!load_plugin_dir:out_exec.rb
!load_plugin_dir:out_exec_filter.rb
!load_plugin_dir:out_file.rb
!load_plugin_dir:out_forward.rb
!load_plugin_dir:out_null.rb
!load_plugin_dir:out_roundrobin.rb
!load_plugin_dir:out_stdout.rb
!load_plugin_dir:out_stream.rb
!load_plugin_dir:out_test.rb

→gemはない。

!load_gem_plugins:[]

→ここから他のpluginディレクトリ。まずは/etc/fluent/plugin。

!load_plugin_dir:/etc/fluent/plugin
!load_plugin_dir:.
!load_plugin_dir:..

→そして-pオプションで指定したディレクトリ。

!load_plugin_dir:/略/fluent-hoge/plugin
!load_plugin_dir:.
!load_plugin_dir:..
!load_plugin_dir:fluent-plugin-hogehoge
!load_plugin_dir:simple_thread_plugin.rb

あれ?ディレクトリの上さらっと舐めるだけ?

ここからtypeがaddされている。みつかったものは特に何も表示されず次へ次へ……。

2012-09-07 20:16:24 +0900: adding source type="forward"
2012-09-07 20:16:24 +0900: adding source type="thread"
2012-09-07 20:16:24 +0900: adding source type="tail"
2012-09-07 20:16:24 +0900: 'pos_file PATH' parameter is not set to a 'tail' source.
2012-09-07 20:16:24 +0900: this parameter is highly recommended to save the position to resume tailing.

で、今回見つかってないtypeが出てくると、try_load_pluginが呼ばれる。

2012-09-07 20:16:24 +0900: adding source type="inputs"
!try_load_plugin:input, inputs
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/http_parser.rb-0.5.3/lib
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/msgpack-0.4.7/lib
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/yajl-ruby-1.1.0/lib
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/iobuffer-1.1.2/lib
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/cool.io-1.1.0/lib
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/http_parser.rb-0.5.3/lib
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.25/lib
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/json-1.7.4/lib
!try_load_plugin:/usr/local/lib/ruby/site_ruby/1.9.1
!try_load_plugin:/usr/local/lib/ruby/site_ruby/1.9.1/i686-linux
!try_load_plugin:/usr/local/lib/ruby/site_ruby
!try_load_plugin:/usr/local/lib/ruby/vendor_ruby/1.9.1
!try_load_plugin:/usr/local/lib/ruby/vendor_ruby/1.9.1/i686-linux
!try_load_plugin:/usr/local/lib/ruby/vendor_ruby
!try_load_plugin:/usr/local/lib/ruby/1.9.1
!try_load_plugin:/usr/local/lib/ruby/1.9.1/i686-linux
!try_load_plugin:/usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.25/lib
2012-09-07 20:16:24 +0900: config error file="/略/fluent-hoge/fluent.conf" error="Unknown input plugin 'inputs'. Run 'gem search -rd fluent-plugin' to find plugins"
2012-09-07 20:16:24 +0900: process finished code=256
2012-09-07 20:16:24 +0900: process died within 1 second. exit.

まずプラグインの種類によって読み込まれるパスとファイル名が決まるみたい。

    case name
    when 'input'
      path = "fluent/plugin/in_#{type}"
    when 'output'
      path = "fluent/plugin/out_#{type}"
    when 'buffer'
      path = "fluent/plugin/buf_#{type}"
    else
      return
    end

Or you can create gem package that includes lib/fluent/plugin/_.rb file.

がここで効いてくるわけか。

そしたら$LOAD_PATHの中にfluent/plugin/in_inputs.rbがないか探しに行ってる。そしてそこにもなかった場合、インストールしたGemの中を探す。

    # prefer LOAD_PATH than gems
    files = $LOAD_PATH.map {|lp|
puts "!try_load_plugin:#{lp}"
      lpath = File.join(lp, "#{path}.rb")
      File.exist?(lpath) ? lpath : nil
    }.compact
    unless files.empty?
      # prefer newer version
      require files.sort.last
      return
    end

    # search gems
    if defined?(::Gem::Specification) && ::Gem::Specification.respond_to?(:find_all)
      specs = Gem::Specification.find_all {|spec|
        spec.contains_requirable_file? path
      }

      # prefer newer version
      specs = specs.sort_by {|spec| spec.version }
      if spec = specs.last
        spec.require_paths.each {|lib|
          file = "#{spec.full_gem_path}/#{lib}/#{path}"
          require file
        }
      end

っていう流れになっているらしい。

うーんGemで入れたら読めるんだけどなー。

Git add, commitをした時、中でどんな事が起こっているのか

あらすじ

いまだにファイルがどういうタイミングでリポジトリ、インデックス、ワーキングエリア間を行き来しているのかわからんので色々調べてみることに。

やってみる

まずは、空のREADMEファイルを作ってFirst commit。

$ git init
Initialized empty Git repository in /tmp/repos/.git/
$ touch README
$ git add README
$ git commit -m "first commit"
[master (root-commit) b0d99b3] first commit
 0 files changed
 create mode 100644 README
$ git log --graph --date-order --all --date=short -C -M --pretty=format:"%h"\ %t\ %ad\ %Cblue%cn%Creset\ %Cgreen%d%Creset\ %s
* b0d99b3 543b9be 2012-08-29 kk_Ataka  (HEAD, master) first commit
色々確認

※確認スクリプト

  • rev-parse コマンドはタグとかブランチとかHEAD、HEAD^^など指定したらそのコミットのSHA1が返ってくる
    • 最初のコミットオブジェクトは b0d99b3
  • cat-file コマンドでコミットオブジェクトの内容を確認できる
    • コミットオブジェクトに関連付いているツリーオブジェクトは 543b9be
  • ls-tree コマンドで上のツリーオブジェクトにぶら下がっているツリーオブジェクト、ブロブオブジェクトを確認できる
    • 今は直下にREADMEファイルしかないから e69de29
  • コミットすると.git/objects/の下に3つファイルが作られる
    • SHA1の先頭2桁でディレクトリを作り、その下に残りのSHA1でファイル作成
ゆっくり

READMEを編集してみる。

$ echo "このリポジトリはテスト用です" > README 
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   README
#
no changes added to commit (use "git add" and/or "git commit -a")

で、色々確認。まだ作業エリア(ワーキングツリー)内での変更しかしてない(git addしてない)ので特に変わらず。

addしたとき

ここでREADMEファイルをadd。

$ git add README
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   README
#

再度、色々確認。

リポジトリにコミットされているREADME e69de29 の他にde7e855 というオブジェクトがステージされている。おまけに.git/objectsの下にも。

  • addした時点で、gitリポジトリの中に入れ込まれている?
  • リセットしても.git/objectsの下からは消えない(ただ、この状態でほったらかしておくとどこからも参照されないからいずれgcされて死ぬ?)
$ git reset HEAD README
Unstaged changes after reset:
M	README
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   README
#
no changes added to commit (use "git add" and/or "git commit -a")
$ find .git/objects
.git/objects/de/7e8558a365886d75f9c1ac6861693be19bdc53
.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
.git/objects/54/3b9bebdc6bd5c4b22136034a95dd097a57d3dd
.git/objects/b0/d99b34f1e2497a78619de4fde08b62f2d851f7
$ git cat-file -p de7e8558
このリポジトリはテスト用です
commitしたとき

もう一回addして今度はコミット。

$ git add README
$ git commit -m "README追記"
[master 30eab42] README追記
 1 file changed, 1 insertion(+)

すごい見づらいけど、.git/objectsの下に2つファイルが増えている。追ってみると今回のコミットオブジェクト 30eab42 ツリーオブジェクト 9dd7dd4 …の下にさっきaddした時にできていたREADME(de7e855)

まとめ

以上の事から

  • addした時、既にリポジトリにファイルは追加されている。また、インデックスの参照先も更新されている
初期状態

addした後

  • commitした時、インデックスがそのまま次のコミットのツリーオブジェクトになる。そのツリーオブジェクトを取りまとめるコミットオブジェクトを作成し、tree情報とparent情報を持たせる
  • で、ブランチの参照先を更新してやる
commitした後

でいいのかなあ。

Fluentdのプラグインを作成してみる(練習用)

前回までのあらすじ

Fluentdというログ収集ツールを使ってApacheのログを取得するまで - kk_Atakaの日記 で、confファイルのコメントアウトを外してとりあえず動くっていうところまではいけた。今度は簡単なプラグインを作ってみる。

ひたすら何かを吐き続けるプラグイン

Inputプラグインの場合

Fluent::Inputクラスを継承する。

class SimpleInput < Fluent::Input
  # 第一引数がプラグインの名前、<source> typeに指定される
  Fluent::Plugin.register_input("simple_in", self)

  def start
    @thread = Thread.new(&method(:run))
  end

  def run
    loop do
      # emitメソッドの第一引数が <match **.**> の**に該当すればその形式で出力される
      # fluent.confでdebug.**はstdoutで出力すると定義されている
      Fluent::Engine.emit("debug.debug", Fluent::Engine.now, {"simple" => "debudebu"})
      # fluent.confに<match simple.output> type simple_outを定義したので、以下のOutput形式で出力される
      Fluent::Engine.emit("simple.output", Fluent::Engine.now, {"simple" => "simout"})
      sleep(1)
    end
  end
end

上のソースには書かれていないけど、他にもメソッドがいくつか。(公式サイトより)

  • configure メソッド
    • スタート前に呼び出される
    • confハッシュにパラメータを入れられる?
    • エラーはFluent::ConfigErrorを投げる
  • start メソッド
    • スタート時に呼ばれる
    • ここでスレッドを作ったりファイルをオープンしたり
  • shutdown メソッド
    • シャットダウン時に呼ばれる
    • スレッドやファイルのクローズはここで。
  • イベントをsubmitするためにはFluent::Engine.emit(tag, time, record)メソッドを使う
    • tagはString、timeはUnixTime、recordはハッシュ。
Outputプラグインの場合

まず種類がいろいろ。

  • Buffered output plugins Fluent::BufferedOutputクラスを継承
  • Time sliced output plugins Fluent::TimeSlicedOutputクラスを継承/Buffered output pluginを継承したプラグイン?
  • Non-buffered output plugins Fluent::Outputクラスを継承

今回はFluent::Outputを使った。

class SimpleOutput < Fluent::Output
  # 第一引数がプラグインの名前、<match> の typeに指定される
  Fluent::Plugin.register_output("simple_out", self)

  def emit(tag, es, chain)
    chain.next
    es.each do |time, record|
      # 出力内容
      puts "simple_out: #{time} - #{record}"
    end
  end
end
  • configure, start, shutdown はInputプラグイン同様にある
  • emit メソッドはイベントに到達した場合呼ばれる
    • esはFluent::EventStreamオブジェクト(イベントが入ってる?)
      • eachで回すとイベントを検索できる。
    • chainはトランザクションメッセージのオブジェクト。
      • 適切なポイントでchain.nextを呼ぶとエラーを吐いたときにrollbackしてくれる。
プラグインをデバッグしたい場合
  • fluentdコマンド実行するときに-vvオプションを指定する事でデバッグメッセージが表示できる。
  • デバッグにはstdoutかcopyが役に立つ
    • stdoutはマッチしたイベントをコンソールに出力する
    • copyはマッチしたイベントを複数のプラグインにコピーできる
      • stdoutと一緒に使う事ができる

Configファイル

# Inputプラグイン指定
<source>
  type simple_in
</source>

# Fluent::Engine.emit("simple.output", Fluent::Engine.now, {"simple" => "simout"})
# simple.outputタグ指定した方はsimple_output(Outputプラグイン)出力にマッチする
<match simple.output>
  type simple_out
</match>

#
## match tag=debug.** and dump to console
# Fluent::Engine.emit("debug.debug", Fluent::Engine.now, {"simple" => "debudebu"})  
# debug.debugタグ指定した方はstdout出力にマッチする
<match debug.**>
  type stdout
</match>

これを実行すると…。

$ bundle exec fluentd -c /home/kk_Ataka/fluentd/fluent.conf -p /home/kk_Ataka/fluentd/plugin/
2012-08-17 22:19:41 +0900: starting fluentd-0.10.24
2012-08-17 22:19:41 +0900: reading config file path="/home/kk_Ataka/fluentd/fluent.conf"
(略)
2012-08-17 22:19:41 +0900: following tail of /usr/local/apache2/logs/access_log
2012-08-17 22:19:41 +0900: following tail of /usr/local/apache2/logs/error_log
2012-08-17 22:19:41 +0900 debug.debug: {"simple":"debudebu"}
simple_out: 1345209581 - {"simple"=>"simout"}
2012-08-17 22:19:42 +0900 debug.debug: {"simple":"debudebu"}
simple_out: 1345209582 - {"simple"=>"simout"}
2012-08-17 22:19:43 +0900 debug.debug: {"simple":"debudebu"}
simple_out: 1345209583 - {"simple"=>"simout"}
2012-08-17 22:19:44 +0900 debug.debug: {"simple":"debudebu"}
simple_out: 1345209584 - {"simple"=>"simout"}
2012-08-17 22:19:45 +0900 debug.debug: {"simple":"debudebu"}
simple_out: 1345209585 - {"simple"=>"simout"}
2012-08-17 22:19:46 +0900 debug.debug: {"simple":"debudebu"}
simple_out: 1345209586 - {"simple"=>"simout"}

1秒刻みでプラグインに記述した内容が出力される。こっちが標準のstdout出力。

2012-08-17 22:19:46 +0900 debug.debug: {"simple":"debudebu"}

こっちが自分で記述したOutputプラグイン書式の出力。

simple_out: 1345209586 - {"simple"=>"simout"}

Sphinxの見出しについて学びなおし

前回までのあらすじ

見出し周辺を読み直し

セクションのヘッダは、セクションのタイトルを句読点などの記号の文字でアンダーラインを引くことで設定します。必要に応じてでオーバーラインも併用することができます。

使用していない種類のアンダーラインが出てくると、見出しのレベルが一段変わる、というルールになっています。

. These are a single line of text (one or more words) with adornment: an underline alone, or an underline and an overline together, in dashes "-----", equals "======", tildes "~~~~~~" or any of the non-alphanumeric characters = - ` : ' " ~ ^ _ * + # < > that you feel comfortable with.

つまり……

  • 英数字以外の記号でアンダーラインを引くとセクションヘッダ
    • アンダーラインとオーバーラインで囲むのも別の定義として判断される
    • 己がしっくりくるものを選んでよい
  • 初めて出てきた順に見出しレベルが割り当てられる

確認

Pythonドキュメント慣例にならってこういうソースを書くと、

##################
上下 # 部
##################

ここは上下 # で囲った部の文章。

下 # 部
##############

ここは下 # で囲った部の文章。

*******************
上下 * 章
*******************

ここは上下 * で囲った章の文章。

下 * 章
*******************

ここは下 * で囲った章の文章。

===========================
上下 = セクション
===========================

ここは上下 = で囲ったセクションの文章。

下 = セクション
===========================

ここは下 = で囲ったセクションの文章。

---------------------------
上下 - サブセクション
---------------------------

ここは上下 - で囲ったサブセクションの文章。

下 - サブセクション
---------------------------

ここは下 - で囲ったサブセクションの文章。

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
上下 ^ サブサブセクション
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

ここは上下 ^ で囲ったサブサブセクションの文章。

下 ^ サブサブセクション
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

ここは下 ^ で囲ったサブサブセクションの文章。

"""""""""""""""""""""""""""""""""
上下 " パラグラフ
"""""""""""""""""""""""""""""""""

ここは上下 " で囲ったパラグラフの文章。

下 " パラグラフ
"""""""""""""""""""""""""""""""""

ここは下 " で囲ったパラグラフの文章。

htmlはこう生成される。

<h1>上下 # 部<a class="headerlink" href="#id1" title="Permalink to this headline"></a></h1>
<p>ここは上下 # で囲った部の文章。</p>
<div class="section" id="id2">
<h2>下 # 部<a class="headerlink" href="#id2" title="Permalink to this headline"></a></h2>
<p>ここは下 # で囲った部の文章。</p>
<div class="section" id="id3">
<h3>上下 * 章<a class="headerlink" href="#id3" title="Permalink to this headline"></a></h3>
<p>ここは上下 * で囲った章の文章。</p>
<div class="section" id="id4">
<h4>下 * 章<a class="headerlink" href="#id4" title="Permalink to this headline"></a></h4>
<p>ここは下 * で囲った章の文章。</p>
<div class="section" id="id5">
<h5>上下 = セクション<a class="headerlink" href="#id5" title="Permalink to this headline"></a></h5>
<p>ここは上下 = で囲ったセクションの文章。</p>
<div class="section" id="id6">
<h6>下 = セクション<a class="headerlink" href="#id6" title="Permalink to this headline"></a></h6>
<p>ここは下 = で囲ったセクションの文章。</p>
<div class="section" id="id7">
<h7>上下 - サブセクション<a class="headerlink" href="#id7" title="Permalink to this headline"></a></h7>
<p>ここは上下 - で囲ったサブセクションの文章。</p>
<div class="section" id="id8">
<h8>下 - サブセクション<a class="headerlink" href="#id8" title="Permalink to this headline"></a></h8>
<p>ここは下 - で囲ったサブセクションの文章。</p>
<div class="section" id="id9">
<h9>上下 ^ サブサブセクション<a class="headerlink" href="#id9" title="Permalink to this headline"></a></h9>
<p>ここは上下 ^ で囲ったサブサブセクションの文章。</p>
<div class="section" id="id10">
<h10>下 ^ サブサブセクション<a class="headerlink" href="#id10" title="Permalink to this headline"></a></h10>
<p>ここは下 ^ で囲ったサブサブセクションの文章。</p>
<div class="section" id="id11">
<h11>上下 &#8221; パラグラフ<a class="headerlink" href="#id11" title="Permalink to this headline"></a></h11>
<p>ここは上下 &#8221; で囲ったパラグラフの文章。</p>
<div class="section" id="id12">
<h12>&#8221; パラグラフ<a class="headerlink" href="#id12" title="Permalink to this headline"></a></h12>
<p>ここは下 &#8221; で囲ったパラグラフの文章。</p>

まあ、h12まであるページとか見たことないんだけど、新しいものを定義し続けるとどんどん深くなる。

ここでソースを以下のように編集。がっつりカットしつつ、適当に数個残し。あと<<<で新しいセクションを定義してみた。

  # 部
##############

ここは下 # で囲った部の文章。

*******************
上下 * 章
*******************

ここは上下 * で囲った章の文章。

下 - サブセクション
---------------------------

ここは下 - で囲ったサブセクションの文章。

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
新しく作った
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

<<<<<で新しくセクションを作ってみた。

htmlはこう生成される。

<h1>下 # 部<a class="headerlink" href="#id1" title="Permalink to this headline"></a></h1>
<p>ここは下 # で囲った部の文章。</p>
<div class="section" id="id2">
<h2>上下 * 章<a class="headerlink" href="#id2" title="Permalink to this headline"></a></h2>
<p>ここは上下 * で囲った章の文章。</p>
<div class="section" id="id3">
<h3>下 - サブセクション<a class="headerlink" href="#id3" title="Permalink to this headline"></a></h3>
<p>ここは下 - で囲ったサブセクションの文章。</p>
<div class="section" id="id4">
<h4>新しく作った<a class="headerlink" href="#id4" title="Permalink to this headline"></a></h4>
<p>&lt;&lt;&lt;&lt;&lt;で新しくセクションを作ってみた。</p>

うーんなるほど。

ちゃんとドキュメント読まず、他の記法と同じノリでこれはこの見出しって決めつけてしまっていた!