systemd-journal 2.0.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.
@@ -1,5 +1,5 @@
1
- require 'systemd/journal/native'
2
- require 'systemd/journal_error'
1
+ require "systemd/journal/native"
2
+ require "systemd/journal_error"
3
3
 
4
4
  module Systemd
5
5
  class Journal
@@ -8,21 +8,21 @@ module Systemd
8
8
  # (https://github.com/zonque/systemd-journal.gem).
9
9
  module Writable
10
10
  # system is unusable
11
- LOG_EMERG = 0
11
+ LOG_EMERG = 0
12
12
  # action must be taken immediately
13
- LOG_ALERT = 1
13
+ LOG_ALERT = 1
14
14
  # critical conditions
15
- LOG_CRIT = 2
15
+ LOG_CRIT = 2
16
16
  # error conditions
17
- LOG_ERR = 3
17
+ LOG_ERR = 3
18
18
  # warning conditions
19
19
  LOG_WARNING = 4
20
20
  # normal but significant condition
21
- LOG_NOTICE = 5
21
+ LOG_NOTICE = 5
22
22
  # informational
23
- LOG_INFO = 6
23
+ LOG_INFO = 6
24
24
  # debug-level messages
25
- LOG_DEBUG = 7
25
+ LOG_DEBUG = 7
26
26
 
27
27
  # @private
28
28
  def self.included(base)
@@ -66,7 +66,7 @@ module Systemd
66
66
  # severity of the event.
67
67
  # @param [String] message the content of the message to write.
68
68
  def print(level, message)
69
- rc = Native.sd_journal_print(level, message.to_s.gsub('%', '%%'))
69
+ rc = Native.sd_journal_print(level, message.to_s.gsub("%", "%%"))
70
70
  raise JournalError, rc if rc < 0
71
71
  end
72
72
 
@@ -74,7 +74,7 @@ module Systemd
74
74
  # @param [Hash] contents the set of key-value pairs defining the event.
75
75
  def message(contents)
76
76
  items = contents.flat_map do |k, v|
77
- value = v.to_s.gsub('%', '%%')
77
+ value = v.to_s.gsub("%", "%%")
78
78
  [:string, "#{k.to_s.upcase}=#{value}"]
79
79
  end
80
80
  # add a null pointer to terminate the varargs
@@ -1,17 +1,17 @@
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'
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.
@@ -26,11 +26,14 @@ module Systemd
26
26
  include Systemd::Journal::Filterable
27
27
  include Systemd::Journal::Waitable
28
28
 
29
+ # Returns the iterations to auto reopen
30
+ attr_reader :auto_reopen
31
+
29
32
  # Returns a new instance of a Journal, opened with the provided options.
30
33
  # @param [Hash] opts optional initialization parameters.
31
34
  # @option opts [Integer] :flags a set of bitwise OR-ed
32
35
  # {Systemd::Journal::Flags} which control what journal files are opened.
33
- # Defaults to `0`, meaning all journals avaiable to the current user.
36
+ # Defaults to `0`, meaning all journals available to the current user.
34
37
  # @option opts [String] :path if provided, open the journal files living
35
38
  # in the provided directory only. Any provided flags will be ignored
36
39
  # since sd_journal_open_directory does not currently accept any flags.
@@ -46,9 +49,15 @@ module Systemd
46
49
  # path: '/var/log/journal/5f5777e46c5f4131bd9b71cbed6b9abf'
47
50
  # )
48
51
  def initialize(opts = {})
52
+ @reopen_options = opts.dup # retain the options for auto reopen
49
53
  open_type, flags = validate_options!(opts)
50
54
  ptr = FFI::MemoryPointer.new(:pointer, 1)
51
55
 
56
+ @reopen_filter_conditions = [] # retain the conditions for auto reopen
57
+ @auto_reopen = (opts.key?(:auto_reopen) ? opts.delete(:auto_reopen) : false)
58
+ if @auto_reopen
59
+ @auto_reopen = @auto_reopen.is_a?(Integer) ? @auto_reopen : ITERATIONS_TO_AUTO_REOPEN
60
+ end
52
61
  @finalize = (opts.key?(:finalize) ? opts.delete(:finalize) : true)
53
62
  rc = open_journal(open_type, ptr, opts, flags)
54
63
  raise JournalError, rc if rc < 0
@@ -62,7 +71,7 @@ module Systemd
62
71
  j = new(opts.merge(finalize: false))
63
72
  yield j
64
73
  ensure
65
- j.close if j
74
+ j&.close
66
75
  end
67
76
 
68
77
  # Iterate over each entry in the journal, respecting the applied
