systemd-journal 2.1.0 → 2.1.1
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/.github/workflows/ruby.yml +17 -5
- data/Gemfile +2 -2
- data/Rakefile +9 -17
- data/examples/journal_directory.rb +1 -1
- data/examples/ssh_watcher.rb +6 -6
- data/lib/systemd/ffi_size_t.rb +5 -5
- data/lib/systemd/id128.rb +12 -14
- data/lib/systemd/journal/fields.rb +9 -9
- data/lib/systemd/journal/filterable.rb +29 -0
- data/lib/systemd/journal/flags.rb +3 -3
- data/lib/systemd/journal/native.rb +30 -30
- data/lib/systemd/journal/navigable.rb +29 -20
- data/lib/systemd/journal/version.rb +1 -1
- data/lib/systemd/journal/waitable.rb +4 -4
- data/lib/systemd/journal/writable.rb +11 -11
- data/lib/systemd/journal.rb +39 -36
- data/lib/systemd/journal_entry.rb +10 -5
- data/lib/systemd/journal_error.rb +1 -1
- data/lib/systemd-journal.rb +1 -1
- data/lib/systemd.rb +1 -1
- data/spec/spec_helper.rb +8 -8
- data/spec/systemd/id128_spec.rb +19 -17
- data/spec/systemd/journal_entry_spec.rb +32 -32
- data/spec/systemd/journal_spec.rb +148 -133
- data/spec/systemd_spec.rb +5 -5
- data/systemd-journal.gemspec +23 -25
- metadata +18 -28
data/lib/systemd/journal.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
1
|
+
require "systemd/journal/version"
|
2
|
+
require "systemd/journal/native"
|
3
|
+
require "systemd/journal/flags"
|
4
|
+
require "systemd/journal/writable"
|
5
|
+
require "systemd/journal/fields"
|
6
|
+
require "systemd/journal/navigable"
|
7
|
+
require "systemd/journal/filterable"
|
8
|
+
require "systemd/journal/waitable"
|
9
|
+
require "systemd/journal/shim"
|
10
|
+
require "systemd/journal_error"
|
11
|
+
require "systemd/journal_entry"
|
12
|
+
require "systemd/id128"
|
13
|
+
require "systemd/ffi_size_t"
|
14
|
+
require "systemd"
|
15
15
|
|
16
16
|
module Systemd
|
17
17
|
# Class to allow interacting with the systemd journal.
|
@@ -53,6 +53,7 @@ module Systemd
|
|
53
53
|
open_type, flags = validate_options!(opts)
|
54
54
|
ptr = FFI::MemoryPointer.new(:pointer, 1)
|
55
55
|
|
56
|
+
@reopen_filter_conditions = [] # retain the conditions for auto reopen
|
56
57
|
@auto_reopen = (opts.key?(:auto_reopen) ? opts.delete(:auto_reopen) : false)
|
57
58
|
if @auto_reopen
|
58
59
|
@auto_reopen = @auto_reopen.is_a?(Integer) ? @auto_reopen : ITERATIONS_TO_AUTO_REOPEN
|
@@ -70,7 +71,7 @@ module Systemd
|
|
70
71
|
j = new(opts.merge(finalize: false))
|
71
72
|
yield j
|
72
73
|
ensure
|
73
|
-
j
|
74
|
+
j&.close
|
74
75
|
end
|
75
76
|
|
76
77
|
# Iterate over each entry in the journal, respecting the applied
|
@@ -96,13 +97,13 @@ module Systemd
|
|
96
97
|
def read_field(field)
|
97
98
|
len_ptr = FFI::MemoryPointer.new(:size_t, 1)
|
98
99
|
out_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
99
|
-
field
|
100
|
+
field = field.to_s.upcase
|
100
101
|
rc = Native.sd_journal_get_data(@ptr, field, out_ptr, len_ptr)
|
101
102
|
|
102
103
|
raise JournalError, rc if rc < 0
|
103
104
|
|
104
105
|
len = len_ptr.read_size_t
|
105
|
-
string_from_out_ptr(out_ptr, len).split(
|
106
|
+
string_from_out_ptr(out_ptr, len).split("=", 2).last
|
106
107
|
end
|
107
108
|
|
108
109
|
# Read the contents of all fields from the current journal entry.
|
@@ -129,7 +130,7 @@ module Systemd
|
|
129
130
|
|
130
131
|
JournalEntry.new(
|
131
132
|
results,
|
132
|
-
realtime_ts:
|
133
|
+
realtime_ts: read_realtime,
|
133
134
|
monotonic_ts: read_monotonic
|
134
135
|
)
|
135
136
|
end
|
@@ -236,6 +237,21 @@ module Systemd
|
|
236
237
|
)
|
237
238
|
end
|
238
239
|
|
240
|
+
# @private
|
241
|
+
def self.finalize(ptr)
|
242
|
+
proc { Native.sd_journal_close(ptr) unless ptr.nil? }
|
243
|
+
end
|
244
|
+
|
245
|
+
# @private
|
246
|
+
# some sd_journal_* functions return strings that we're expected to free
|
247
|
+
# ourselves. This function copies the string from a char* to a ruby string,
|
248
|
+
# frees the char*, and returns the ruby string.
|
249
|
+
def self.read_and_free_outstr(ptr)
|
250
|
+
str = ptr.read_string
|
251
|
+
Shim.free(ptr)
|
252
|
+
str
|
253
|
+
end
|
254
|
+
|
239
255
|
private
|
240
256
|
|
241
257
|
def open_journal(type, ptr, opts, flags)
|
@@ -247,13 +263,13 @@ module Systemd
|
|
247
263
|
Native.sd_journal_open_directory(ptr, opts[:path], 0)
|
248
264
|
when :files, :file
|
249
265
|
files = Array(opts[type])
|
250
|
-
@open_target = "file#{files.one? ?
|
266
|
+
@open_target = "file#{files.one? ? "" : "s"}:#{files.join(",")}"
|
251
267
|
Native.sd_journal_open_files(ptr, array_to_ptrs(files), 0)
|
252
268
|
when :container
|
253
269
|
@open_target = "container:#{opts[:container]}"
|
254
270
|
Native.sd_journal_open_container(ptr, opts[:container], flags)
|
255
271
|
when :local
|
256
|
-
@open_target =
|
272
|
+
@open_target = "journal:local"
|
257
273
|
Native.sd_journal_open(ptr, flags)
|
258
274
|
else
|
259
275
|
raise ArgumentError, "Unknown open type: #{type}"
|
@@ -269,7 +285,7 @@ module Systemd
|
|
269
285
|
end
|
270
286
|
|
271
287
|
def read_monotonic
|
272
|
-
out
|
288
|
+
out = FFI::MemoryPointer.new(:uint64, 1)
|
273
289
|
boot = FFI::MemoryPointer.new(Systemd::Id128::Native::Id128, 1)
|
274
290
|
|
275
291
|
rc = Native.sd_journal_get_monotonic_usec(@ptr, out, boot)
|
@@ -297,7 +313,7 @@ module Systemd
|
|
297
313
|
|
298
314
|
if type == :container && !Native.open_container?
|
299
315
|
raise ArgumentError,
|
300
|
-
|
316
|
+
"This native library version does not support opening containers"
|
301
317
|
end
|
302
318
|
|
303
319
|
flags = opts[:flags] if [:local, :container].include?(type)
|
@@ -306,10 +322,6 @@ module Systemd
|
|
306
322
|
[type, flags]
|
307
323
|
end
|
308
324
|
|
309
|
-
def self.finalize(ptr)
|
310
|
-
proc { Native.sd_journal_close(ptr) unless ptr.nil? }
|
311
|
-
end
|
312
|
-
|
313
325
|
def enumerate_helper(enum_function)
|
314
326
|
len_ptr = FFI::MemoryPointer.new(:size_t, 1)
|
315
327
|
out_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
@@ -319,20 +331,11 @@ module Systemd
|
|
319
331
|
return nil if rc == 0
|
320
332
|
|
321
333
|
len = len_ptr.read_size_t
|
322
|
-
string_from_out_ptr(out_ptr, len).split(
|
334
|
+
string_from_out_ptr(out_ptr, len).split("=", 2)
|
323
335
|
end
|
324
336
|
|
325
337
|
def string_from_out_ptr(p, len)
|
326
338
|
p.read_pointer.read_string(len)
|
327
339
|
end
|
328
|
-
|
329
|
-
# some sd_journal_* functions return strings that we're expected to free
|
330
|
-
# ourselves. This function copies the string from a char* to a ruby string,
|
331
|
-
# frees the char*, and returns the ruby string.
|
332
|
-
def self.read_and_free_outstr(ptr)
|
333
|
-
str = ptr.read_string
|
334
|
-
Shim.free(ptr)
|
335
|
-
str
|
336
|
-
end
|
337
340
|
end
|
338
341
|
end
|
@@ -12,8 +12,8 @@ module Systemd
|
|
12
12
|
# with a given journal entry.
|
13
13
|
def initialize(entry, context = {})
|
14
14
|
inspect = []
|
15
|
-
@entry
|
16
|
-
@ctx
|
15
|
+
@entry = entry
|
16
|
+
@ctx = context
|
17
17
|
@fields = entry.map do |key, value|
|
18
18
|
name = key.downcase.to_sym
|
19
19
|
define_singleton_method(name) { value } unless respond_to?(name)
|
@@ -21,7 +21,7 @@ module Systemd
|
|
21
21
|
inspect.push("#{name}: '#{value}'")
|
22
22
|
name
|
23
23
|
end
|
24
|
-
@inspect = inspect.join(
|
24
|
+
@inspect = inspect.join(", ")
|
25
25
|
end
|
26
26
|
|
27
27
|
# Returns the wall-clock time that this entry was received by the journal.
|
@@ -43,7 +43,12 @@ module Systemd
|
|
43
43
|
def method_missing(m, *args)
|
44
44
|
# not all journal entries will have all fields. don't raise an error
|
45
45
|
# unless the user passed arguments.
|
46
|
-
super
|
46
|
+
super unless args.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
# @private
|
50
|
+
def respond_to_missing?(m, include_private = false)
|
51
|
+
fields&.include?(m) || super
|
47
52
|
end
|
48
53
|
|
49
54
|
# Get the value of a given field in the entry, or nil if it doesn't exist
|
@@ -94,7 +99,7 @@ module Systemd
|
|
94
99
|
|
95
100
|
# @private
|
96
101
|
def inspect
|
97
|
-
format(
|
102
|
+
format("#<%s:0x%016x %s>", self.class.name, object_id, @inspect)
|
98
103
|
end
|
99
104
|
|
100
105
|
private
|
data/lib/systemd-journal.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# included for backwards compatibility with older versions
|
2
|
-
require
|
2
|
+
require "systemd/journal"
|
data/lib/systemd.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "rspec"
|
2
|
+
require "json"
|
3
|
+
require "simplecov"
|
4
4
|
|
5
5
|
module SpecHelper
|
6
6
|
def fixture_dir
|
7
|
-
@path ||= File.join(File.expand_path(
|
7
|
+
@path ||= File.join(File.expand_path("..", __FILE__), "fixtures")
|
8
8
|
end
|
9
9
|
|
10
10
|
def journal_file
|
11
|
-
@file ||= File.join(fixture_dir,
|
11
|
+
@file ||= File.join(fixture_dir, "test.journal")
|
12
12
|
end
|
13
13
|
|
14
14
|
def journal_json
|
15
|
-
@json ||= JSON.parse(File.read(File.join(fixture_dir,
|
15
|
+
@json ||= JSON.parse(File.read(File.join(fixture_dir, "test.json")))
|
16
16
|
end
|
17
17
|
|
18
18
|
def entry_field(index, name)
|
@@ -21,9 +21,9 @@ module SpecHelper
|
|
21
21
|
end
|
22
22
|
|
23
23
|
SimpleCov.start do
|
24
|
-
add_filter
|
24
|
+
add_filter ".bundle/"
|
25
25
|
end
|
26
|
-
require
|
26
|
+
require "systemd/journal"
|
27
27
|
|
28
28
|
RSpec.configure do |config|
|
29
29
|
config.disable_monkey_patching!
|
data/spec/systemd/id128_spec.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
RSpec.describe Systemd::Id128 do
|
4
4
|
subject(:id128) { Systemd::Id128 }
|
5
5
|
|
6
|
-
describe
|
7
|
-
it
|
6
|
+
describe "machine_id" do
|
7
|
+
it "should be a 128 bit hexadecimal string" do
|
8
8
|
expect(id128.machine_id).to match(/[0-9a-f]{16}/)
|
9
9
|
end
|
10
10
|
|
11
|
-
it
|
11
|
+
it "should match when called twice" do
|
12
12
|
m1 = id128.machine_id
|
13
13
|
m2 = id128.machine_id
|
14
14
|
expect(m1).to eq(m2)
|
@@ -17,26 +17,28 @@ RSpec.describe Systemd::Id128 do
|
|
17
17
|
|
18
18
|
# travis-ci does not boot with systemd so these cases
|
19
19
|
# will raise exceptions.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
unless ENV["TRAVIS"]
|
21
|
+
context "when booted under systemd" do
|
22
|
+
describe "boot_id" do
|
23
|
+
it "should be a 128 bit hexadecimal string" do
|
24
|
+
expect(id128.boot_id).to match(/[0-9a-f]{16}/)
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
it "should match when called twice" do
|
28
|
+
b1 = id128.boot_id
|
29
|
+
b2 = id128.boot_id
|
30
|
+
expect(b1).to eq(b2)
|
31
|
+
end
|
30
32
|
end
|
31
33
|
end
|
32
|
-
end
|
34
|
+
end
|
33
35
|
|
34
|
-
describe
|
35
|
-
it
|
36
|
+
describe "random" do
|
37
|
+
it "should be a 128 bit hexadecimal string" do
|
36
38
|
expect(id128.random).to match(/[0-9a-f]{16}/)
|
37
39
|
end
|
38
40
|
|
39
|
-
it
|
41
|
+
it "should return a different value when called again" do
|
40
42
|
r1 = id128.random
|
41
43
|
r2 = id128.random
|
42
44
|
expect(r1).to_not eq(r2)
|
@@ -1,81 +1,81 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
RSpec.describe Systemd::JournalEntry do
|
4
|
-
let(:msg)
|
5
|
-
let(:pid)
|
6
|
-
let(:hash)
|
4
|
+
let(:msg) { "test message" }
|
5
|
+
let(:pid) { "123" }
|
6
|
+
let(:hash) { {"_PID" => pid, "MESSAGE" => msg} }
|
7
7
|
subject(:entry) { Systemd::JournalEntry.new(hash) }
|
8
8
|
|
9
|
-
describe
|
10
|
-
it
|
9
|
+
describe "initialize" do
|
10
|
+
it "takes a hash as an argument" do
|
11
11
|
expect { Systemd::JournalEntry.new(hash) }.to_not raise_error
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
describe
|
16
|
-
it
|
15
|
+
describe "[]" do
|
16
|
+
it "accepts symbols as a field name" do
|
17
17
|
expect(entry[:message]).to eq(msg)
|
18
18
|
end
|
19
19
|
|
20
|
-
it
|
21
|
-
expect(entry[
|
20
|
+
it "accepts strings as a field name" do
|
21
|
+
expect(entry["message"]).to eq(msg)
|
22
22
|
end
|
23
23
|
|
24
|
-
it
|
25
|
-
expect(entry[
|
24
|
+
it "doesnt care about case" do
|
25
|
+
expect(entry["MeSSage"]).to eq(msg)
|
26
26
|
end
|
27
27
|
|
28
|
-
it
|
29
|
-
expect(entry[
|
28
|
+
it "returns nil if not found" do
|
29
|
+
expect(entry["missing"]).to be nil
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
describe
|
34
|
-
it
|
33
|
+
describe "each" do
|
34
|
+
it "is chainable as an enumerator" do
|
35
35
|
expect(entry.each.class).to be(Enumerator)
|
36
36
|
end
|
37
37
|
|
38
|
-
it
|
38
|
+
it "yields each key/value in turn" do
|
39
39
|
items = entry.map { |k, v| [k, v] }
|
40
|
-
expect(items).to eq([[
|
40
|
+
expect(items).to eq([["_PID", pid], ["MESSAGE", msg]])
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
describe
|
45
|
-
it
|
44
|
+
describe "to_h" do
|
45
|
+
it "returns a deep copy of the entry" do
|
46
46
|
copy = subject.to_h
|
47
47
|
expect(copy).to eq(hash)
|
48
|
-
expect { copy[
|
48
|
+
expect { copy["_PID"] << "3" }.to_not change { subject._pid }
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
describe
|
53
|
-
context
|
54
|
-
it
|
52
|
+
describe "catalog" do
|
53
|
+
context "without a catalog" do
|
54
|
+
it "returns nil if the entry has no catalog" do
|
55
55
|
expect(entry.catalog).to be nil
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
context
|
60
|
-
let(:catalog) {
|
59
|
+
context "with a catalog" do
|
60
|
+
let(:catalog) { "Process @_PID@ said @MESSAGE@" }
|
61
61
|
subject(:entry) do
|
62
|
-
Systemd::JournalEntry.new(hash.merge(message_id:
|
62
|
+
Systemd::JournalEntry.new(hash.merge(message_id: "123"))
|
63
63
|
end
|
64
64
|
|
65
65
|
before(:each) do
|
66
66
|
allow(Systemd::Journal).to receive(:catalog_for).and_return(catalog)
|
67
67
|
end
|
68
68
|
|
69
|
-
it
|
70
|
-
expect(entry.catalog).to eq(
|
69
|
+
it "does field substitution by default" do
|
70
|
+
expect(entry.catalog).to eq("Process 123 said test message")
|
71
71
|
end
|
72
72
|
|
73
|
-
it
|
73
|
+
it "does field substitution when requested" do
|
74
74
|
expect(entry.catalog(replace: true))
|
75
|
-
.to eq(
|
75
|
+
.to eq("Process 123 said test message")
|
76
76
|
end
|
77
77
|
|
78
|
-
it
|
78
|
+
it "skips field substitution if requested" do
|
79
79
|
expect(entry.catalog(replace: false)).to eq(catalog)
|
80
80
|
end
|
81
81
|
end
|