fluent-plugin-slack 0.4.0 → 0.5.0
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 +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
|