@@ -88,13 +97,13 @@ module Systemd
88
97
  def read_field(field)
89
98
  len_ptr = FFI::MemoryPointer.new(:size_t, 1)
90
99
  out_ptr = FFI::MemoryPointer.new(:pointer, 1)
91
- field = field.to_s.upcase
100
+ field = field.to_s.upcase
92
101
  rc = Native.sd_journal_get_data(@ptr, field, out_ptr, len_ptr)
93
102
 
94
103
  raise JournalError, rc if rc < 0
95
104
 
96
105
  len = len_ptr.read_size_t
97
- string_from_out_ptr(out_ptr, len).split('=', 2).last
106
+ string_from_out_ptr(out_ptr, len).split("=", 2).last
98
107
  end
99
108
 
100
109
  # Read the contents of all fields from the current journal entry.
@@ -121,7 +130,7 @@ module Systemd
121
130
 
122
131
  JournalEntry.new(
123
132
  results,
124
- realtime_ts: read_realtime,
133
+ realtime_ts: read_realtime,
125
134
  monotonic_ts: read_monotonic
126
135
  )
127
136
  end
@@ -228,6 +237,21 @@ module Systemd
228
237
  )
229
238
  end
230
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
+
231
255
  private
232
256
 
233
257
  def open_journal(type, ptr, opts, flags)
@@ -239,13 +263,13 @@ module Systemd
239
263
  Native.sd_journal_open_directory(ptr, opts[:path], 0)
240
264
  when :files, :file
241
265
  files = Array(opts[type])
242
- @open_target = "file#{files.one? ? '' : 's'}:#{files.join(',')}"
266
+ @open_target = "file#{files.one? ? "" : "s"}:#{files.join(",")}"
243
267
  Native.sd_journal_open_files(ptr, array_to_ptrs(files), 0)
244
268
  when :container
245
269
  @open_target = "container:#{opts[:container]}"
246
270
  Native.sd_journal_open_container(ptr, opts[:container], flags)
247
271
  when :local
248
- @open_target = 'journal:local'
272
+ @open_target = "journal:local"
249
273
  Native.sd_journal_open(ptr, flags)
250
274
  else
251
275
  raise ArgumentError, "Unknown open type: #{type}"
@@ -261,7 +285,7 @@ module Systemd
261
285
  end
262
286
 
263
287
  def read_monotonic
264
- out = FFI::MemoryPointer.new(:uint64, 1)
288
+ out = FFI::MemoryPointer.new(:uint64, 1)
265
289
  boot = FFI::MemoryPointer.new(Systemd::Id128::Native::Id128, 1)
266
290
 
267
291
  rc = Native.sd_journal_get_monotonic_usec(@ptr, out, boot)
@@ -289,7 +313,7 @@ module Systemd
289
313
 
290
314
  if type == :container && !Native.open_container?
291
315
  raise ArgumentError,
292
- 'This native library version does not support opening containers'
316
+ "This native library version does not support opening containers"
293
317
  end
294
318
 
295
319
  flags = opts[:flags] if [:local, :container].include?(type)
@@ -298,10 +322,6 @@ module Systemd
298
322
  [type, flags]
299
323
  end
300
324
 
301
- def self.finalize(ptr)
302
- proc { Native.sd_journal_close(ptr) unless ptr.nil? }
303
- end
304
-
305
325
  def enumerate_helper(enum_function)
306
326
  len_ptr = FFI::MemoryPointer.new(:size_t, 1)
307
327
  out_ptr = FFI::MemoryPointer.new(:pointer, 1)
@@ -311,20 +331,11 @@ module Systemd
311
331
  return nil if rc == 0
312
332
 
313
333
  len = len_ptr.read_size_t
314
- string_from_out_ptr(out_ptr, len).split('=', 2)
334
+ string_from_out_ptr(out_ptr, len).split("=", 2)
315
335
  end
316
336
 
317
337
  def string_from_out_ptr(p, len)
318
338
  p.read_pointer.read_string(len)
319
339
  end
320
-
321
- # some sd_journal_* functions return strings that we're expected to free
322
- # ourselves. This function copies the string from a char* to a ruby string,
323
- # frees the char*, and returns the ruby string.
324
- def self.read_and_free_outstr(ptr)
325
- str = ptr.read_string
326
- Shim.free(ptr)
327
- str
328
- end
329
340
  end
330
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 = entry
16
- @ctx = context
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(m, *args) unless args.empty?
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('#<%s:0x%016x %s>', self.class.name, object_id, @inspect)
102
+ format("#<%s:0x%016x %s>", self.class.name, object_id, @inspect)
98
103
  end
