在开发和使用一些 logstash 自定义插件的时候,几乎不可避免会导入其他 RubyGems 模块 —— 因为都用不上模块的小型处理,直接写在 filters/ruby 插件配置里就够了 —— 这时候,运行 logstash 命令可能会发现一个问题:这个 gem 模块一直是 “no found” 状态。

这其实是因为我们一般是通过 java 命令来运行的 logstash,这时候它回去寻找的 Gem 路径跟我们预计中的是不一致的。

要查看 logstash 运行时实际的 Gem 查找路径,首先要通过 ps aux 命令确定 ruby 的实际运行方式:

$ ps uax|grep logstash
raochenlin      27268  38.0  4.3  3268156 181344 s003  S+    7:10PM   0:22.36 /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java -Xmx500m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -Djava.awt.headless=true -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -jar /Downloads/logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar -I/Users/raochenlin/Downloads/logstash-1.4.2/lib /Users/raochenlin/Downloads/logstash-1.4.2/lib/logstash/runner.rb agent -f test.conf

看,实际的运行方式应该是:java -jar logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar -Ilogstash-1.4.2/lib logstash-1.4.2/lib/logstash/runner.rb 这样。

那么我们查看 gem 路径的命令也就知道怎么写了:

java -jar logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar `which gem` env

你会看到这样的输出:

RubyGems Environment:
- RUBYGEMS VERSION: 2.1.9
- RUBY VERSION: 1.9.3 (2014-02-24 patchlevel 392) [java]
- INSTALLATION DIRECTORY: file:/Downloads/logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar!/META-INF/jruby.home/lib/ruby/gems/shared
- RUBY EXECUTABLE: java -jar /Downloads/logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar
- EXECUTABLE DIRECTORY: file:/Downloads/logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar!/META-INF/jruby.home/bin
- SPEC CACHE DIRECTORY: /.gem/specs
- RUBYGEMS PLATFORMS:
- ruby
- universal-java-1.7
- GEM PATHS:
- file:/Downloads/logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar!/META-INF/jruby.home/lib/ruby/gems/shared
- /.gem/jruby/1.9
- GEM CONFIGURATION:
- :update_sources => true
- :verbose => true
- :backtrace => false
- :bulk_threshold => 1000
- “install” => “–no-rdoc –no-ri –env-shebang”
- “update” => “–no-rdoc –no-ri –env-shebang”
- :sources => [“http://ruby.taobao.org/”]
- REMOTE SOURCES:
- http://ruby.taobao.org/
- SHELL PATH:
- /usr/bin
- /bin
- /usr/sbin
- /sbin
- /usr/local/bin

看到其中的 GEM PATHS 部分,是一个以 file: 开头的路径!也就是说,要求所有的 gem 包都打包在这个 jruby-complete-1.7.11.jar 里面才认。

所以我们需要把额外的 gem 包,也加入这个 jar 里:

jar uf jruby-completa-1.7.11.jar META-INF/jruby.home/lib/ruby/1.9/CUSTOM_RUBY_GEM_LIB

注:加入 jar 是用的相对路径,所以前面这串目录要提前创建然后复制文件进去。

当然,其实还有另一个办法。

让我们返回去再看一次 logstash 的进程,在 jar 后面,还有一个 -I 参数!所以,其实我们还可以把文件安装在 logstash-1.4.2/lib 目录下去。

最后,你可能会问:那 --pluginpath 参数指定的位置可不可以呢?

答案是:也可以。

这个参数指定的位置在 logstash-1.4.2/lib/logstash/agent.rb 中,被加入了 $LOAD_PATH 中:

  def configure_plugin_path(paths)
    paths.each do |path|
      if !Dir.exists?(path)
        warn(I18n.t("logstash.agent.configuration.plugin_path_missing",
                    :path => path))
      end
      plugin_glob = File.join(path, "logstash", "{inputs,codecs,filters,outputs}", "*.rb")
      if Dir.glob(plugin_glob).empty?
        @logger.warn(I18n.t("logstash.agent.configuration.no_plugins_found",
                    :path => path, :plugin_glob => plugin_glob))
      end
      @logger.debug("Adding plugin path", :path => path)
      $LOAD_PATH.unshift(path)
    end
  end

$LOAD_PATH 是 Ruby 的一个特殊变量,类似于 Perl 的 @INC 或者 Java 的 class_path 。在这个数组里的路径下的文件,都可以被 require 导入。

可以运行如下命令查看:

$ java -jar logstash-1.4.2/vendor/jar/jruby-complete-1.7.11.jar -e 'p $LOAD_PATH'
["file:/Users/raochenlin/Downloads/logstash-1.4.2/vendor/jar/rar/jruby-complete-1.7.11.jar!/META-INF/jruby.home/lib/ruby/1.9/site_ruby", "file:/Users/raochenlin/Downloads/logstash-1.4.2/vendor/jar/rar/jruby-complete-1.7.11.jar!/META-INF/jruby.home/lib/ruby/shared", "file:/Users/raochenlin/Downloads/logstash-1.4.2/vendor/jar/rar/jruby-complete-1.7.11.jar!/META-INF/jruby.home/lib/ruby/1.9"]

这三种方式,你喜欢哪种呢?