├── .github ├── ISSUE_TEMPLATE.md └── workflows │ ├── linux.yml │ └── windows.yml ├── .gitignore ├── AUTHORS ├── Gemfile ├── README.md ├── Rakefile ├── VERSION ├── fluent-plugin-hipchat.gemspec ├── lib └── fluent │ └── plugin │ └── out_hipchat.rb └── test ├── plugin └── out_hipchat.rb └── test_helper.rb /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Problem 2 | 3 | ... 4 | 5 | #### Steps to replicate 6 | 7 | Provide example config and message 8 | 9 | #### Expected Behavior 10 | 11 | ... 12 | 13 | #### Your environment 14 | 15 | * OS version 16 | * paste result of ``fluentd --version`` or ``td-agent --version`` 17 | * plugin version 18 | * paste boot log of fluentd or td-agent 19 | * paste result of ``fluent-gem list``, ``td-agent-gem list`` or your Gemfile.lock 20 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Testing on Ubuntu 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | ruby: 12 | - 2.5 13 | - 2.6 14 | - 2.7 15 | - 3.0 16 | os: 17 | - ubuntu-latest 18 | name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: ruby/setup-ruby@v1 22 | with: 23 | ruby-version: ${{ matrix.ruby }} 24 | - name: unit testing 25 | env: 26 | CI: true 27 | run: | 28 | gem install bundler rake 29 | bundle install --jobs 4 --retry 3 30 | bundle exec rake test 31 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Testing on Windows 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | ruby: [ '2.5', '2.6', '2.7', '3.0' ] 12 | os: 13 | - windows-latest 14 | name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: ${{ matrix.ruby }} 20 | - name: unit testing 21 | env: 22 | CI: true 23 | run: | 24 | gem install bundler rake 25 | bundle install --jobs 4 --retry 3 26 | bundle exec rake test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /fluent/ 2 | /pkg/ 3 | /coverage/ 4 | /vendor/ 5 | Gemfile.lock 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Yuichi Tateno 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | gem "simplecov", :require => false 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fluent event to hipchat plugin. 2 | 3 | [![Build Status](https://travis-ci.org/fluent-plugins-nursery/fluent-plugin-hipchat.svg?branch=master)](https://travis-ci.org/fluent-plugins-nursery/fluent-plugin-hipchat) 4 | 5 | # Requirements 6 | 7 | | fluent-plugin-hipchat | fluentd | ruby | 8 | |-----------------------|------------|--------| 9 | | >= 0.3.0 | >= v0.14.0 | >= 2.1 | 10 | | < 0.3.0 | >= v0.12.0 | >= 1.9 | 11 | 12 | # Installation 13 | 14 | $ fluent-gem install fluent-plugin-hipchat 15 | 16 | # Usage 17 | 18 | 19 | @type hipchat 20 | api_token XXX 21 | default_room my_room 22 | default_from fluentd 23 | default_color yellow 24 | default_notify 0 25 | default_format html 26 | default_timeout 3 # HipChat API Request Timeout Seconds (default 3) 27 | key_name message 28 | 29 | # proxy settings 30 | # http_proxy_host localhost 31 | # http_proxy_port 8080 32 | # http_proxy_user username 33 | # http_proxy_pass password 34 | 35 | 36 | fluent_logger.post('hipchat', { 37 | :from => 'alice', 38 | :message => 'Hello
World!', 39 | :color => 'red', 40 | :room => 'my_room', 41 | :notify => 1, 42 | :format => 'text', 43 | }) 44 | 45 | # set topic 46 | fluent_logger.post('hipchat', { 47 | :from => 'alice', 48 | :topic => 'new topic', 49 | :room => 'my_room', 50 | }) 51 | 52 | 53 | # Copyright 54 | 55 | Copyright (c) 2012- Yuichi Tateno 56 | 57 | # License 58 | 59 | Apache License, Version 2.0 60 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rake/testtask' 5 | 6 | Rake::TestTask.new(:test) do |test| 7 | test.libs << 'lib' << 'test' 8 | test.test_files = FileList['test/plugin/*.rb'] 9 | test.verbose = true 10 | end 11 | 12 | task :coverage do |t| 13 | ENV['SIMPLE_COV'] = '1' 14 | Rake::Task["test"].invoke 15 | end 16 | 17 | task :default => [:build] 18 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.3.0 2 | -------------------------------------------------------------------------------- /fluent-plugin-hipchat.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | $:.push File.expand_path('../lib', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.name = "fluent-plugin-hipchat" 6 | gem.description = "fluent HipChat plugin" 7 | gem.homepage = "https://github.com/fluent-plugins-nursery/fluent-plugin-hipchat" 8 | gem.summary = gem.description 9 | gem.version = File.read("VERSION").strip 10 | gem.authors = ["Yuichi Tateno"] 11 | gem.email = "hotchpotch@gmail.com" 12 | gem.has_rdoc = false 13 | gem.files = `git ls-files`.split("\n") 14 | gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 15 | gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 16 | gem.require_paths = ['lib'] 17 | 18 | gem.add_dependency "fluentd", [">= 0.14.0", "< 2"] 19 | gem.add_dependency "hipchat-api", ">= 1.0.0" 20 | 21 | gem.add_development_dependency "rake", ">= 0.9.2" 22 | gem.add_development_dependency "simplecov", ">= 0.5.4" 23 | gem.add_development_dependency "rr", ">= 1.0.0" 24 | gem.add_development_dependency "pry" 25 | gem.add_development_dependency "test-unit", ">= 3.1.0" 26 | gem.add_development_dependency "test-unit-rr", ">= 1.0.5" 27 | gem.add_development_dependency "webrick" 28 | end 29 | -------------------------------------------------------------------------------- /lib/fluent/plugin/out_hipchat.rb: -------------------------------------------------------------------------------- 1 | require 'hipchat-api' 2 | require 'fluent/plugin/output' 3 | 4 | module Fluent::Plugin 5 | class HipchatOutput < Output 6 | COLORS = %w(yellow red green purple gray random) 7 | FORMAT = %w(html text) 8 | DEFAULT_BUFFER_TYPE = "memory" 9 | Fluent::Plugin.register_output('hipchat', self) 10 | 11 | helpers :compat_parameters 12 | 13 | config_param :api_token, :string, secret: true 14 | config_param :default_room, :string, default: nil 15 | config_param :default_color, :string, default: 'yellow' 16 | config_param :default_from, :string, default: 'fluentd' 17 | config_param :default_notify, :bool, default: false 18 | config_param :default_format, :string, default: 'html' 19 | config_param :key_name, :string, default: 'message' 20 | config_param :default_timeout, :time, default: nil 21 | config_param :http_proxy_host, :string, default: nil 22 | config_param :http_proxy_port, :integer, default: nil 23 | config_param :http_proxy_user, :string, default: nil 24 | config_param :http_proxy_pass, :string, default: nil 25 | config_param :flush_interval, :time, default: 1 26 | 27 | config_section :buffer do 28 | config_set_default :@type, DEFAULT_BUFFER_TYPE 29 | config_set_default :chunk_keys, ['tag'] 30 | end 31 | 32 | attr_reader :hipchat 33 | 34 | def configure(conf) 35 | compat_parameters_convert(conf, :buffer) 36 | super 37 | 38 | @hipchat = HipChat::API.new(conf['api_token']) 39 | @default_room = conf['default_room'] 40 | @default_notify = conf['default_notify'] || 0 41 | @default_timeout = conf['default_timeout'] 42 | if conf['http_proxy_host'] 43 | HipChat::API.http_proxy( 44 | conf['http_proxy_host'], 45 | conf['http_proxy_port'], 46 | conf['http_proxy_user'], 47 | conf['http_proxy_pass']) 48 | end 49 | raise Fluent::ConfigError, "'tag' in chunk_keys is required." if not @chunk_key_tag 50 | end 51 | 52 | def format(tag, time, record) 53 | [time, record].to_msgpack 54 | end 55 | 56 | def formatted_to_msgpack_binary 57 | true 58 | end 59 | 60 | def multi_workers_ready? 61 | true 62 | end 63 | 64 | def write(chunk) 65 | chunk.msgpack_each do |(time,record)| 66 | begin 67 | send_message(record) if record[@key_name] 68 | set_topic(record) if record['topic'] 69 | rescue => e 70 | log.error("HipChat Error:", error_class: e.class, error: e.message) 71 | end 72 | end 73 | end 74 | 75 | def send_message(record) 76 | room = record['room'] || @default_room 77 | from = record['from'] || @default_from 78 | message = record[@key_name] 79 | if record['notify'].nil? 80 | notify = @default_notify 81 | else 82 | notify = record['notify'] ? 1 : 0 83 | end 84 | color = COLORS.include?(record['color']) ? record['color'] : @default_color 85 | message_format = FORMAT.include?(record['format']) ? record['format'] : @default_format 86 | @hipchat.set_timeout(@default_timeout.to_i) unless @default_timeout.nil? 87 | response = @hipchat.rooms_message(room, from, message, notify, color, message_format) 88 | raise StandardError, response['error'][@key_name].to_s if defined?(response['error'][@key_name]) 89 | end 90 | 91 | def set_topic(record) 92 | room = record['room'] || @default_room 93 | from = record['from'] || @default_from 94 | topic = record['topic'] 95 | response = @hipchat.rooms_topic(room, topic, from) 96 | raise StandardError, response['error'][@key_name].to_s if defined?(response['error'][@key_name]) 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /test/plugin/out_hipchat.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'fluent/plugin/out_hipchat' 3 | 4 | class HipchatOutputTest < Test::Unit::TestCase 5 | include Fluent::Test::Helpers 6 | 7 | def setup 8 | super 9 | Fluent::Test.setup 10 | end 11 | 12 | CONFIG = %[ 13 | type hipchat 14 | api_token testtoken 15 | default_room testroom 16 | default_from testuser 17 | default_color yellow 18 | ] 19 | 20 | CONFIG_FOR_PROXY = %[ 21 | http_proxy_host localhost 22 | http_proxy_port 8080 23 | http_proxy_user user 24 | http_proxy_pass password 25 | ] 26 | 27 | def create_driver(conf = CONFIG) 28 | Fluent::Test::Driver::Output.new(Fluent::Plugin::HipchatOutput) { 29 | }.configure(conf) 30 | end 31 | 32 | def test_configure_default 33 | d = create_driver %[ 34 | type hipchat 35 | api_token testtoken 36 | ] 37 | assert_equal "yellow", d.instance.default_color 38 | assert_equal "fluentd", d.instance.default_from 39 | assert_equal 0, d.instance.default_notify 40 | assert_equal "html", d.instance.default_format 41 | assert_equal "message", d.instance.key_name 42 | assert_equal 1, d.instance.flush_interval 43 | end 44 | 45 | def test_format 46 | d = create_driver 47 | stub(d.instance.hipchat).rooms_message('testroom', 'testuser', 'foo', 0, 'red', 'html') 48 | assert_equal d.instance.hipchat.instance_variable_get(:@token), 'testtoken' 49 | time = event_time 50 | d.run(default_tag: "test") do 51 | d.feed(time, {'message' => 'foo', 'color' => 'red'}) 52 | end 53 | assert_equal [time, {'message' => 'foo', 'color' => 'red'}].to_msgpack, d.formatted[0] 54 | end 55 | 56 | def test_default_message 57 | d = create_driver(<<-EOF) 58 | type hipchat 59 | api_token xxx 60 | default_room testroom 61 | EOF 62 | stub(d.instance.hipchat).rooms_message('testroom', 'fluentd', 'foo', 0, 'yellow', 'html') 63 | assert_equal d.instance.hipchat.instance_variable_get(:@token), 'xxx' 64 | d.run(default_tag: "test") do 65 | d.feed({'message' => 'foo'}) 66 | end 67 | end 68 | 69 | def test_set_default_timeout 70 | d = create_driver(<<-EOF) 71 | type hipchat 72 | api_token xxx 73 | default_timeout 5 74 | EOF 75 | stub(d.instance.hipchat).set_timeout(5) 76 | d.run(default_tag: "test") do 77 | d.feed({'message' => 'foo'}) 78 | end 79 | end 80 | 81 | def test_message 82 | d = create_driver 83 | stub(d.instance.hipchat).rooms_message('testroom', 'testuser', 'foo', 0, 'red', 'html') 84 | assert_equal d.instance.hipchat.instance_variable_get(:@token), 'testtoken' 85 | d.run(default_tag: "test") do 86 | d.feed({'message' => 'foo', 'color' => 'red'}) 87 | end 88 | end 89 | 90 | def test_message_override 91 | d = create_driver 92 | stub(d.instance.hipchat).rooms_message('my', 'alice', 'aaa', 1, 'random', 'text') 93 | d.run(default_tag: "test") do 94 | d.feed( 95 | { 96 | 'room' => 'my', 97 | 'from' => 'alice', 98 | 'message' => 'aaa', 99 | 'notify' => true, 100 | 'color' => 'random', 101 | 'format' => 'text', 102 | } 103 | ) 104 | end 105 | end 106 | 107 | def test_topic 108 | d = create_driver 109 | stub(d.instance.hipchat).rooms_topic('testroom', 'foo', 'testuser') 110 | d.run(default_tag: "test") do 111 | d.feed({'topic' => 'foo'}) 112 | end 113 | end 114 | 115 | def test_set_topic_response_error 116 | d = create_driver 117 | stub(d.instance.hipchat).rooms_topic('testroom', 'foo', 'testuser') { 118 | {'error' => { 'code' => 400, 'type' => 'Bad Request', 'message' => 'Topic body must be between 1 and 250 characters.' } } 119 | } 120 | stub($log).error("HipChat Error:", error_class: StandardError, error: 'Topic body must be between 1 and 250 characters.') 121 | d.run(default_tag: "test") do 122 | d.feed({'topic' => 'foo'}) 123 | end 124 | end 125 | 126 | def test_send_message_response_error 127 | d = create_driver 128 | stub(d.instance.hipchat).rooms_message('testroom', '', 'foo', 0, 'yellow', 'html') { 129 | {'error' => { 'code' => 400, 'type' => 'Bad Request', 'message' => 'From name may not contain HTML.' } } 130 | } 131 | stub($log).error("HipChat Error:", error_class: StandardError, error: 'From name may not contain HTML.') 132 | d.run(default_tag: "test") do 133 | d.feed({'from' => '', 'message' => 'foo'}) 134 | end 135 | end 136 | 137 | def test_color_validate 138 | d = create_driver 139 | stub(d.instance.hipchat).rooms_message('testroom', 'testuser', 'foo', 0, 'yellow', 'html') 140 | d.run(default_tag: "test") do 141 | d.feed({'message' => 'foo', 'color' => 'invalid'}) 142 | end 143 | end 144 | 145 | def test_http_proxy 146 | create_driver(CONFIG + CONFIG_FOR_PROXY) 147 | assert_equal 'localhost', HipChat::API.default_options[:http_proxyaddr] 148 | assert_equal '8080', HipChat::API.default_options[:http_proxyport] 149 | assert_equal 'user', HipChat::API.default_options[:http_proxyuser] 150 | assert_equal 'password', HipChat::API.default_options[:http_proxypass] 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | 4 | if ENV['SIMPLE_COV'] 5 | require 'simplecov' 6 | SimpleCov.start do 7 | add_filter 'test/' 8 | add_filter 'pkg/' 9 | add_filter 'vendor/' 10 | end 11 | end 12 | 13 | require 'test/unit' 14 | require 'test/unit/rr' 15 | require 'fluent/test' 16 | require 'fluent/test/driver/output' 17 | require 'fluent/test/helpers' 18 | --------------------------------------------------------------------------------