fluent-plugin-slack 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/README.md +87 -0
- data/VERSION +1 -1
- data/fluent-plugin-slack.gemspec +4 -3
- data/lib/fluent/plugin/out_buffered_slack.rb +169 -88
- data/lib/fluent/plugin/out_slack.rb +199 -0
- data/lib/fluent/plugin/slack_client.rb +107 -0
- data/test/plugin/test_out_slack.rb +259 -0
- data/test/plugin/test_slack_client.rb +73 -0
- data/test/test_helper.rb +7 -1
- metadata +47 -31
- data/README.rdoc +0 -46
- data/test/plugin/test_out_buffered_slack.rb +0 -73
- data/test/plugin/test_out_buffered_slack_rtm.rb +0 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c08f44114d6d2335cb8cee39f0053e79000270bc
|
4
|
+
data.tar.gz: 25186d53c993522e99e92acfb479859d0e6e265d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e454cae0012869146257e6c1160b739a4620f27966eeb192991b677d7243de00d24516146de16ca38be2971bd403f7d31197d1fc9f24f54d162ab64c968932c
|
7
|
+
data.tar.gz: 1a277e5b7ad062af8e25618b7742c680a3c9878ef36f28f4a7ad47a2a782861c909930251dcf0a699890885a95eb331e31fc27158ff5f06ddf003f514bdc91fa
|
data/.gitignore
CHANGED
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Fluent event to slack plugin.
|
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
|
+
color good
|
18
|
+
icon_emoji :ghost:
|
19
|
+
flush_interval 60s
|
20
|
+
</match>
|
21
|
+
```
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
fluent_logger.post('slack', {
|
25
|
+
:message => 'Hello<br>World!'
|
26
|
+
})
|
27
|
+
```
|
28
|
+
|
29
|
+
# Usage (Slack API)
|
30
|
+
|
31
|
+
```apache
|
32
|
+
<match slack>
|
33
|
+
type slack
|
34
|
+
token xoxb-XXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXX
|
35
|
+
channel general
|
36
|
+
username sowasowa
|
37
|
+
color good
|
38
|
+
icon_emoji :ghost:
|
39
|
+
flush_interval 60s
|
40
|
+
</match>
|
41
|
+
```
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
fluent_logger.post('slack', {
|
45
|
+
:message => 'Hello<br>World!'
|
46
|
+
})
|
47
|
+
```
|
48
|
+
|
49
|
+
### Parameter
|
50
|
+
|
51
|
+
|parameter|description|default|
|
52
|
+
|---|---|---|
|
53
|
+
|webhook_uri|Incoming Webhook URI (Required for Incoming Webhook mode)||
|
54
|
+
|token|Token for Slack API (Required for Slack API mode)||
|
55
|
+
|username|name of bot|fluentd|
|
56
|
+
|color|color to use|good|
|
57
|
+
|icon_emoji|emoji to use as the icon|`:question:`|
|
58
|
+
|channel|channel to send messages (without first '#')||
|
59
|
+
|channel_keys|keys used to format channel. %s will be replaced with value specified by channel_keys if this option is used|nil|
|
60
|
+
|title|title format. %s will be replaced with value specified by title_keys. title is created from the first appeared record on each tag|nil|
|
61
|
+
|title_keys|keys used to format the title|nil|
|
62
|
+
|message|message format. %s will be replaced with value specified by message_keys|%s|
|
63
|
+
|message_keys|keys used to format messages|message|
|
64
|
+
|
65
|
+
`fluent-plugin-slack` uses `SetTimeKeyMixin` and `SetTagKeyMixin`, so you can also use:
|
66
|
+
|
67
|
+
|parameter|description|default|
|
68
|
+
|---|---|---|
|
69
|
+
|timezone|timezone such as `Asia/Tokyo`||
|
70
|
+
|localtime|use localtime as timezone|true|
|
71
|
+
|utc|use utc as timezone||
|
72
|
+
|time_key|key name for time used in xxx_keys|time|
|
73
|
+
|time_format|time format. This will be formatted with Time#strftime.|%H:%M:%S|
|
74
|
+
|tag_key|key name for tag used in xxx_keys|tag|
|
75
|
+
|
76
|
+
`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).
|
77
|
+
|
78
|
+
# Contributors
|
79
|
+
|
80
|
+
- [@sonots](https://github.com/sonots)
|
81
|
+
- [@kenjiskywalker](https://github.com/kenjiskywalker)
|
82
|
+
|
83
|
+
# Copyright
|
84
|
+
|
85
|
+
* Copyright:: Copyright (c) 2014- Keisuke SOGAWA
|
86
|
+
* License:: Apache License, Version 2.0
|
87
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/fluent-plugin-slack.gemspec
CHANGED
@@ -16,11 +16,12 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ['lib']
|
17
17
|
|
18
18
|
gem.add_dependency "fluentd", ">= 0.10.8"
|
19
|
-
gem.add_dependency "activesupport", "~>3.2.0"
|
20
|
-
gem.add_dependency "tzinfo", ">=0.3.38"
|
21
19
|
|
22
20
|
gem.add_development_dependency "rake", ">= 10.1.1"
|
23
21
|
gem.add_development_dependency "rr", ">= 1.0.0"
|
24
22
|
gem.add_development_dependency "pry"
|
25
|
-
gem.add_development_dependency
|
23
|
+
gem.add_development_dependency "pry-nav"
|
24
|
+
gem.add_development_dependency "test-unit", "~> 3.0.2"
|
25
|
+
gem.add_development_dependency "test-unit-rr", "~> 1.0.3"
|
26
|
+
gem.add_development_dependency "dotenv"
|
26
27
|
end
|
@@ -1,118 +1,199 @@
|
|
1
|
+
require_relative 'slack_client'
|
2
|
+
|
1
3
|
module Fluent
|
2
|
-
class
|
3
|
-
|
4
|
-
Fluent::Plugin.register_output('
|
5
|
-
config_param :api_key, :string, default: nil
|
6
|
-
config_param :token, :string, default: nil
|
7
|
-
config_param :team, :string, default: nil
|
8
|
-
config_param :channel, :string
|
9
|
-
config_param :username, :string
|
10
|
-
config_param :color, :string
|
11
|
-
config_param :icon_emoji, :string
|
12
|
-
config_param :timezone, :string, default: nil
|
13
|
-
config_param :rtm, :bool , default: false
|
14
|
-
config_param :webhook_url,:string, default: nil
|
15
|
-
|
16
|
-
attr_reader :slack
|
4
|
+
class SlackOutput < Fluent::BufferedOutput
|
5
|
+
Fluent::Plugin.register_output('buffered_slack', self) # old version compatiblity
|
6
|
+
Fluent::Plugin.register_output('slack', self)
|
17
7
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
8
|
+
include SetTimeKeyMixin
|
9
|
+
include SetTagKeyMixin
|
21
10
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
11
|
+
config_set_default :include_time_key, true
|
12
|
+
config_set_default :include_tag_key, true
|
13
|
+
|
14
|
+
config_param :webhook_url, :string, default: nil # incoming webhook
|
15
|
+
config_param :token, :string, default: nil # api token
|
16
|
+
config_param :username, :string, default: 'fluentd'
|
17
|
+
config_param :color, :string, default: 'good'
|
18
|
+
config_param :icon_emoji, :string, default: ':question:'
|
29
19
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
}].to_json
|
42
|
-
}
|
43
|
-
get_request(params)
|
44
|
-
else
|
45
|
-
payload = {
|
46
|
-
channel: @channel,
|
47
|
-
username: @username,
|
48
|
-
icon_emoji: @icon_emoji,
|
49
|
-
attachments: [{
|
50
|
-
fallback: messages.keys.join(','),
|
51
|
-
color: @color,
|
52
|
-
fields: messages.map{|k,v| {title: k, value: v} }
|
53
|
-
}]}
|
54
|
-
post_request(
|
55
|
-
payload: payload.to_json
|
56
|
-
)
|
57
|
-
end
|
58
|
-
rescue => e
|
59
|
-
$log.error("Slack Error: #{e.backtrace[0]} / #{e.message}")
|
60
|
-
end
|
20
|
+
config_param :channel, :string
|
21
|
+
config_param :channel_keys, default: nil do |val|
|
22
|
+
val.split(',')
|
23
|
+
end
|
24
|
+
config_param :title, :string, default: nil
|
25
|
+
config_param :title_keys, default: nil do |val|
|
26
|
+
val.split(',')
|
27
|
+
end
|
28
|
+
config_param :message, :string, default: nil
|
29
|
+
config_param :message_keys, default: nil do |val|
|
30
|
+
val.split(',')
|
61
31
|
end
|
62
32
|
|
33
|
+
# for test
|
34
|
+
attr_reader :slack, :time_format, :localtime, :timef
|
35
|
+
|
63
36
|
def initialize
|
64
37
|
super
|
65
|
-
require 'active_support/time'
|
66
38
|
require 'uri'
|
67
|
-
require 'net/http'
|
68
39
|
end
|
69
40
|
|
70
41
|
def configure(conf)
|
42
|
+
conf['time_format'] ||= '%H:%M:%S' # old version compatiblity
|
43
|
+
conf['localtime'] ||= true unless conf['utc']
|
44
|
+
|
71
45
|
super
|
72
46
|
|
73
|
-
@channel
|
74
|
-
@
|
75
|
-
@color = conf['color'] || 'good'
|
76
|
-
@icon_emoji = conf['icon_emoji'] || ':question:'
|
47
|
+
@channel = URI.unescape(@channel) # old version compatibility
|
48
|
+
@channel = '#' + @channel unless @channel.start_with?('#')
|
77
49
|
|
78
|
-
if @
|
79
|
-
|
50
|
+
if @webhook_url
|
51
|
+
# following default values are for old version compatibility
|
52
|
+
@title ||= '%s'
|
53
|
+
@title_keys ||= %w[tag]
|
54
|
+
@message ||= '[%s] %s'
|
55
|
+
@message_keys ||= %w[time message]
|
56
|
+
@slack = Fluent::SlackClient::IncomingWebhook.new(@webhook_url)
|
80
57
|
else
|
81
|
-
@
|
82
|
-
|
83
|
-
|
84
|
-
@
|
58
|
+
unless @token
|
59
|
+
raise Fluent::ConfigError.new("`token` is required to call slack api")
|
60
|
+
end
|
61
|
+
@message ||= '%s'
|
62
|
+
@message_keys ||= %w[message]
|
63
|
+
@slack = Fluent::SlackClient::WebApi.new
|
64
|
+
end
|
65
|
+
@slack.log = log
|
66
|
+
@slack.debug_dev = log.out if log.level <= Fluent::Log::LEVEL_TRACE
|
67
|
+
|
68
|
+
begin
|
69
|
+
@message % (['1'] * @message_keys.length)
|
70
|
+
rescue ArgumentError
|
71
|
+
raise Fluent::ConfigError, "string specifier '%s' for `message` and `message_keys` specification mismatch"
|
72
|
+
end
|
73
|
+
if @title and @title_keys
|
74
|
+
begin
|
75
|
+
@title % (['1'] * @title_keys.length)
|
76
|
+
rescue ArgumentError
|
77
|
+
raise Fluent::ConfigError, "string specifier '%s' for `title` and `title_keys` specification mismatch"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
if @channel_keys
|
81
|
+
begin
|
82
|
+
@channel % (['1'] * @channel_keys.length)
|
83
|
+
rescue ArgumentError
|
84
|
+
raise Fluent::ConfigError, "string specifier '%s' for `channel` and `channel_keys` specification mismatch"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def format(tag, time, record)
|
90
|
+
[tag, time, record].to_msgpack
|
91
|
+
end
|
92
|
+
|
93
|
+
def write(chunk)
|
94
|
+
begin
|
95
|
+
payloads = build_payloads(chunk)
|
96
|
+
payloads.each {|payload| @slack.post_message(payload) }
|
97
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
98
|
+
log.warn "out_slack:", :error => e.to_s, :error_class => e.class.to_s
|
99
|
+
raise e # let Fluentd retry
|
100
|
+
rescue => e
|
101
|
+
log.error "out_slack:", :error => e.to_s, :error_class => e.class.to_s
|
102
|
+
log.warn_backtrace e.backtrace
|
103
|
+
# discard. @todo: add more retriable errors
|
85
104
|
end
|
86
105
|
end
|
87
106
|
|
88
107
|
private
|
89
|
-
|
90
|
-
|
91
|
-
|
108
|
+
|
109
|
+
def build_payloads(chunk)
|
110
|
+
if @title
|
111
|
+
build_title_payloads(chunk)
|
112
|
+
else
|
113
|
+
build_plain_payloads(chunk)
|
92
114
|
end
|
93
115
|
end
|
94
116
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
117
|
+
def common_payload
|
118
|
+
return @common_payload if @common_payload
|
119
|
+
@common_payload = {
|
120
|
+
username: @username,
|
121
|
+
icon_emoji: @icon_emoji,
|
122
|
+
}
|
123
|
+
@common_payload[:token] = @token if @token
|
124
|
+
@common_payload
|
103
125
|
end
|
104
126
|
|
105
|
-
|
106
|
-
|
127
|
+
Field = Struct.new("Field", :title, :value)
|
128
|
+
|
129
|
+
def build_title_payloads(chunk)
|
130
|
+
ch_fields = {}
|
131
|
+
chunk.msgpack_each do |tag, time, record|
|
132
|
+
channel = build_channel(record)
|
133
|
+
per = tag # title per tag
|
134
|
+
ch_fields[channel] ||= {}
|
135
|
+
ch_fields[channel][per] ||= Field.new(build_title(record), '')
|
136
|
+
ch_fields[channel][per].value << "#{build_message(record)}\n"
|
137
|
+
end
|
138
|
+
ch_fields.map do |channel, fields|
|
139
|
+
{
|
140
|
+
channel: channel,
|
141
|
+
attachments: [{
|
142
|
+
:color => @color,
|
143
|
+
:fallback => fields.values.map(&:title).join(' '), # fallback is the message shown on popup
|
144
|
+
:fields => fields.values.map(&:to_h)
|
145
|
+
}],
|
146
|
+
}.merge(common_payload)
|
147
|
+
end
|
107
148
|
end
|
108
149
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
150
|
+
def build_plain_payloads(chunk)
|
151
|
+
messages = {}
|
152
|
+
chunk.msgpack_each do |tag, time, record|
|
153
|
+
channel = build_channel(record)
|
154
|
+
messages[channel] ||= ''
|
155
|
+
messages[channel] << "#{build_message(record)}\n"
|
156
|
+
end
|
157
|
+
messages.map do |channel, text|
|
158
|
+
{
|
159
|
+
channel: channel,
|
160
|
+
attachments: [{
|
161
|
+
:color => @color,
|
162
|
+
:fallback => text,
|
163
|
+
:text => text,
|
164
|
+
}],
|
165
|
+
}.merge(common_payload)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def build_message(record)
|
170
|
+
values = fetch_keys(record, @message_keys)
|
171
|
+
@message % values
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_title(record)
|
175
|
+
return @title unless @title_keys
|
176
|
+
|
177
|
+
values = fetch_keys(record, @title_keys)
|
178
|
+
@title % values
|
179
|
+
end
|
180
|
+
|
181
|
+
def build_channel(record)
|
182
|
+
return @channel unless @channel_keys
|
183
|
+
|
184
|
+
values = fetch_keys(record, @channel_keys)
|
185
|
+
@channel % values
|
186
|
+
end
|
187
|
+
|
188
|
+
def fetch_keys(record, keys)
|
189
|
+
Array(keys).map do |key|
|
190
|
+
begin
|
191
|
+
record.fetch(key).to_s
|
192
|
+
rescue KeyError
|
193
|
+
log.warn "out_slack: the specified key '#{key}' not found in record. [#{record}]"
|
194
|
+
''
|
195
|
+
end
|
196
|
+
end
|
116
197
|
end
|
117
198
|
end
|
118
199
|
end
|
@@ -0,0 +1,199 @@
|
|
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
|
+
include SetTimeKeyMixin
|
9
|
+
include SetTagKeyMixin
|
10
|
+
|
11
|
+
config_set_default :include_time_key, true
|
12
|
+
config_set_default :include_tag_key, true
|
13
|
+
|
14
|
+
config_param :webhook_url, :string, default: nil # incoming webhook
|
15
|
+
config_param :token, :string, default: nil # api token
|
16
|
+
config_param :username, :string, default: 'fluentd'
|
17
|
+
config_param :color, :string, default: 'good'
|
18
|
+
config_param :icon_emoji, :string, default: ':question:'
|
19
|
+
|
20
|
+
config_param :channel, :string
|
21
|
+
config_param :channel_keys, default: nil do |val|
|
22
|
+
val.split(',')
|
23
|
+
end
|
24
|
+
config_param :title, :string, default: nil
|
25
|
+
config_param :title_keys, default: nil do |val|
|
26
|
+
val.split(',')
|
27
|
+
end
|
28
|
+
config_param :message, :string, default: nil
|
29
|
+
config_param :message_keys, default: nil do |val|
|
30
|
+
val.split(',')
|
31
|
+
end
|
32
|
+
|
33
|
+
# for test
|
34
|
+
attr_reader :slack, :time_format, :localtime, :timef
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
super
|
38
|
+
require 'uri'
|
39
|
+
end
|
40
|
+
|
41
|
+
def configure(conf)
|
42
|
+
conf['time_format'] ||= '%H:%M:%S' # old version compatiblity
|
43
|
+
conf['localtime'] ||= true unless conf['utc']
|
44
|
+
|
45
|
+
super
|
46
|
+
|
47
|
+
@channel = URI.unescape(@channel) # old version compatibility
|
48
|
+
@channel = '#' + @channel unless @channel.start_with?('#')
|
49
|
+
|
50
|
+
if @webhook_url
|
51
|
+
# following default values are for old version compatibility
|
52
|
+
@title ||= '%s'
|
53
|
+
@title_keys ||= %w[tag]
|
54
|
+
@message ||= '[%s] %s'
|
55
|
+
@message_keys ||= %w[time message]
|
56
|
+
@slack = Fluent::SlackClient::IncomingWebhook.new(@webhook_url)
|
57
|
+
else
|
58
|
+
unless @token
|
59
|
+
raise Fluent::ConfigError.new("`token` is required to call slack api")
|
60
|
+
end
|
61
|
+
@message ||= '%s'
|
62
|
+
@message_keys ||= %w[message]
|
63
|
+
@slack = Fluent::SlackClient::WebApi.new
|
64
|
+
end
|
65
|
+
@slack.log = log
|
66
|
+
@slack.debug_dev = log.out if log.level <= Fluent::Log::LEVEL_TRACE
|
67
|
+
|
68
|
+
begin
|
69
|
+
@message % (['1'] * @message_keys.length)
|
70
|
+
rescue ArgumentError
|
71
|
+
raise Fluent::ConfigError, "string specifier '%s' for `message` and `message_keys` specification mismatch"
|
72
|
+
end
|
73
|
+
if @title and @title_keys
|
74
|
+
begin
|
75
|
+
@title % (['1'] * @title_keys.length)
|
76
|
+
rescue ArgumentError
|
77
|
+
raise Fluent::ConfigError, "string specifier '%s' for `title` and `title_keys` specification mismatch"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
if @channel_keys
|
81
|
+
begin
|
82
|
+
@channel % (['1'] * @channel_keys.length)
|
83
|
+
rescue ArgumentError
|
84
|
+
raise Fluent::ConfigError, "string specifier '%s' for `channel` and `channel_keys` specification mismatch"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def format(tag, time, record)
|
90
|
+
[tag, time, record].to_msgpack
|
91
|
+
end
|
92
|
+
|
93
|
+
def write(chunk)
|
94
|
+
begin
|
95
|
+
payloads = build_payloads(chunk)
|
96
|
+
payloads.each {|payload| @slack.post_message(payload) }
|
97
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
98
|
+
log.warn "out_slack:", :error => e.to_s, :error_class => e.class.to_s
|
99
|
+
raise e # let Fluentd retry
|
100
|
+
rescue => e
|
101
|
+
log.error "out_slack:", :error => e.to_s, :error_class => e.class.to_s
|
102
|
+
log.warn_backtrace e.backtrace
|
103
|
+
# discard. @todo: add more retriable errors
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def build_payloads(chunk)
|
110
|
+
if @title
|
111
|
+
build_title_payloads(chunk)
|
112
|
+
else
|
113
|
+
build_plain_payloads(chunk)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def common_payload
|
118
|
+
return @common_payload if @common_payload
|
119
|
+
@common_payload = {
|
120
|
+
username: @username,
|
121
|
+
icon_emoji: @icon_emoji,
|
122
|
+
}
|
123
|
+
@common_payload[:token] = @token if @token
|
124
|
+
@common_payload
|
125
|
+
end
|
126
|
+
|
127
|
+
Field = Struct.new("Field", :title, :value)
|
128
|
+
|
129
|
+
def build_title_payloads(chunk)
|
130
|
+
ch_fields = {}
|
131
|
+
chunk.msgpack_each do |tag, time, record|
|
132
|
+
channel = build_channel(record)
|
133
|
+
per = tag # title per tag
|
134
|
+
ch_fields[channel] ||= {}
|
135
|
+
ch_fields[channel][per] ||= Field.new(build_title(record), '')
|
136
|
+
ch_fields[channel][per].value << "#{build_message(record)}\n"
|
137
|
+
end
|
138
|
+
ch_fields.map do |channel, fields|
|
139
|
+
{
|
140
|
+
channel: channel,
|
141
|
+
attachments: [{
|
142
|
+
:color => @color,
|
143
|
+
:fallback => fields.values.map(&:title).join(' '), # fallback is the message shown on popup
|
144
|
+
:fields => fields.values.map(&:to_h)
|
145
|
+
}],
|
146
|
+
}.merge(common_payload)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def build_plain_payloads(chunk)
|
151
|
+
messages = {}
|
152
|
+
chunk.msgpack_each do |tag, time, record|
|
153
|
+
channel = build_channel(record)
|
154
|
+
messages[channel] ||= ''
|
155
|
+
messages[channel] << "#{build_message(record)}\n"
|
156
|
+
end
|
157
|
+
messages.map do |channel, text|
|
158
|
+
{
|
159
|
+
channel: channel,
|
160
|
+
attachments: [{
|
161
|
+
:color => @color,
|
162
|
+
:fallback => text,
|
163
|
+
:text => text,
|
164
|
+
}],
|
165
|
+
}.merge(common_payload)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def build_message(record)
|
170
|
+
values = fetch_keys(record, @message_keys)
|
171
|
+
@message % values
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_title(record)
|
175
|
+
return @title unless @title_keys
|
176
|
+
|
177
|
+
values = fetch_keys(record, @title_keys)
|
178
|
+
@title % values
|
179
|
+
end
|
180
|
+
|
181
|
+
def build_channel(record)
|
182
|
+
return @channel unless @channel_keys
|
183
|
+
|
184
|
+
values = fetch_keys(record, @channel_keys)
|
185
|
+
@channel % values
|
186
|
+
end
|
187
|
+
|
188
|
+
def fetch_keys(record, keys)
|
189
|
+
Array(keys).map do |key|
|
190
|
+
begin
|
191
|
+
record.fetch(key).to_s
|
192
|
+
rescue KeyError
|
193
|
+
log.warn "out_slack: the specified key '#{key}' not found in record. [#{record}]"
|
194
|
+
''
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|