99
104
 
100
105
  private
@@ -1,7 +1,7 @@
1
- require 'ffi'
1
+ require "ffi"
2
2
 
3
3
  module Systemd
4
- # This execption is raised whenever a sd_journal_* call returns an error.
4
+ # This exception is raised whenever a sd_journal_* call returns an error.
5
5
  class JournalError < StandardError
6
6
  # Returns the (positive) error number.
7
7
  attr_reader :code
@@ -1,2 +1,2 @@
1
1
  # included for backwards compatibility with older versions
2
- require 'systemd/journal'
2
+ require "systemd/journal"
data/lib/systemd.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'systemd/id128'
1
+ require "systemd/id128"
2
2
 
3
3
  module Systemd
4
4
  # See {Systemd::Id128.machine_id}.
data/spec/spec_helper.rb CHANGED
@@ -1,18 +1,18 @@
1
- require 'rspec'
2
- require 'json'
3
- require 'simplecov'
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('..', __FILE__), 'fixtures')
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, 'test.journal')
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, 'test.json')))
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 '.bundle/'
24
+ add_filter ".bundle/"
25
25
  end
26
- require 'systemd/journal'
26
+ require "systemd/journal"
27
27
 
28
28
  RSpec.configure do |config|
29
29
  config.disable_monkey_patching!
@@ -1,14 +1,14 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  RSpec.describe Systemd::Id128 do
4
4
  subject(:id128) { Systemd::Id128 }
5
5
 
6
- describe 'machine_id' do
7
- it 'should be a 128 bit hexadecimal string' do
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 'should match when called twice' do
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
- context 'when booted under systemd' do
21
- describe 'boot_id' do
22
- it 'should be a 128 bit hexadecimal string' do
23
- expect(id128.boot_id).to match(/[0-9a-f]{16}/)
24
- end
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
- it 'should match when called twice' do
27
- b1 = id128.boot_id
28
- b2 = id128.boot_id
29
- expect(b1).to eq(b2)
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 unless ENV['TRAVIS']
34
+ end
33
35
 
34
- describe 'random' do
35
- it 'should be a 128 bit hexadecimal string' do
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 'should return a different value when called again' do
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 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  RSpec.describe Systemd::JournalEntry do
4
- let(:msg) { 'test message' }
5
- let(:pid) { '123' }
6
- let(:hash) { { '_PID' => pid, 'MESSAGE' => msg } }
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 'initialize' do
10
- it 'takes a hash as an argument' do
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 '[]' do
16
- it 'accepts symbols as a field name' do
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 'accepts strings as a field name' do
21
- expect(entry['message']).to eq(msg)
20
+ it "accepts strings as a field name" do
21
+ expect(entry["message"]).to eq(msg)
22
22
  end
23
23
 
24
- it 'doesnt care about case' do
25
- expect(entry['MeSSage']).to eq(msg)
24
+ it "doesnt care about case" do
25
+ expect(entry["MeSSage"]).to eq(msg)
26
26
  end
27
27
 
28
- it 'returns nil if not found' do
29
- expect(entry['missing']).to be nil
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 'each' do
34
- it 'is chainable as an enumerator' do
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 'yields each key/value in turn' do
38
+ it "yields each key/value in turn" do
39
39
  items = entry.map { |k, v| [k, v] }
40
- expect(items).to eq([['_PID', pid], ['MESSAGE', msg]])
40
+ expect(items).to eq([["_PID", pid], ["MESSAGE", msg]])
41
41
  end
42
42
  end
43
43
 
44
- describe 'to_h' do
45
- it 'returns a deep copy of the entry' do
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['_PID'] << '3' }.to_not change { subject._pid }
48
+ expect { copy["_PID"] << "3" }.to_not change { subject._pid }
49
49
  end
50
50
  end
51
51
 
52
- describe 'catalog' do
53
- context 'without a catalog' do
54
- it 'returns nil if the entry has no catalog' do
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 'with a catalog' do
60
- let(:catalog) { 'Process @_PID@ said @MESSAGE@' }
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: '123'))
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 'does field substitution by default' do
70
- expect(entry.catalog).to eq('Process 123 said test message')
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 'does field substitution when requested' do
73
+ it "does field substitution when requested" do
74
74
  expect(entry.catalog(replace: true))
75
- .to eq('Process 123 said test message')
75
+ .to eq("Process 123 said test message")
76
76
  end
77
77
 
78
- it 'skips field substition if requested' do
78
+ it "skips field substitution if requested" do
79
79
  expect(entry.catalog(replace: false)).to eq(catalog)
80
80
  end
81
81
  end