読者です 読者をやめる 読者になる 読者になる

fluent-plugin-fastladderができました

いつまで目grepするつもりですか?

https://github.com/laiso/fluent-plugin-fastladder

https://github.com/laiso/fluent-plugin-fastladder/raw/master/doc/screenshot.png

ログを購読して全行読みたいビジネスパーソンに最適のソリューションが完成しました。

これからは目tailして脳cachedから取り出すことができるでしょう。

コード

実用的なrubyコードってはじめて書いてみたのでエキスパートの人はレビューしてね。

# -*- coding: utf-8 -*-
module Fluent
  class FastladderOutput < Fluent::Output
    Fluent::Plugin.register_output('fastladder', self)

    config_param :base_url, :string, :default => 'http://localhost:3000'
    config_param :api_key, :string, :default => nil
    config_param :key, :string, :default => nil

    attr_reader :feeds

    def initialize
      super
      require 'json'
      require 'net/http'
    end

    def configure(conf)
      super
      if @api_key.nil?
        raise Fluent::ConfigError, "'api_key' must be specifed."
      end

      @feeds = conf.elements.select { |element|
        element.name == 'feed'
      }.each { |element|
        if element.has_key?("pattern")
          element["regex"] = Regexp.new(element["pattern"])
        end

        element.keys.each do |k|
          # read and throw away to supress unread configuration warning
          element[k]
        end
      }
    end

    def emit(tag, es, chain)
      records = es.map do |time,record|
        [tag, time, record]
      end

      update_feed records

      chain.next
    end

    private
    def update_feed(records)
      items = {}
      records.each do |tag, time, record|
        feed = apply_feed tag
        unless feed.is_a?(Hash)
          $log.warn "Could not find a valid feed. tag(#{tag})"
        end
        unless items[feed]
          items[feed] = []
        end
        items[feed] << create_item(tag, time, record, feed)
      end

      items.each do |feed, items|
        Net::HTTP.post_form URI("#{@base_url}/rpc/update_feeds"), { 
            "api_key"=> @api_key,
            "feeds"=> JSON.dump(items)
        }
      end
    end

    private
    def apply_feed(tag)
      @feeds.select {|feed| feed['regex'] =~ tag}.shift
    end

    private
    def create_item(tag, time, record, feed)
      {
        "feedlink"=> "#fluent.feed.#{feed['key']}",
        "feedtitle"=> feed['title'] || 'NO TITLE',
        "feeddescription"=> feed['description'] || '',
        "link"=> "#fluent.feed.#{tag}.#{time}",
        "body"=> record.to_s,
        "category"=> feed['category'] || 'log',
        "title"=> time.to_s,
      }
    end

  end
end

おまけ

  • エンタープライズplagger*1ことFluentdを最近触っていて、勉強がてらにプラグインの作り方を調べていた。
  • Fastladder側にもインストール型フィードリーダーの醍醐味である非公開情報の購読にちょうどいいRPCもついたことだし作ってみた。他の人のプラグイン見てコピペすれば楽勝かと思ってたんだけど最終的にはFastladderRailsアプリの中のコードまで読んでデバッグしてたので休日が潰れてしまった
  • Fastladderがモリモリアップデートされていて非文書化APIでもあるのでなんとかgemsには上げずにおく。
  • iOS開発まわりでもどんどんRubyで書かれた便利なツールが出てきていて、そういうツールをハックしたりするのにどうしてもRubyを読み書きする必要が出てきた
  • items.each do |item| end みたいな記法ですらハイハイ黒魔法黒魔法で済ませていたので昨日役割を知ったのだった。順序が逆だろうけどObjective-Cっぽいと思った。

*1:https://twitter.com/todesking/statuses/299135681953943553

*2:@AntiBayesianに教えてもらいました

*3:@twittに教えてもらいました