fluent-plugin-systemd 0.3.1 → 1.0.0.rc1
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/README.md +4 -2
- data/lib/fluent/plugin/filter_systemd_entry.rb +5 -6
- data/lib/fluent/plugin/in_systemd.rb +17 -30
- data/lib/fluent/plugin/systemd/entry_mutator.rb +44 -35
- metadata +11 -12
- data/lib/fluent/plugin/systemd/pos_writer.rb +0 -80
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13f78fa020542fb3c022c6d0870ecf1ec2d307ae
|
4
|
+
data.tar.gz: 5cac751f2b33bc5783bf3a721999890e9f2c51ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89677dd5dbf1c6648e37ec969590392cf54a929ae15d30a64824df1b3a93f1a7ea54f71c83839f484bcc914f5ffbaf8b2b32c05320e1dc45084ea30ec48afdae
|
7
|
+
data.tar.gz: dcd1c5f2eaac7c766344bbbae29d7fc8fb10cc951cc7dfbb07982ae5946dc8eef6ec26593b8ade6bac1c8fe981b78bb5f291982b9941a6ef02a85801919893c8
|
data/README.md
CHANGED
@@ -44,7 +44,7 @@ or
|
|
44
44
|
read_from_head true
|
45
45
|
<storage>
|
46
46
|
@type local
|
47
|
-
persistent
|
47
|
+
persistent false
|
48
48
|
path kube-proxy.pos
|
49
49
|
</storage>
|
50
50
|
<entry>
|
@@ -196,7 +196,7 @@ For systems with systemd installed you can run the tests against your installed
|
|
196
196
|
|
197
197
|
Issues and pull requests are very welcome.
|
198
198
|
|
199
|
-
If you want to make a contribution but need some help or advice feel free to message me @errm on the [Fluentd Slack](http://slack.fluentd.org/), or send me an email
|
199
|
+
If you want to make a contribution but need some help or advice feel free to message me @errm on the [Fluentd Slack](http://slack.fluentd.org/), or send me an email edward-robinson@cookpad.com
|
200
200
|
|
201
201
|
We have adopted the [Contributor Covenant](CODE_OF_CONDUCT.md) and thus expect anyone interacting with contributors, maintainers and users of this project to abide by it.
|
202
202
|
|
@@ -216,3 +216,5 @@ Many thanks to our fantastic contributors
|
|
216
216
|
* [neko-neko](https://github.com/neko-neko)
|
217
217
|
* [Sadayuki Furuhashi](https://github.com/frsyuki)
|
218
218
|
* [Jesus Rafael Carrillo](https://github.com/jescarri)
|
219
|
+
* [John Thomas Wile II](https://github.com/jtwile2)
|
220
|
+
* [Kazuhiro Suzuki](https://github.com/ksauzz)
|
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
2
|
+
|
3
|
+
require 'fluent/plugin/filter'
|
4
|
+
require 'fluent/plugin/systemd/entry_mutator'
|
4
5
|
|
5
6
|
module Fluent
|
6
7
|
module Plugin
|
7
8
|
# Fluentd systemd/journal filter plugin
|
8
9
|
class SystemdEntryFilter < Filter
|
9
|
-
Fluent::Plugin.register_filter(
|
10
|
+
Fluent::Plugin.register_filter('systemd_entry', self)
|
10
11
|
|
11
12
|
config_param :field_map, :hash, default: {}
|
12
13
|
config_param :field_map_strict, :bool, default: false
|
@@ -16,9 +17,7 @@ module Fluent
|
|
16
17
|
def configure(conf)
|
17
18
|
super
|
18
19
|
@mutator = SystemdEntryMutator.new(**@config_root_section.to_h)
|
19
|
-
|
20
|
-
log.warn("`field_map_strict` set to true with empty `field_map`, expect no fields")
|
21
|
-
end
|
20
|
+
@mutator.warnings.each { |warning| log.warn(warning) }
|
22
21
|
end
|
23
22
|
|
24
23
|
def filter(_tag, _time, entry)
|
@@ -1,33 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
2
|
+
|
3
|
+
require 'systemd/journal'
|
4
|
+
require 'fluent/plugin/input'
|
5
|
+
require 'fluent/plugin/systemd/entry_mutator'
|
6
6
|
|
7
7
|
module Fluent
|
8
8
|
module Plugin
|
9
|
-
|
10
|
-
|
9
|
+
# Fluentd plugin for reading from the systemd journal
|
10
|
+
class SystemdInput < Input
|
11
|
+
Fluent::Plugin.register_input('systemd', self)
|
11
12
|
|
12
13
|
helpers :timer, :storage
|
13
14
|
|
14
|
-
DEFAULT_STORAGE_TYPE =
|
15
|
+
DEFAULT_STORAGE_TYPE = 'local'
|
15
16
|
|
16
|
-
config_param :path, :string, default:
|
17
|
+
config_param :path, :string, default: '/var/log/journal'
|
17
18
|
config_param :filters, :array, default: []
|
18
|
-
config_param :pos_file, :string, default: nil, deprecated: "Use <storage> section with `persistent: true' instead"
|
19
19
|
config_param :read_from_head, :bool, default: false
|
20
|
-
config_param :strip_underscores, :bool, default: false, deprecated: "Use <entry> section or `systemd_entry` " \
|
21
|
-
"filter plugin instead"
|
22
20
|
config_param :tag, :string
|
23
21
|
|
24
22
|
config_section :storage do
|
25
|
-
config_set_default :usage,
|
23
|
+
config_set_default :usage, 'positions'
|
26
24
|
config_set_default :@type, DEFAULT_STORAGE_TYPE
|
27
25
|
config_set_default :persistent, false
|
28
26
|
end
|
29
27
|
|
30
|
-
config_section :entry, param_name:
|
28
|
+
config_section :entry, param_name: 'entry_opts', required: false, multi: false do
|
31
29
|
config_param :field_map, :hash, default: {}
|
32
30
|
config_param :field_map_strict, :bool, default: false
|
33
31
|
config_param :fields_strip_underscores, :bool, default: false
|
@@ -37,27 +35,16 @@ module Fluent
|
|
37
35
|
def configure(conf)
|
38
36
|
super
|
39
37
|
@journal = nil
|
40
|
-
@pos_storage =
|
41
|
-
|
42
|
-
|
43
|
-
mut_opts = @strip_underscores ? { fields_strip_underscores: true } : @entry_opts.to_h
|
44
|
-
@mutator = SystemdEntryMutator.new(**mut_opts)
|
45
|
-
if @mutator.field_map_strict && @mutator.field_map.empty?
|
46
|
-
log.warn("`field_map_strict` set to true with empty `field_map`, expect no fields")
|
47
|
-
end
|
38
|
+
@pos_storage = storage_create(usage: 'positions')
|
39
|
+
@mutator = SystemdEntryMutator.new(**@entry_opts.to_h)
|
40
|
+
@mutator.warnings.each { |warning| log.warn(warning) }
|
48
41
|
end
|
49
42
|
|
50
43
|
def start
|
51
44
|
super
|
52
|
-
@pos_storage.start
|
53
45
|
timer_execute(:in_systemd_emit_worker, 1, &method(:run))
|
54
46
|
end
|
55
47
|
|
56
|
-
def shutdown
|
57
|
-
@pos_storage.shutdown
|
58
|
-
super
|
59
|
-
end
|
60
|
-
|
61
48
|
private
|
62
49
|
|
63
50
|
def init_journal
|
@@ -80,8 +67,8 @@ module Fluent
|
|
80
67
|
seek_to(cursor || read_from)
|
81
68
|
rescue Systemd::JournalError
|
82
69
|
log.warn(
|
83
|
-
"Could not seek to cursor #{cursor} found in
|
84
|
-
"falling back to reading from #{read_from}"
|
70
|
+
"Could not seek to cursor #{cursor} found in position file: #{@pos_storage.path}, " \
|
71
|
+
"falling back to reading from #{read_from}"
|
85
72
|
)
|
86
73
|
seek_to(read_from)
|
87
74
|
end
|
@@ -120,7 +107,7 @@ module Fluent
|
|
120
107
|
retries += 1
|
121
108
|
sleep 1.5**retries + rand(0..3)
|
122
109
|
retry
|
123
|
-
rescue => e
|
110
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
124
111
|
log.error("Exception emitting record: #{e}")
|
125
112
|
end
|
126
113
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require 'fluent/config/error'
|
3
4
|
|
4
5
|
module Fluent
|
5
6
|
module Plugin
|
@@ -19,12 +20,11 @@ module Fluent
|
|
19
20
|
# "<new_field2>" => ["<source_field2>"]
|
20
21
|
# }
|
21
22
|
class SystemdEntryMutator
|
22
|
-
|
23
23
|
Options = Struct.new(
|
24
24
|
:field_map,
|
25
25
|
:field_map_strict,
|
26
26
|
:fields_lowercase,
|
27
|
-
:fields_strip_underscores
|
27
|
+
:fields_strip_underscores
|
28
28
|
)
|
29
29
|
|
30
30
|
def self.default_opts
|
@@ -56,6 +56,10 @@ module Fluent
|
|
56
56
|
super
|
57
57
|
end
|
58
58
|
|
59
|
+
def respond_to_missing?(sym, include_private = false)
|
60
|
+
@opts.members.include?(sym) || super
|
61
|
+
end
|
62
|
+
|
59
63
|
# The main run method that performs all configured mutations, if any,
|
60
64
|
# against a single journal entry. Returns the mutated entry hash.
|
61
65
|
# entry - hash or `Systemd::Journal:Entry`
|
@@ -69,13 +73,11 @@ module Fluent
|
|
69
73
|
# entry hash.
|
70
74
|
# entry - hash or `Systemd::Journal:Entry`
|
71
75
|
def map_fields(entry)
|
72
|
-
|
73
|
-
@map.each do |cstm, sysds|
|
76
|
+
@map.each_with_object({}) do |(cstm, sysds), mapped|
|
74
77
|
vals = sysds.collect { |fld| entry[fld] }.compact
|
75
78
|
next if vals.empty? # systemd field does not exist in source entry
|
76
|
-
mapped[cstm] = vals
|
79
|
+
mapped[cstm] = join_if_needed(vals)
|
77
80
|
end
|
78
|
-
mapped
|
79
81
|
end
|
80
82
|
|
81
83
|
# Run field formatting (mutations applied to all non-mapped fields)
|
@@ -84,20 +86,34 @@ module Fluent
|
|
84
86
|
# mapped - Optional hash that represents a previously mapped entry to
|
85
87
|
# which the formatted fields will be added
|
86
88
|
def format_fields(entry, mapped = nil)
|
87
|
-
mapped
|
88
|
-
entry.each do |fld, val|
|
89
|
+
entry.each_with_object(mapped || {}) do |(fld, val), formatted_entry|
|
89
90
|
# don't mess with explicitly mapped fields
|
90
91
|
next if @map_src_fields.include?(fld)
|
91
|
-
fld = fld
|
92
|
-
fld = fld.downcase if @opts.fields_lowercase
|
92
|
+
fld = format_field_name(fld)
|
93
93
|
# account for mapping (appending) to an existing systemd field
|
94
|
-
|
94
|
+
formatted_entry[fld] = join_if_needed([val, mapped[fld]])
|
95
95
|
end
|
96
|
-
|
96
|
+
end
|
97
|
+
|
98
|
+
def warnings
|
99
|
+
return [] unless field_map_strict && field_map.empty?
|
100
|
+
'`field_map_strict` set to true with empty `field_map`, expect no fields'
|
97
101
|
end
|
98
102
|
|
99
103
|
private
|
100
104
|
|
105
|
+
def join_if_needed(values)
|
106
|
+
values.compact!
|
107
|
+
return values.first if values.length == 1
|
108
|
+
values.join(' ')
|
109
|
+
end
|
110
|
+
|
111
|
+
def format_field_name(name)
|
112
|
+
name = name.gsub(/\A_+/, '') if @opts.fields_strip_underscores
|
113
|
+
name = name.downcase if @opts.fields_lowercase
|
114
|
+
name
|
115
|
+
end
|
116
|
+
|
101
117
|
# Returns a `SystemdEntryMutator::Options` struct derived from the
|
102
118
|
# elements in the supplied hash merged with the option defaults
|
103
119
|
def options_from_hash(opts)
|
@@ -109,39 +125,32 @@ module Fluent
|
|
109
125
|
end
|
110
126
|
|
111
127
|
def validate_options(opts)
|
112
|
-
|
113
|
-
|
114
|
-
end
|
115
|
-
unless validate_strings_or_empty(opts[:field_map].values, true)
|
116
|
-
err = "`field_map` values must be strings or array of strings"
|
117
|
-
end
|
128
|
+
validate_all_strings opts[:field_map].keys, '`field_map` keys must be strings'
|
129
|
+
validate_all_strings opts[:field_map].values, '`field_map` values must be strings or an array of strings', true
|
118
130
|
%i[field_map_strict fields_strip_underscores fields_lowercase].each do |opt|
|
119
|
-
|
131
|
+
validate_boolean opts[opt], opt
|
120
132
|
end
|
121
|
-
fail Fluent::ConfigError, err unless err.nil?
|
122
133
|
end
|
123
134
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
return true if arr.empty?
|
128
|
-
arr.each do |v|
|
129
|
-
return true if v.is_a?(String)
|
130
|
-
if v.is_a?(Array) && nested
|
131
|
-
v.each { |nstd| return false unless nstd.is_a?(String) }
|
132
|
-
end
|
135
|
+
def validate_all_strings(arr, message, allow_nesting = false)
|
136
|
+
valid = arr.all? do |value|
|
137
|
+
value.is_a?(String) || allow_nesting && value.is_a?(Array) && value.all? { |key| key.is_a?(String) }
|
133
138
|
end
|
134
|
-
|
139
|
+
raise Fluent::ConfigError, message unless valid
|
140
|
+
end
|
141
|
+
|
142
|
+
def validate_boolean(value, name)
|
143
|
+
raise Fluent::ConfigError, "`#{name}` must be boolean" unless [true, false].include?(value)
|
135
144
|
end
|
136
145
|
|
137
|
-
# Compute the inverse of a human friendly field map `
|
146
|
+
# Compute the inverse of a human friendly field map `field_map` which is what
|
138
147
|
# the mutator uses for the actual mapping. The resulting structure for
|
139
148
|
# the inverse field map hash is:
|
140
149
|
# {"<new_field_name>" => ["<source_field_name>", ...], ...}
|
141
|
-
def invert_field_map(
|
150
|
+
def invert_field_map(field_map)
|
142
151
|
invs = {}
|
143
|
-
|
144
|
-
sysds =
|
152
|
+
field_map.values.flatten.uniq.each do |cstm|
|
153
|
+
sysds = field_map.select { |_, v| (v == cstm || v.include?(cstm)) }
|
145
154
|
invs[cstm] = sysds.keys
|
146
155
|
end
|
147
156
|
invs
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-systemd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ed Robinson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,19 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.53.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.53.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: fluentd
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,7 +102,7 @@ dependencies:
|
|
102
102
|
version: '1.3'
|
103
103
|
description: This is a fluentd input plugin. It reads logs from the systemd journal.
|
104
104
|
email:
|
105
|
-
-
|
105
|
+
- edward-robinson@cookpad.com
|
106
106
|
executables: []
|
107
107
|
extensions: []
|
108
108
|
extra_rdoc_files: []
|
@@ -112,7 +112,6 @@ files:
|
|
112
112
|
- lib/fluent/plugin/filter_systemd_entry.rb
|
113
113
|
- lib/fluent/plugin/in_systemd.rb
|
114
114
|
- lib/fluent/plugin/systemd/entry_mutator.rb
|
115
|
-
- lib/fluent/plugin/systemd/pos_writer.rb
|
116
115
|
homepage: https://github.com/reevoo/fluent-plugin-systemd
|
117
116
|
licenses:
|
118
117
|
- MIT
|
@@ -128,12 +127,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
127
|
version: '0'
|
129
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
129
|
requirements:
|
131
|
-
- - "
|
130
|
+
- - ">"
|
132
131
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
132
|
+
version: 1.3.1
|
134
133
|
requirements: []
|
135
134
|
rubyforge_project:
|
136
|
-
rubygems_version: 2.6.
|
135
|
+
rubygems_version: 2.6.13
|
137
136
|
signing_key:
|
138
137
|
specification_version: 4
|
139
138
|
summary: Input plugin to read from systemd journal.
|
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "fluent/plugin/input"
|
3
|
-
|
4
|
-
module Fluent
|
5
|
-
module Plugin
|
6
|
-
class SystemdInput < Input
|
7
|
-
class PosWriter
|
8
|
-
def initialize(pos_file, storage)
|
9
|
-
@path = pos_file
|
10
|
-
@lock = Mutex.new
|
11
|
-
@storage = storage
|
12
|
-
@cursor = nil
|
13
|
-
@written_cursor = nil
|
14
|
-
setup
|
15
|
-
end
|
16
|
-
|
17
|
-
def get(key)
|
18
|
-
@storage ? @storage.get(key) : @cursor
|
19
|
-
end
|
20
|
-
|
21
|
-
def put(key, cursor)
|
22
|
-
return @storage.put(key, cursor) if @storage
|
23
|
-
@lock.synchronize { @cursor = cursor }
|
24
|
-
end
|
25
|
-
|
26
|
-
def path
|
27
|
-
@path || @storage.path
|
28
|
-
end
|
29
|
-
|
30
|
-
def start
|
31
|
-
return unless @path
|
32
|
-
@running = true
|
33
|
-
@thread = Thread.new(&method(:work))
|
34
|
-
end
|
35
|
-
|
36
|
-
def shutdown
|
37
|
-
return unless @path
|
38
|
-
@running = false
|
39
|
-
@thread.join
|
40
|
-
write_pos
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def setup
|
46
|
-
if @storage.persistent
|
47
|
-
migrate_to_storage if @path && File.exist?(@path)
|
48
|
-
elsif @path
|
49
|
-
@cursor = IO.read(@path).chomp if File.exist?(@path)
|
50
|
-
@storage = nil
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def migrate_to_storage
|
55
|
-
@storage.put(:journal, IO.read(@path).chomp)
|
56
|
-
File.delete(@path)
|
57
|
-
@path = nil
|
58
|
-
end
|
59
|
-
|
60
|
-
def work
|
61
|
-
while @running
|
62
|
-
write_pos
|
63
|
-
sleep 1
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def write_pos
|
68
|
-
@lock.synchronize do
|
69
|
-
if @written_cursor != @cursor
|
70
|
-
file = File.open(@path, "w+", 0o644)
|
71
|
-
file.print @cursor
|
72
|
-
file.close
|
73
|
-
@written_cursor = @cursor
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|