fluent-plugin-slack-stakater 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +95 -0
- data/Gemfile +3 -0
- data/Gemfile.fluentd.0.12 +4 -0
- data/README.md +126 -0
- data/Rakefile +16 -0
- data/VERSION +1 -0
- data/example.conf +18 -0
- data/fluent-plugin-slack.gemspec +28 -0
- data/lib/fluent/plugin/out_buffered_slack.rb +1 -0
- data/lib/fluent/plugin/out_slack.rb +382 -0
- data/lib/fluent/plugin/slack_client.rb +289 -0
- data/lib/fluent/plugin/slack_client/error.rb +24 -0
- data/test.sh +2 -0
- data/test/plugin/test_out_slack.rb +566 -0
- data/test/plugin/test_slack_client.rb +282 -0
- data/test/test_helper.rb +32 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 96ca178e4ae8555e05a0fdd6466c04c637a42c6d
|
4
|
+
data.tar.gz: 7288a88ed3aa6501568d18ca3af72e34bb9bfae7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 02ae31e51c9d21408f1278a96dc109be007a122e34a8322bb91cac8ea180558258a5b54f975b9f6135cf1a7748439617cf976e9568650705aa398bc85ea68d0f
|
7
|
+
data.tar.gz: 14174efe5615240505d66269a5343a0602d1e1625c8ac24521608cbc8be7523dd01a9b02deac6bf377796f0a1dc75c0e279fb7998edf0f2c8397e907c5704bdf
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
## 0.6.7 (2017/05/23)
|
2
|
+
|
3
|
+
Enhancements:
|
4
|
+
|
5
|
+
* Allow channel @username (DM)
|
6
|
+
|
7
|
+
## 0.6.6 (2017/05/23)
|
8
|
+
|
9
|
+
Enhancements:
|
10
|
+
|
11
|
+
* Make channel config optional on webhook because webhook has its defaul channel setting (thanks to @hirakiuc)
|
12
|
+
|
13
|
+
## 0.6.5 (2017/05/20)
|
14
|
+
|
15
|
+
Enhancements:
|
16
|
+
|
17
|
+
* Avoid Encoding::UndefinedConversionError from ASCII-8BIT to UTF-8 on to_json by doing String#scrub! (thanks @yoheimuta)
|
18
|
+
|
19
|
+
## 0.6.4 (2016/07/07)
|
20
|
+
|
21
|
+
Enhancements:
|
22
|
+
|
23
|
+
* Add `as_user` option (thanks @yacchin1205)
|
24
|
+
|
25
|
+
## 0.6.3 (2016/05/11)
|
26
|
+
|
27
|
+
Enhancements:
|
28
|
+
|
29
|
+
* Add `verbose_fallback` option to show fallback (popup) verbosely (thanks @eisuke)
|
30
|
+
|
31
|
+
## 0.6.2 (2015/12/17)
|
32
|
+
|
33
|
+
Fixes:
|
34
|
+
|
35
|
+
* escape special characters in message (thanks @fujiwara)
|
36
|
+
|
37
|
+
## 0.6.1 (2015/05/17)
|
38
|
+
|
39
|
+
Fixes:
|
40
|
+
|
41
|
+
* Support ruby 1.9.3
|
42
|
+
|
43
|
+
## 0.6.0 (2015/04/02)
|
44
|
+
|
45
|
+
This version has impcompatibility with previous versions in default option values
|
46
|
+
|
47
|
+
Enhancements:
|
48
|
+
|
49
|
+
* Support `link_names` and `parse` option. `link_names` option is `true` as default
|
50
|
+
|
51
|
+
Changes:
|
52
|
+
|
53
|
+
* the default payload of Incoming Webhook was changed
|
54
|
+
* `color` is `nil` as default
|
55
|
+
* `icon_emoji` is `nil` as default
|
56
|
+
* `username` is `nil` as default
|
57
|
+
* `mrkdwn` is `true` as default
|
58
|
+
|
59
|
+
## 0.5.5 (2015/04/01)
|
60
|
+
|
61
|
+
Enhancements:
|
62
|
+
|
63
|
+
* Support Slackbot Remote Control API
|
64
|
+
|
65
|
+
## 0.5.4 (2015/03/31)
|
66
|
+
|
67
|
+
Enhancements:
|
68
|
+
|
69
|
+
* Support `mrkdwn` option
|
70
|
+
|
71
|
+
## 0.5.3 (2015/03/29)
|
72
|
+
|
73
|
+
Enhancements:
|
74
|
+
|
75
|
+
* Support `https_proxy` option
|
76
|
+
|
77
|
+
## 0.5.2 (2015/03/29)
|
78
|
+
|
79
|
+
Enhancements:
|
80
|
+
|
81
|
+
* Support `icon_url` option (thanks to @jwyjoy)
|
82
|
+
|
83
|
+
## 0.5.1 (2015/03/27)
|
84
|
+
|
85
|
+
Enhancements:
|
86
|
+
|
87
|
+
* Support `auto_channels_create` option to automatically create channels.
|
88
|
+
|
89
|
+
## 0.5.0 (2015/03/22)
|
90
|
+
|
91
|
+
Enhancements:
|
92
|
+
|
93
|
+
* Support `message` and `message_keys` options
|
94
|
+
* Support `title` and `title_keys` options
|
95
|
+
* Support `channel_keys` options to dynamically change channels
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# fluent-plugin-slack [](https://travis-ci.org/sowawa/fluent-plugin-slack)
|
2
|
+
|
3
|
+
# Installation
|
4
|
+
|
5
|
+
```
|
6
|
+
$ fluent-gem install fluent-plugin-slack
|
7
|
+
```
|
8
|
+
|
9
|
+
# Usage (Incoming Webhook)
|
10
|
+
|
11
|
+
```apache
|
12
|
+
<match slack>
|
13
|
+
@type slack
|
14
|
+
webhook_url https://hooks.slack.com/services/XXX/XXX/XXX
|
15
|
+
channel general
|
16
|
+
username sowasowa
|
17
|
+
icon_emoji :ghost:
|
18
|
+
flush_interval 60s
|
19
|
+
</match>
|
20
|
+
```
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
fluent_logger.post('slack', {
|
24
|
+
:message => 'Hello<br>World!'
|
25
|
+
})
|
26
|
+
```
|
27
|
+
|
28
|
+
# Usage (Slackbot)
|
29
|
+
|
30
|
+
```apache
|
31
|
+
<match slack>
|
32
|
+
@type slack
|
33
|
+
slackbot_url https://xxxx.slack.com/services/hooks/slackbot?token=XXXXXXXXX
|
34
|
+
channel general
|
35
|
+
flush_interval 60s
|
36
|
+
</match>
|
37
|
+
```
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
fluent_logger.post('slack', {
|
41
|
+
:message => 'Hello<br>World!'
|
42
|
+
})
|
43
|
+
```
|
44
|
+
|
45
|
+
# Usage (Web API a.k.a. Bots)
|
46
|
+
|
47
|
+
```apache
|
48
|
+
<match slack>
|
49
|
+
@type slack
|
50
|
+
token xoxb-XXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXX
|
51
|
+
channel C061EG9SL
|
52
|
+
username sowasowa
|
53
|
+
icon_emoji :ghost:
|
54
|
+
flush_interval 60s
|
55
|
+
</match>
|
56
|
+
```
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
fluent_logger.post('slack', {
|
60
|
+
:message => 'Hello<br>World!'
|
61
|
+
})
|
62
|
+
```
|
63
|
+
|
64
|
+
### Parameter
|
65
|
+
|
66
|
+
|parameter|description|default|
|
67
|
+
|---|---|---|
|
68
|
+
|webhook_url|Incoming Webhook URI (Required for Incoming Webhook mode). See https://api.slack.com/incoming-webhooks||
|
69
|
+
|slackbot_url|Slackbot URI (Required for Slackbot mode). See https://api.slack.com/slackbot. NOTE: most of optional parameters such as `username`, `color`, `icon_emoji`, `icon_url`, and `title` are not available for this mode, but Desktop Notification via Highlight Words works with only this mode||
|
70
|
+
|token|Token for Web API (Required for Web API mode). See https://api.slack.com/web||
|
71
|
+
|as_user|post messages as a bot user. See https://api.slack.com/bot-users#post_messages_and_react_to_users. NOTE: This parameter is only enabled if you use the Web API with your bot token. You cannot use both of `username` and `icon_emoji`(`icon_url`) when you set this parameter to `true`.|nil|
|
72
|
+
|username|name of bot|nil|
|
73
|
+
|color|color to use such as `good` or `bad`. See `Color` section of https://api.slack.com/docs/attachments. NOTE: This parameter must **not** be specified to receive Desktop Notification via Mentions in cases of Incoming Webhook and Slack Web API|nil|
|
74
|
+
|icon_emoji|emoji to use as the icon. either of `icon_emoji` or `icon_url` can be specified|nil|
|
75
|
+
|icon_url|url to an image to use as the icon. either of `icon_emoji` or `icon_url` can be specified|nil|
|
76
|
+
|mrkdwn|enable formatting. see https://api.slack.com/docs/formatting|true|
|
77
|
+
|link_names|find and link channel names and usernames. NOTE: This parameter must be `true` to receive Desktop Notification via Mentions in cases of Incoming Webhook and Slack Web API|true|
|
78
|
+
|parse|change how messages are treated. `none` or `full` can be specified. See `Parsing mode` section of https://api.slack.com/docs/formatting|nil|
|
79
|
+
|channel|Channel name or id to send messages (without first '#'). Channel ID is recommended because it is unchanged even if a channel is renamed||
|
80
|
+
|channel_keys|keys used to format channel. %s will be replaced with value specified by channel_keys if this option is used|nil|
|
81
|
+
|title|title format. %s will be replaced with value specified by title_keys. title is created from the first appeared record on each tag. NOTE: This parameter must **not** be specified to receive Desktop Notification via Mentions in cases of Incoming Webhook and Slack Web API|nil|
|
82
|
+
|title_keys|keys used to format the title|nil|
|
83
|
+
|message|message format. %s will be replaced with value specified by message_keys|%s|
|
84
|
+
|message_keys|keys used to format messages|message|
|
85
|
+
|auto_channels_create|Create channels if not exist. Not available for Incoming Webhook mode (since Incoming Webhook is specific to a channel). A web api `token` for Normal User is required (Bot User can not create channels. See https://api.slack.com/bot-users)|false|
|
86
|
+
|https_proxy|https proxy url such as `https://proxy.foo.bar:443`|nil|
|
87
|
+
|verbose_fallback|Originally, only `title` is used for the fallback which is the message shown on popup if `title` is given. If this option is set to be `true`, messages are also included to the fallback attribute|false|
|
88
|
+
|
89
|
+
`fluent-plugin-slack` uses `SetTimeKeyMixin` and `SetTagKeyMixin`, so you can also use:
|
90
|
+
|
91
|
+
|parameter|description|default|
|
92
|
+
|---|---|---|
|
93
|
+
|timezone|timezone such as `Asia/Tokyo`||
|
94
|
+
|localtime|use localtime as timezone|true|
|
95
|
+
|utc|use utc as timezone||
|
96
|
+
|time_key|key name for time used in xxx_keys|time|
|
97
|
+
|time_format|time format. This will be formatted with Time#strftime.|%H:%M:%S|
|
98
|
+
|tag_key|key name for tag used in xxx_keys|tag|
|
99
|
+
|
100
|
+
`fluent-plugin-slack` is a kind of BufferedOutput plugin, so you can also use [Buffer Parameters](http://docs.fluentd.org/articles/out_exec#buffer-parameters).
|
101
|
+
|
102
|
+
## FAQ
|
103
|
+
|
104
|
+
### Desktop Notification seems not working?
|
105
|
+
|
106
|
+
Currently, slack.com has following limitations:
|
107
|
+
|
108
|
+
1. Desktop Notification via both Highlight Words and Mentions works only with Slackbot Remote Control
|
109
|
+
2. Desktop Notification via Mentions works for the `text` field if `link_names` parameter is specified in cases of Incoming Webhook and Slack Web API, that is,
|
110
|
+
* Desktop Notification does not work for the `attachments` filed (used in `color` and `title`)
|
111
|
+
* Desktop Notification via Highlight Words does not work for Incoming Webhook and Slack Web API anyway
|
112
|
+
|
113
|
+
## ChangeLog
|
114
|
+
|
115
|
+
See [CHANGELOG.md](CHANGELOG.md) for details.
|
116
|
+
|
117
|
+
# Contributors
|
118
|
+
|
119
|
+
- [@sonots](https://github.com/sonots)
|
120
|
+
- [@kenjiskywalker](https://github.com/kenjiskywalker)
|
121
|
+
|
122
|
+
# Copyright
|
123
|
+
|
124
|
+
* Copyright:: Copyright (c) 2014- Keisuke SOGAWA
|
125
|
+
* License:: Apache License, Version 2.0
|
126
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
Rake::TestTask.new(:test) do |test|
|
6
|
+
test.libs << 'lib' << 'test'
|
7
|
+
test.pattern = 'test/**/test_*.rb'
|
8
|
+
test.verbose = true
|
9
|
+
end
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
desc 'Open an irb session preloaded with the gem library'
|
13
|
+
task :console do
|
14
|
+
sh 'irb -rubygems -I lib'
|
15
|
+
end
|
16
|
+
task :c => :console
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.6.8
|
data/example.conf
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
<source>
|
2
|
+
@type forward
|
3
|
+
</source>
|
4
|
+
|
5
|
+
<match tag>
|
6
|
+
@type slack
|
7
|
+
token "#{ENV['TOKEN']}"
|
8
|
+
username fluentd
|
9
|
+
color good
|
10
|
+
icon_emoji :ghost: # if you want to use icon_url, delete this param.
|
11
|
+
#icon_url http://www.google.com/s2/favicons?domain=www.google.de
|
12
|
+
channel general
|
13
|
+
message %s %s
|
14
|
+
message_keys tag,message
|
15
|
+
title %s %s
|
16
|
+
title_keys tag,message
|
17
|
+
flush_interval 1s # slack API has limit as a post / sec
|
18
|
+
</match>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "fluent-plugin-slack-stakater"
|
6
|
+
gem.description = "fluent Slack plugin"
|
7
|
+
gem.homepage = "https://github.com/stakater/fluent-plugin-slack"
|
8
|
+
gem.license = "Apache-2.0"
|
9
|
+
gem.summary = gem.description
|
10
|
+
gem.version = File.read("VERSION").strip
|
11
|
+
gem.authors = ["Keisuke SOGAWA", "Naotoshi Seo", "stakater"]
|
12
|
+
gem.email = ["hello@stakater.com"]
|
13
|
+
gem.has_rdoc = false
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
|
19
|
+
gem.add_dependency "fluentd", ">= 0.12.0"
|
20
|
+
|
21
|
+
gem.add_development_dependency "rake", ">= 10.1.1"
|
22
|
+
gem.add_development_dependency "rr", ">= 1.0.0"
|
23
|
+
gem.add_development_dependency "pry"
|
24
|
+
gem.add_development_dependency "pry-nav"
|
25
|
+
gem.add_development_dependency "test-unit", "~> 3.0.2"
|
26
|
+
gem.add_development_dependency "test-unit-rr", "~> 1.0.3"
|
27
|
+
gem.add_development_dependency "dotenv"
|
28
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
out_slack.rb
|
@@ -0,0 +1,382 @@
|
|
1
|
+
require_relative 'slack_client'
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
class SlackOutput < Fluent::BufferedOutput
|
5
|
+
Fluent::Plugin.register_output('buffered_slack', self) # old version compatiblity
|
6
|
+
Fluent::Plugin.register_output('slack', self)
|
7
|
+
|
8
|
+
helpers :record_accessor
|
9
|
+
|
10
|
+
# For fluentd v0.12.16 or earlier
|
11
|
+
class << self
|
12
|
+
unless method_defined?(:desc)
|
13
|
+
def desc(description)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
include SetTimeKeyMixin
|
19
|
+
include SetTagKeyMixin
|
20
|
+
|
21
|
+
config_set_default :include_time_key, true
|
22
|
+
config_set_default :include_tag_key, true
|
23
|
+
|
24
|
+
desc <<-DESC
|
25
|
+
Incoming Webhook URI (Required for Incoming Webhook mode).
|
26
|
+
See: https://api.slack.com/incoming-webhooks
|
27
|
+
DESC
|
28
|
+
config_param :webhook_url, :string, default: nil
|
29
|
+
desc <<-DESC
|
30
|
+
Slackbot URI (Required for Slackbot mode).
|
31
|
+
See https://api.slack.com/slackbot.
|
32
|
+
NOTE: most of optional parameters such as `username`, `color`, `icon_emoji`,
|
33
|
+
`icon_url`, and `title` are not available for this mode, but Desktop Notification
|
34
|
+
via Highlight Words works with only this mode.
|
35
|
+
DESC
|
36
|
+
config_param :slackbot_url, :string, default: nil
|
37
|
+
desc <<-DESC
|
38
|
+
Token for Web API (Required for Web API mode). See: https://api.slack.com/web.
|
39
|
+
DESC
|
40
|
+
config_param :token, :string, default: nil
|
41
|
+
desc "Name of bot."
|
42
|
+
config_param :username, :string, default: nil
|
43
|
+
desc <<-DESC
|
44
|
+
Color to use such as `good` or `bad`.
|
45
|
+
See Color section of https://api.slack.com/docs/attachments.
|
46
|
+
NOTE: This parameter must not be specified to receive Desktop Notification
|
47
|
+
via Mentions in cases of Incoming Webhook and Slack Web API.
|
48
|
+
DESC
|
49
|
+
config_param :color, :string, default: nil
|
50
|
+
desc <<-DESC
|
51
|
+
Emoji to use as the icon.
|
52
|
+
Either of `icon_emoji` or `icon_url` can be specified.
|
53
|
+
DESC
|
54
|
+
config_param :as_user, :bool, default: nil
|
55
|
+
desc <<-DESC
|
56
|
+
Post message as the authenticated user.
|
57
|
+
NOTE: This parameter is only enabled if you use the Web API with your bot token.
|
58
|
+
You cannot use both of `username` and `icon_emoji`(`icon_url`) when
|
59
|
+
you set this parameter to `true`.
|
60
|
+
DESC
|
61
|
+
config_param :icon_emoji, :string, default: nil
|
62
|
+
desc <<-DESC
|
63
|
+
Url to an image to use as the icon.
|
64
|
+
Either of `icon_emoji` or `icon_url` can be specified.
|
65
|
+
DESC
|
66
|
+
config_param :icon_url, :string, default: nil
|
67
|
+
desc "Enable formatting. See: https://api.slack.com/docs/formatting."
|
68
|
+
config_param :mrkdwn, :bool, default: true
|
69
|
+
desc <<-DESC
|
70
|
+
Find and link channel names and usernames.
|
71
|
+
NOTE: This parameter must be `true` to receive Desktop Notification
|
72
|
+
via Mentions in cases of Incoming Webhook and Slack Web API.
|
73
|
+
DESC
|
74
|
+
config_param :link_names, :bool, default: true
|
75
|
+
desc <<-DESC
|
76
|
+
Change how messages are treated. `none` or `full` can be specified.
|
77
|
+
See Parsing mode section of https://api.slack.com/docs/formatting.
|
78
|
+
DESC
|
79
|
+
config_param :parse, :string, default: nil
|
80
|
+
desc <<-DESC
|
81
|
+
Create channels if not exist. Not available for Incoming Webhook mode
|
82
|
+
(since Incoming Webhook is specific to a channel).
|
83
|
+
A web api token for Normal User is required.
|
84
|
+
(Bot User can not create channels. See https://api.slack.com/bot-users)
|
85
|
+
DESC
|
86
|
+
config_param :auto_channels_create, :bool, default: false
|
87
|
+
desc "https proxy url such as https://proxy.foo.bar:443"
|
88
|
+
config_param :https_proxy, :string, default: nil
|
89
|
+
|
90
|
+
desc "channel to send messages (without first '#')."
|
91
|
+
config_param :channel, :string, default: nil
|
92
|
+
desc <<-DESC
|
93
|
+
Keys used to format channel.
|
94
|
+
%s will be replaced with value specified by channel_keys if this option is used.
|
95
|
+
DESC
|
96
|
+
config_param :channel_keys, default: nil do |val|
|
97
|
+
val.split(',')
|
98
|
+
end
|
99
|
+
desc <<-DESC
|
100
|
+
Title format.
|
101
|
+
%s will be replaced with value specified by title_keys.
|
102
|
+
Title is created from the first appeared record on each tag.
|
103
|
+
NOTE: This parameter must **not** be specified to receive Desktop Notification
|
104
|
+
via Mentions in cases of Incoming Webhook and Slack Web API.
|
105
|
+
DESC
|
106
|
+
config_param :title, :string, default: nil
|
107
|
+
desc "Keys used to format the title."
|
108
|
+
config_param :title_keys, default: nil do |val|
|
109
|
+
val.split(',')
|
110
|
+
end
|
111
|
+
desc <<-DESC
|
112
|
+
Message format.
|
113
|
+
%s will be replaced with value specified by message_keys.
|
114
|
+
DESC
|
115
|
+
config_param :message, :string, default: nil
|
116
|
+
desc "Keys used to format messages."
|
117
|
+
config_param :message_keys, default: nil do |val|
|
118
|
+
val.split(',')
|
119
|
+
end
|
120
|
+
|
121
|
+
desc "Include messages to the fallback attributes"
|
122
|
+
config_param :verbose_fallback, :bool, default: false
|
123
|
+
|
124
|
+
# for test
|
125
|
+
attr_reader :slack, :time_format, :localtime, :timef, :mrkdwn_in, :post_message_opts
|
126
|
+
|
127
|
+
def initialize
|
128
|
+
super
|
129
|
+
require 'uri'
|
130
|
+
end
|
131
|
+
|
132
|
+
def configure(conf)
|
133
|
+
conf['time_format'] ||= '%H:%M:%S' # old version compatiblity
|
134
|
+
conf['localtime'] ||= true unless conf['utc']
|
135
|
+
|
136
|
+
super
|
137
|
+
|
138
|
+
if @channel
|
139
|
+
@channel = URI.unescape(@channel) # old version compatibility
|
140
|
+
if !@channel.start_with?('#') and !@channel.start_with?('@')
|
141
|
+
@channel = '#' + @channel # Add # since `#` is handled as a comment in fluentd conf
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if @webhook_url
|
146
|
+
if @webhook_url.empty?
|
147
|
+
raise Fluent::ConfigError.new("`webhook_url` is an empty string")
|
148
|
+
end
|
149
|
+
unless @as_user.nil?
|
150
|
+
log.warn "out_slack: `as_user` parameter are not available for Incoming Webhook"
|
151
|
+
end
|
152
|
+
@slack = Fluent::SlackClient::IncomingWebhook.new(@webhook_url)
|
153
|
+
elsif @slackbot_url
|
154
|
+
if @slackbot_url.empty?
|
155
|
+
raise Fluent::ConfigError.new("`slackbot_url` is an empty string")
|
156
|
+
end
|
157
|
+
if @channel.nil?
|
158
|
+
raise Fluent::ConfigError.new("`channel` parameter required for Slackbot Remote Control")
|
159
|
+
end
|
160
|
+
|
161
|
+
if @username or @color or @icon_emoji or @icon_url
|
162
|
+
log.warn "out_slack: `username`, `color`, `icon_emoji`, `icon_url` parameters are not available for Slackbot Remote Control"
|
163
|
+
end
|
164
|
+
unless @as_user.nil?
|
165
|
+
log.warn "out_slack: `as_user` parameter are not available for Slackbot Remote Control"
|
166
|
+
end
|
167
|
+
@slack = Fluent::SlackClient::Slackbot.new(@slackbot_url)
|
168
|
+
elsif @token
|
169
|
+
if @token.empty?
|
170
|
+
raise Fluent::ConfigError.new("`token` is an empty string")
|
171
|
+
end
|
172
|
+
if @channel.nil?
|
173
|
+
raise Fluent::ConfigError.new("`channel` parameter required for Slack WebApi")
|
174
|
+
end
|
175
|
+
|
176
|
+
@slack = Fluent::SlackClient::WebApi.new
|
177
|
+
else
|
178
|
+
raise Fluent::ConfigError.new("One of `webhook_url` or `slackbot_url`, or `token` is required")
|
179
|
+
end
|
180
|
+
@slack.log = log
|
181
|
+
@slack.debug_dev = log.out if log.level <= Fluent::Log::LEVEL_TRACE
|
182
|
+
|
183
|
+
if @https_proxy
|
184
|
+
@slack.https_proxy = @https_proxy
|
185
|
+
end
|
186
|
+
|
187
|
+
@message ||= '%s'
|
188
|
+
@message_keys ||= %w[message]
|
189
|
+
begin
|
190
|
+
@message % (['1'] * @message_keys.length)
|
191
|
+
rescue ArgumentError
|
192
|
+
raise Fluent::ConfigError, "string specifier '%s' for `message` and `message_keys` specification mismatch"
|
193
|
+
end
|
194
|
+
if @title and @title_keys
|
195
|
+
begin
|
196
|
+
@title % (['1'] * @title_keys.length)
|
197
|
+
rescue ArgumentError
|
198
|
+
raise Fluent::ConfigError, "string specifier '%s' for `title` and `title_keys` specification mismatch"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
if @channel && @channel_keys
|
202
|
+
begin
|
203
|
+
@channel % (['1'] * @channel_keys.length)
|
204
|
+
rescue ArgumentError
|
205
|
+
raise Fluent::ConfigError, "string specifier '%s' for `channel` and `channel_keys` specification mismatch"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
if @icon_emoji and @icon_url
|
210
|
+
raise Fluent::ConfigError, "either of `icon_emoji` or `icon_url` can be specified"
|
211
|
+
end
|
212
|
+
|
213
|
+
if @as_user and (@icon_emoji or @icon_url or @username)
|
214
|
+
raise Fluent::ConfigError, "`username`, `icon_emoji` and `icon_url` cannot be specified when `as_user` is set to true"
|
215
|
+
end
|
216
|
+
|
217
|
+
if @mrkdwn
|
218
|
+
# Enable markdown for attachments. See https://api.slack.com/docs/formatting
|
219
|
+
@mrkdwn_in = %w[text fields]
|
220
|
+
end
|
221
|
+
|
222
|
+
if @parse and !%w[none full].include?(@parse)
|
223
|
+
raise Fluent::ConfigError, "`parse` must be either of `none` or `full`"
|
224
|
+
end
|
225
|
+
|
226
|
+
@post_message_opts = {}
|
227
|
+
if @auto_channels_create
|
228
|
+
raise Fluent::ConfigError, "`token` parameter is required to use `auto_channels_create`" unless @token
|
229
|
+
@post_message_opts = {auto_channels_create: true}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def format(tag, time, record)
|
234
|
+
[tag, time, record].to_msgpack
|
235
|
+
end
|
236
|
+
|
237
|
+
def write(chunk)
|
238
|
+
begin
|
239
|
+
payloads = build_payloads(chunk)
|
240
|
+
payloads.each {|payload| @slack.post_message(payload, @post_message_opts) }
|
241
|
+
rescue Timeout::Error => e
|
242
|
+
log.warn "out_slack:", :error => e.to_s, :error_class => e.class.to_s
|
243
|
+
raise e # let Fluentd retry
|
244
|
+
rescue => e
|
245
|
+
log.error "out_slack:", :error => e.to_s, :error_class => e.class.to_s
|
246
|
+
log.warn_backtrace e.backtrace
|
247
|
+
# discard. @todo: add more retriable errors
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
def build_payloads(chunk)
|
254
|
+
if @title
|
255
|
+
build_title_payloads(chunk)
|
256
|
+
elsif @color
|
257
|
+
build_color_payloads(chunk)
|
258
|
+
else
|
259
|
+
build_plain_payloads(chunk)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def common_payload
|
264
|
+
return @common_payload if @common_payload
|
265
|
+
@common_payload = {}
|
266
|
+
@common_payload[:as_user] = @as_user unless @as_user.nil?
|
267
|
+
@common_payload[:username] = @username if @username
|
268
|
+
@common_payload[:icon_emoji] = @icon_emoji if @icon_emoji
|
269
|
+
@common_payload[:icon_url] = @icon_url if @icon_url
|
270
|
+
@common_payload[:mrkdwn] = @mrkdwn if @mrkdwn
|
271
|
+
@common_payload[:link_names] = @link_names if @link_names
|
272
|
+
@common_payload[:parse] = @parse if @parse
|
273
|
+
@common_payload[:token] = @token if @token
|
274
|
+
@common_payload
|
275
|
+
end
|
276
|
+
|
277
|
+
def common_attachment
|
278
|
+
return @common_attachment if @common_attachment
|
279
|
+
@common_attachment = {}
|
280
|
+
@common_attachment[:color] = @color if @color
|
281
|
+
@common_attachment[:mrkdwn_in] = @mrkdwn_in if @mrkdwn_in
|
282
|
+
@common_attachment
|
283
|
+
end
|
284
|
+
|
285
|
+
Field = Struct.new("Field", :title, :value)
|
286
|
+
# ruby 1.9.x does not provide #to_h
|
287
|
+
Field.send(:define_method, :to_h) { {title: title, value: value} }
|
288
|
+
|
289
|
+
def build_title_payloads(chunk)
|
290
|
+
ch_fields = {}
|
291
|
+
chunk.msgpack_each do |tag, time, record|
|
292
|
+
channel = build_channel(record)
|
293
|
+
per = tag # title per tag
|
294
|
+
ch_fields[channel] ||= {}
|
295
|
+
ch_fields[channel][per] ||= Field.new(build_title(record), '')
|
296
|
+
ch_fields[channel][per].value << "#{build_message(record)}\n"
|
297
|
+
end
|
298
|
+
ch_fields.map do |channel, fields|
|
299
|
+
fallback_text = if @verbose_fallback
|
300
|
+
fields.values.map { |f| "#{f.title} #{f.value}" }.join(' ')
|
301
|
+
else
|
302
|
+
fields.values.map(&:title).join(' ')
|
303
|
+
end
|
304
|
+
|
305
|
+
msg = {
|
306
|
+
attachments: [{
|
307
|
+
:fallback => fallback_text, # fallback is the message shown on popup
|
308
|
+
:fields => fields.values.map(&:to_h)
|
309
|
+
}.merge(common_attachment)],
|
310
|
+
}
|
311
|
+
msg.merge!(channel: channel) if channel
|
312
|
+
msg.merge!(common_payload)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def build_color_payloads(chunk)
|
317
|
+
messages = {}
|
318
|
+
chunk.msgpack_each do |tag, time, record|
|
319
|
+
channel = build_channel(record)
|
320
|
+
messages[channel] ||= ''
|
321
|
+
messages[channel] << "#{build_message(record)}\n"
|
322
|
+
end
|
323
|
+
messages.map do |channel, text|
|
324
|
+
msg = {
|
325
|
+
attachments: [{
|
326
|
+
:fallback => text,
|
327
|
+
:text => text,
|
328
|
+
}.merge(common_attachment)],
|
329
|
+
}
|
330
|
+
msg.merge!(channel: channel) if channel
|
331
|
+
msg.merge!(common_payload)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def build_plain_payloads(chunk)
|
336
|
+
messages = {}
|
337
|
+
chunk.msgpack_each do |tag, time, record|
|
338
|
+
channel = build_channel(record)
|
339
|
+
messages[channel] ||= ''
|
340
|
+
messages[channel] << "#{build_message(record)}\n"
|
341
|
+
end
|
342
|
+
messages.map do |channel, text|
|
343
|
+
msg = {text: text}
|
344
|
+
msg.merge!(channel: channel) if channel
|
345
|
+
msg.merge!(common_payload)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def build_message(record)
|
350
|
+
values = fetch_keys(record, @message_keys)
|
351
|
+
@message % values
|
352
|
+
end
|
353
|
+
|
354
|
+
def build_title(record)
|
355
|
+
return @title unless @title_keys
|
356
|
+
|
357
|
+
values = fetch_keys(record, @title_keys)
|
358
|
+
@title % values
|
359
|
+
end
|
360
|
+
|
361
|
+
def build_channel(record)
|
362
|
+
return nil if @channel.nil?
|
363
|
+
return @channel unless @channel_keys
|
364
|
+
|
365
|
+
values = fetch_keys(record, @channel_keys)
|
366
|
+
@channel % values
|
367
|
+
end
|
368
|
+
|
369
|
+
def fetch_keys(record, keys)
|
370
|
+
Array(keys).map do |key|
|
371
|
+
begin
|
372
|
+
accessor = record_accessor_create(key)
|
373
|
+
accessor.call(record).to_s
|
374
|
+
#record.fetch(key).to_s
|
375
|
+
rescue KeyError
|
376
|
+
log.warn "out_slack: the specified key '#{key}' not found in record. [#{record}]"
|
377
|
+
''
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|