systemd-journal 1.1.3 → 1.2.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/.rubocop.yml +21 -7
- data/.travis.yml +23 -0
- data/lib/systemd/id128.rb +2 -2
- data/lib/systemd/journal.rb +39 -13
- data/lib/systemd/journal/fields.rb +6 -6
- data/lib/systemd/journal/native.rb +9 -4
- data/lib/systemd/journal/version.rb +1 -1
- data/lib/systemd/journal/waitable.rb +0 -1
- data/lib/systemd/journal/writable.rb +19 -1
- data/lib/systemd/journal_entry.rb +19 -4
- data/lib/systemd/journal_error.rb +0 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/systemd/journal_entry_spec.rb +74 -0
- data/spec/systemd/journal_spec.rb +207 -18
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 978a030e0e1fbf45c4d7cf8f597fb746c125d9ed
|
4
|
+
data.tar.gz: b4ead8920befcd90d27351b7552128347e165a6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16629bfb3cf7f6d58894eeec4f2e977c6eeed09eca46c09669060b6f25c1cb10237a3f2aa03551b291e974e7ae6ee10e884b11ffc4069680a30a17c76b11f51d
|
7
|
+
data.tar.gz: 45339d61c9c50b4eec0fcf36ac68d9cfe9feeacd8bb07f2d47f5c235b1d2e0f1bd64ce29a6118c6f226e1918d717a8b10285abc69816e56232f5c3227f8801ca
|
data/.rubocop.yml
CHANGED
@@ -4,14 +4,28 @@ SignalException:
|
|
4
4
|
RaiseArgs:
|
5
5
|
Enabled: false
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
SpaceInsideBlockBraces:
|
8
|
+
Enabled: true
|
9
|
+
SpaceBeforeBlockBraces:
|
10
|
+
Enabled: true
|
11
|
+
|
9
12
|
Lambda:
|
10
13
|
Enabled: false
|
11
14
|
|
12
|
-
GlobalVars:
|
13
|
-
AllowedVariables:
|
14
|
-
- $NO_FFI_SPEC
|
15
|
-
|
16
15
|
Documentation:
|
17
|
-
Enabled: false
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
MethodLength:
|
19
|
+
Max: 20
|
20
|
+
|
21
|
+
ClassLength:
|
22
|
+
Max: 150
|
23
|
+
|
24
|
+
Style/FileName:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/GuardClause:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/ClassAndModuleChildren:
|
31
|
+
Enabled: false
|
data/.travis.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1.2
|
4
|
+
- 1.9.3
|
5
|
+
before_script:
|
6
|
+
- export PREV_DIR=$(pwd)
|
7
|
+
- sudo apt-get update -qq
|
8
|
+
- sudo apt-get install autotools-dev automake autoconf libtool libdbus-1-dev libcap-dev libblkid-dev libpam-dev libcryptsetup-dev libaudit-dev libacl1-dev libattr1-dev libselinux-dev liblzma-dev libgcrypt-dev libqrencode-dev libmicrohttpd-dev gtk-doc-tools gperf python2.7-dev
|
9
|
+
- git clone git://github.com/systemd/systemd.git /tmp/systemd
|
10
|
+
- >
|
11
|
+
cd /tmp/systemd/ &&
|
12
|
+
git checkout v209 &&
|
13
|
+
sh autogen.sh &&
|
14
|
+
./configure --disable-apparmor --disable-audit --disable-selinux
|
15
|
+
--disable-machined --disable-logind --disable-timedated
|
16
|
+
--disable-timesyncd --disable-localed --disable-resolved
|
17
|
+
--disable-networkd --disable-tests --disable-coredump --disable-hostnamed
|
18
|
+
--disable-manpages --disable-libcryptsetup --disable-python-devel
|
19
|
+
--sysconfdir=/etc --localstatedir=/var --libdir=/usr/lib
|
20
|
+
--with-rootprefix= --with-rootlibdir=/lib
|
21
|
+
- cd /tmp/systemd/ && make && sudo make install
|
22
|
+
- echo "done building in $(pwd), switching back to $PREV_DIR, library path is $LD_LIBRARY_PATH"
|
23
|
+
- cd $PREV_DIR
|
data/lib/systemd/id128.rb
CHANGED
@@ -45,8 +45,8 @@ module Systemd
|
|
45
45
|
module Native
|
46
46
|
require 'ffi'
|
47
47
|
extend FFI::Library
|
48
|
-
ffi_lib %w
|
49
|
-
libsystemd-id128.so.0 libsystemd-id128.so
|
48
|
+
ffi_lib %w( libsystemd.so.0 libsystemd.so
|
49
|
+
libsystemd-id128.so.0 libsystemd-id128.so )
|
50
50
|
|
51
51
|
attach_function :sd_id128_get_machine, [:pointer], :int
|
52
52
|
attach_function :sd_id128_get_boot, [:pointer], :int
|
data/lib/systemd/journal.rb
CHANGED
@@ -49,18 +49,9 @@ module Systemd
|
|
49
49
|
|
50
50
|
flags = opts[:flags] || 0
|
51
51
|
ptr = FFI::MemoryPointer.new(:pointer, 1)
|
52
|
+
opts[:files] = opts[:file] if opts[:file]
|
52
53
|
|
53
|
-
rc =
|
54
|
-
when opts[:path]
|
55
|
-
Native.sd_journal_open_directory(ptr, opts[:path], 0)
|
56
|
-
when opts[:files]
|
57
|
-
Native.sd_journal_open_files(ptr, array_to_ptrs(opts[:files]), 0)
|
58
|
-
when opts[:container]
|
59
|
-
Native.sd_journal_open_container(ptr, opts[:container], flags)
|
60
|
-
else
|
61
|
-
Native.sd_journal_open(ptr, flags)
|
62
|
-
end
|
63
|
-
|
54
|
+
rc = open_journal(ptr, opts, flags)
|
64
55
|
raise JournalError.new(rc) if rc < 0
|
65
56
|
|
66
57
|
@ptr = ptr.read_pointer
|
@@ -121,7 +112,11 @@ module Systemd
|
|
121
112
|
yield(key, value) if block_given?
|
122
113
|
end
|
123
114
|
|
124
|
-
JournalEntry.new(
|
115
|
+
JournalEntry.new(
|
116
|
+
results,
|
117
|
+
realtime_ts: read_realtime,
|
118
|
+
monotonic_ts: read_monotonic
|
119
|
+
)
|
125
120
|
end
|
126
121
|
|
127
122
|
def current_catalog
|
@@ -201,6 +196,37 @@ module Systemd
|
|
201
196
|
|
202
197
|
private
|
203
198
|
|
199
|
+
def open_journal(ptr, opts, flags)
|
200
|
+
case
|
201
|
+
when opts[:path]
|
202
|
+
Native.sd_journal_open_directory(ptr, opts[:path], 0)
|
203
|
+
when opts[:files]
|
204
|
+
Native.sd_journal_open_files(ptr, array_to_ptrs(Array(opts[:files])), 0)
|
205
|
+
when opts[:container]
|
206
|
+
Native.sd_journal_open_container(ptr, opts[:container], flags)
|
207
|
+
else
|
208
|
+
Native.sd_journal_open(ptr, flags)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def read_realtime
|
213
|
+
out = FFI::MemoryPointer.new(:uint64, 1)
|
214
|
+
rc = Native.sd_journal_get_realtime_usec(@ptr, out)
|
215
|
+
raise JournalError.new(rc) if rc < 0
|
216
|
+
|
217
|
+
out.read_uint64
|
218
|
+
end
|
219
|
+
|
220
|
+
def read_monotonic
|
221
|
+
out = FFI::MemoryPointer.new(:uint64, 1)
|
222
|
+
boot = FFI::MemoryPointer.new(Systemd::Id128::Native::Id128, 1)
|
223
|
+
|
224
|
+
rc = Native.sd_journal_get_monotonic_usec(@ptr, out, boot)
|
225
|
+
raise JournalError.new(rc) if rc < 0
|
226
|
+
|
227
|
+
[out.read_uint64, Systemd::Id128::Native::Id128.new(boot).to_s]
|
228
|
+
end
|
229
|
+
|
204
230
|
def array_to_ptrs(strings)
|
205
231
|
ptr = FFI::MemoryPointer.new(:pointer, strings.length + 1)
|
206
232
|
strings.each_with_index do |s, i|
|
@@ -211,7 +237,7 @@ module Systemd
|
|
211
237
|
end
|
212
238
|
|
213
239
|
def validate_options!(opts)
|
214
|
-
exclusive = [:path, :files, :container]
|
240
|
+
exclusive = [:path, :files, :container, :file]
|
215
241
|
provided = (opts.keys & exclusive)
|
216
242
|
if provided.length > 1
|
217
243
|
raise ArgumentError.new("#{provided} are conflicting options")
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module Systemd
|
2
2
|
class Journal
|
3
3
|
# Fields directly passed by client programs and stored in the journal.
|
4
|
-
USER_FIELDS = %w
|
5
|
-
ERRNO SYSLOG_FACILITY SYSLOG_IDENTIFIER SYSLOG_PID
|
4
|
+
USER_FIELDS = %w( MESSAGE MESSAGE_ID PRIORITY CODE_FILE CODE_LINE CODE_FUNC
|
5
|
+
ERRNO SYSLOG_FACILITY SYSLOG_IDENTIFIER SYSLOG_PID )
|
6
6
|
|
7
7
|
# Fields generated by the journal and added to each event.
|
8
|
-
TRUSTED_FIELDS = %w
|
8
|
+
TRUSTED_FIELDS = %w( _PID _UID _GID _COMM _EXE _CMDLINE _AUDIT_SESSION
|
9
9
|
_AUDIT_LOGINUID _SYSTEMD_CGROUP _SYSTEMD_SESSION
|
10
10
|
_SYSTEMD_UNIT _SYSTEMD_USER_UNIT _SYSTEMD_OWNER_UID
|
11
11
|
_SELINUX_CONTEXT _SOURCE_REALTIME_TIMESTAMP _BOOT_ID
|
12
|
-
_MACHINE_ID _HOSTNAME _TRANSPORT
|
12
|
+
_MACHINE_ID _HOSTNAME _TRANSPORT )
|
13
13
|
|
14
14
|
# Fields used in messages originating from the kernel.
|
15
|
-
KERNEL_FIELDS = %w
|
16
|
-
_UDEV_DEVNODE _UDEV_DEVLINK
|
15
|
+
KERNEL_FIELDS = %w( _KERNEL_DEVICE _KERNEL_SUBSYSTEM _UDEV_SYSNAME
|
16
|
+
_UDEV_DEVNODE _UDEV_DEVLINK )
|
17
17
|
end
|
18
18
|
end
|
@@ -8,8 +8,8 @@ module Systemd
|
|
8
8
|
# rubocop:disable LineLength
|
9
9
|
require 'ffi'
|
10
10
|
extend FFI::Library
|
11
|
-
ffi_lib %w
|
12
|
-
libsystemd-journal.so.0 libsystemd-journal.so
|
11
|
+
ffi_lib %w( libsystemd.so.0 libsystemd.so
|
12
|
+
libsystemd-journal.so.0 libsystemd-journal.so)
|
13
13
|
|
14
14
|
# setup/teardown
|
15
15
|
attach_function :sd_journal_open, [:pointer, :int], :int
|
@@ -40,6 +40,9 @@ module Systemd
|
|
40
40
|
attach_function :sd_journal_get_catalog, [:pointer, :pointer], :int
|
41
41
|
attach_function :sd_journal_get_catalog_for_message_id, [Systemd::Id128::Native::Id128.by_value, :pointer], :int
|
42
42
|
|
43
|
+
attach_function :sd_journal_get_realtime_usec, [:pointer, :pointer], :int
|
44
|
+
attach_function :sd_journal_get_monotonic_usec, [:pointer, :pointer, :pointer], :int
|
45
|
+
|
43
46
|
attach_function :sd_journal_get_data_threshold, [:pointer, :pointer], :int
|
44
47
|
attach_function :sd_journal_set_data_threshold, [:pointer, :size_t], :int
|
45
48
|
|
@@ -49,8 +52,8 @@ module Systemd
|
|
49
52
|
attach_function :sd_journal_restart_unique, [:pointer], :void
|
50
53
|
|
51
54
|
# event notification
|
52
|
-
enum
|
53
|
-
attach_function :sd_journal_wait,
|
55
|
+
enum :wake_reason, [:nop, :append, :invalidate]
|
56
|
+
attach_function :sd_journal_wait, [:pointer, :uint64], :wake_reason, blocking: true
|
54
57
|
attach_function :sd_journal_get_fd, [:pointer], :int
|
55
58
|
attach_function :sd_journal_process, [:pointer], :wake_reason
|
56
59
|
attach_function :sd_journal_reliable_fd, [:pointer], :int
|
@@ -65,6 +68,8 @@ module Systemd
|
|
65
68
|
attach_function :sd_journal_print, [:int, :string], :int
|
66
69
|
attach_function :sd_journal_send, [:varargs], :int
|
67
70
|
attach_function :sd_journal_perror, [:string], :int
|
71
|
+
attach_function :sd_journal_stream_fd, [:string, :int, :bool], :int
|
72
|
+
|
68
73
|
# misc
|
69
74
|
attach_function :sd_journal_get_usage, [:pointer, :pointer], :int
|
70
75
|
end
|
@@ -7,7 +7,7 @@ module Systemd
|
|
7
7
|
# the systemd-journal.gem by Daniel Mack
|
8
8
|
# (https://github.com/zonque/systemd-journal.gem).
|
9
9
|
module Writable
|
10
|
-
|
10
|
+
# system is unusable
|
11
11
|
LOG_EMERG = 0
|
12
12
|
# action must be taken immediately
|
13
13
|
LOG_ALERT = 1
|
@@ -32,6 +32,24 @@ module Systemd
|
|
32
32
|
# methods in this module will be available as class methods on
|
33
33
|
# {Systemd::Journal}
|
34
34
|
module ClassMethods
|
35
|
+
|
36
|
+
# Creates a new IO stream which writes newline-seperated messages to
|
37
|
+
# the journal.
|
38
|
+
# @param identifier [String] this value will be passed as
|
39
|
+
# SYSLOG_IDENTIFIER to the journal.
|
40
|
+
# @param priority [Integer] the log level for events writen to this
|
41
|
+
# stream.
|
42
|
+
# @param opts [Hash]
|
43
|
+
# @option opts [Boolean] :prefix true to enable kernel-style log
|
44
|
+
# priority prefixes
|
45
|
+
# @return [IO]
|
46
|
+
def log_stream(identifier, priority, opts = {})
|
47
|
+
fd = Native.sd_journal_stream_fd(identifier, priority, !!opts[:prefix])
|
48
|
+
raise JournalError.new(fd) if fd < 0
|
49
|
+
|
50
|
+
IO.new(fd, File::WRONLY, encoding: Encoding::UTF_8)
|
51
|
+
end
|
52
|
+
|
35
53
|
# write the value of the c errno constant to the systemd journal in the
|
36
54
|
# style of the perror() function.
|
37
55
|
# @param [String] message the text to prefix the error message with.
|
@@ -10,8 +10,9 @@ module Systemd
|
|
10
10
|
# {Systemd::Journal} methods such as {Systemd::Journal#current_entry}.
|
11
11
|
# @param [Hash] entry a hash containing all the key-value pairs associated
|
12
12
|
# with a given journal entry.
|
13
|
-
def initialize(entry)
|
13
|
+
def initialize(entry, context = {})
|
14
14
|
@entry = entry
|
15
|
+
@ctx = context
|
15
16
|
@fields = entry.map do |key, value|
|
16
17
|
name = key.downcase.to_sym
|
17
18
|
define_singleton_method(name) { value } unless respond_to?(name)
|
@@ -19,9 +20,23 @@ module Systemd
|
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
23
|
+
# Returns the wall-clock time that this entry was received by the journal.
|
24
|
+
# @return [Time]
|
25
|
+
def realtime_timestamp
|
26
|
+
@realtime_timestamp ||= Time.at(0, @ctx[:realtime_ts])
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the monotonic time (time since boot) that this entry was received
|
30
|
+
# by the journal. This should be associated with a boot_id.
|
31
|
+
# @return [Time]
|
32
|
+
def monotonic_timestamp
|
33
|
+
@monotonic_timestamp ||= Time.at(0, @ctx[:monotonic_ts].first)
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(m, *args)
|
37
|
+
# not all journal entries will have all fields. don't raise an error
|
38
|
+
# unless the user passed arguments.
|
39
|
+
super(m, *args) unless args.empty?
|
25
40
|
end
|
26
41
|
|
27
42
|
# Get the value of a given field in the entry, or nil if it doesn't exist
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Systemd::JournalEntry do
|
4
|
+
let(:msg) { 'test message' }
|
5
|
+
let(:pid) { '123' }
|
6
|
+
let(:hash) { { '_PID' => pid, 'MESSAGE' => msg } }
|
7
|
+
subject(:entry) { Systemd::JournalEntry.new(hash) }
|
8
|
+
|
9
|
+
describe 'initialize' do
|
10
|
+
it 'takes a hash as an argument' do
|
11
|
+
expect { Systemd::JournalEntry.new(hash) }.to_not raise_error
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '[]' do
|
16
|
+
it 'accepts symbols as a field name' do
|
17
|
+
expect(entry[:message]).to eq(msg)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'accepts strings as a field name' do
|
21
|
+
expect(entry['message']).to eq(msg)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'doesnt care about case' do
|
25
|
+
expect(entry['MeSSage']).to eq(msg)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns nil if not found' do
|
29
|
+
expect(entry['missing']).to be nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'each' do
|
34
|
+
it 'is chainable as an enumerator' do
|
35
|
+
expect(entry.each.class).to be(Enumerator)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'yields each key/value in turn' do
|
39
|
+
expect(entry.map{ |k,v| [k,v] }).to eq([['_PID', pid], ['MESSAGE', msg]])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'catalog' do
|
44
|
+
context 'without a catalog' do
|
45
|
+
it 'returns nil if the entry has no catalog' do
|
46
|
+
expect(entry.catalog).to be nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with a catalog' do
|
51
|
+
let(:catalog) { 'Process @_PID@ said @MESSAGE@' }
|
52
|
+
subject(:entry) do
|
53
|
+
Systemd::JournalEntry.new(hash.merge(message_id: '123'))
|
54
|
+
end
|
55
|
+
|
56
|
+
before(:each) do
|
57
|
+
allow(Systemd::Journal).to receive(:catalog_for).and_return(catalog)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does field substitution by default' do
|
61
|
+
expect(entry.catalog).to eq('Process 123 said test message')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'does field substitution when requested' do
|
65
|
+
expect(entry.catalog(replace: true))
|
66
|
+
.to eq('Process 123 said test message')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'skips field substition if requested' do
|
70
|
+
expect(entry.catalog(replace: false)).to eq(catalog)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Systemd::Journal do
|
4
4
|
subject(:j) do
|
5
|
-
Systemd::Journal.new(
|
5
|
+
Systemd::Journal.new(file: journal_file).tap do |j|
|
6
6
|
j.seek(:head)
|
7
7
|
j.move_next
|
8
8
|
end
|
@@ -31,7 +31,7 @@ RSpec.describe Systemd::Journal do
|
|
31
31
|
transports = %w(syslog journal stdout kernel driver)
|
32
32
|
|
33
33
|
expect(values.length).to eq(5)
|
34
|
-
expect(values).to
|
34
|
+
expect(values).to include(*transports)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -44,13 +44,15 @@ RSpec.describe Systemd::Journal do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'returns the disk usage of the example journal file' do
|
47
|
-
|
47
|
+
pending 'blocks? bytes?'
|
48
|
+
expect(j.disk_usage).to eq(4_005_888)
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
52
|
describe 'data_threshold' do
|
52
53
|
it 'throws a JournalError on invalid return code' do
|
53
|
-
expect(Systemd::Journal::Native)
|
54
|
+
expect(Systemd::Journal::Native)
|
55
|
+
.to receive(:sd_journal_get_data_threshold)
|
54
56
|
.and_return(-1)
|
55
57
|
|
56
58
|
expect { j.data_threshold }.to raise_error(Systemd::JournalError)
|
@@ -63,7 +65,8 @@ RSpec.describe Systemd::Journal do
|
|
63
65
|
|
64
66
|
describe 'data_threshold=' do
|
65
67
|
it 'throws a JournalError on invalid return code' do
|
66
|
-
expect(Systemd::Journal::Native)
|
68
|
+
expect(Systemd::Journal::Native)
|
69
|
+
.to receive(:sd_journal_set_data_threshold)
|
67
70
|
.and_return(-1)
|
68
71
|
|
69
72
|
expect { j.data_threshold = 10 }.to raise_error(Systemd::JournalError)
|
@@ -94,7 +97,7 @@ RSpec.describe Systemd::Journal do
|
|
94
97
|
it 'returns a JournalEntry with the correct values' do
|
95
98
|
entry = j.current_entry
|
96
99
|
expect(entry._hostname).to eq('arch')
|
97
|
-
expect(entry.message).to
|
100
|
+
expect(entry.message).to start_with('Allowing runtime journal')
|
98
101
|
end
|
99
102
|
end
|
100
103
|
|
@@ -114,26 +117,212 @@ RSpec.describe Systemd::Journal do
|
|
114
117
|
entries = j.each.map(&:message)
|
115
118
|
|
116
119
|
expect(entries.first).to start_with('Allowing runtime journal')
|
117
|
-
expect(entries.last).to
|
120
|
+
expect(entries.last).to start_with('ROOT LOGIN ON tty1')
|
118
121
|
end
|
119
122
|
end
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
let(:
|
124
|
-
let(:message_text) { 'Subject: The Journal has been started' }
|
124
|
+
context 'with catalog messages' do
|
125
|
+
let(:msg_id) { 'f77379a8490b408bbe5f6940505a777b' }
|
126
|
+
let(:msg_text) { 'Subject: The Journal has been started' }
|
125
127
|
|
126
|
-
|
127
|
-
|
128
|
-
|
128
|
+
describe 'catalog_for' do
|
129
|
+
subject(:j) { Systemd::Journal }
|
130
|
+
|
131
|
+
it 'throws a JournalError on invalid return code' do
|
132
|
+
expect(Systemd::Journal::Native)
|
133
|
+
.to receive(:sd_journal_get_catalog_for_message_id)
|
134
|
+
.and_return(-1)
|
135
|
+
|
136
|
+
expect { j.catalog_for(msg_id) }.to raise_error(Systemd::JournalError)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns the correct catalog entry' do
|
140
|
+
cat = Systemd::Journal.catalog_for(msg_id)
|
141
|
+
expect(cat).to start_with(msg_text)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'current_catalog' do
|
146
|
+
it 'throws a JournalError on invalid return code' do
|
147
|
+
expect(Systemd::Journal::Native)
|
148
|
+
.to receive(:sd_journal_get_catalog)
|
149
|
+
.and_return(-1)
|
150
|
+
|
151
|
+
expect { j.current_catalog }.to raise_error(Systemd::JournalError)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'returns the correct catalog entry' do
|
155
|
+
# find first entry with a catalog
|
156
|
+
j.move_next until j.current_entry.catalog?
|
157
|
+
|
158
|
+
expect(j.current_catalog).to start_with(msg_text)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe 'filter' do
|
164
|
+
it 'does basic filtering as expected' do
|
165
|
+
j.filter(_transport: 'kernel')
|
166
|
+
j.each do |entry|
|
167
|
+
expect(entry._transport).to eq('kernel')
|
168
|
+
end
|
169
|
+
expect(j.count).to eq(435)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'does filtering with AND conditions' do
|
173
|
+
j.filter(_transport: 'kernel', priority: 3)
|
174
|
+
expect(j.count).to eq(2)
|
175
|
+
j.each do |e|
|
176
|
+
expect(e._transport).to eq('kernel')
|
177
|
+
expect(e.priority).to eq('3')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'does basic filtering with multiple options for the same key' do
|
182
|
+
j.filter(_transport: %w(kernel driver))
|
183
|
+
j.each do |entry|
|
184
|
+
expect(%w(kernel driver)).to include(entry._transport)
|
185
|
+
end
|
186
|
+
expect(j.count).to eq(438)
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'does basic filtering with multiple keys' do
|
190
|
+
j.filter(
|
191
|
+
{ _transport: 'kernel' },
|
192
|
+
{ _systemd_unit: 'systemd-journald.service' }
|
193
|
+
)
|
194
|
+
|
195
|
+
c = j.each_with_object(Hash.new(0)) do |e, h|
|
196
|
+
h[:transport] += 1 if e._transport == 'kernel'
|
197
|
+
h[:unit] += 1 if e._systemd_unit == 'systemd-journald.service'
|
198
|
+
end
|
199
|
+
|
200
|
+
expect(c[:transport]).to eq(435)
|
201
|
+
expect(c[:unit]).to eq(3)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'does crazy stupid filtering' do
|
205
|
+
filter = [
|
206
|
+
{ _transport: 'kernel', priority: 4 },
|
207
|
+
{ _systemd_unit: 'getty@tty1.service' },
|
208
|
+
{ _systemd_unit: 'systemd-logind.service', seat_id: 'seat0' },
|
209
|
+
{ priority: [3, 5] }
|
210
|
+
]
|
211
|
+
|
212
|
+
j.filter(*filter)
|
213
|
+
|
214
|
+
c = j.each_with_object(Hash.new(0)) do |e, h|
|
215
|
+
h[:a] += 1 if e._transport == 'kernel' && e.priority == '4'
|
216
|
+
h[:b] += 1 if e._systemd_unit == 'getty@tty1.service'
|
217
|
+
if e._systemd_unit == 'systemd-logind.service' && e[:seat_id] == 'seat0'
|
218
|
+
h[:c] += 1
|
219
|
+
end
|
220
|
+
h[:d] += 1 if %w(3 5).include?(e.priority)
|
221
|
+
end
|
222
|
+
|
223
|
+
# from journalctl --file <fixture> <filter> --output json | wc -l
|
224
|
+
expect(c[:a]).to eq(26)
|
225
|
+
expect(c[:b]).to eq(1)
|
226
|
+
expect(c[:c]).to eq(1)
|
227
|
+
expect(c[:d]).to eq(11)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe 'cursor' do
|
232
|
+
it 'returns some opaque string' do
|
233
|
+
expect(j.cursor).to be_kind_of(String)
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'throws an error on failure' do
|
237
|
+
expect(Systemd::Journal::Native).to receive(:sd_journal_get_cursor)
|
238
|
+
.and_return(-1)
|
239
|
+
|
240
|
+
expect { j.cursor }.to raise_error(Systemd::JournalError)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe 'cursor?' do
|
245
|
+
let!(:cursor) { j.cursor }
|
246
|
+
it 'returns true if the cursor matches' do
|
247
|
+
expect(j.cursor?(cursor)).to be true
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'throws an error on failure' do
|
251
|
+
expect(Systemd::Journal::Native).to receive(:sd_journal_test_cursor)
|
129
252
|
.and_return(-1)
|
130
253
|
|
131
|
-
expect { j.
|
254
|
+
expect { j.cursor?(cursor) }.to raise_error(Systemd::JournalError)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe 'move' do
|
259
|
+
it 'moves by the specified number of entries' do
|
260
|
+
j.move(1)
|
261
|
+
expect(j.read_field(:message)).to eq(journal_json[1]['MESSAGE'])
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'does not move with 0' do
|
265
|
+
j.move(1)
|
266
|
+
j.move(0)
|
267
|
+
expect(j.read_field(:message)).to eq(journal_json[1]['MESSAGE'])
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'moves backwards' do
|
271
|
+
j.move(3)
|
272
|
+
j.move(-1)
|
273
|
+
expect(j.read_field(:message)).to eq(journal_json[2]['MESSAGE'])
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'returns the number of entries moved' do
|
277
|
+
expect(j.move(3)).to eq(3)
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'returns the number of entries moved even if if less' do
|
281
|
+
j.move(2)
|
282
|
+
expect(j.read_field(:message)).to eq(journal_json[2]['MESSAGE'])
|
283
|
+
expect(j.move(-5)).to eq(2)
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'returns 0 if it did not move' do
|
287
|
+
expect(j.move(-1)).to eq(0)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe 'seek' do
|
292
|
+
it 'treats a string parameter as the cursor' do
|
293
|
+
cursor = j.cursor
|
294
|
+
j.move(3)
|
295
|
+
expect(j.cursor?(cursor)).to be false
|
296
|
+
j.seek(cursor)
|
297
|
+
j.move_next
|
298
|
+
expect(j.cursor?(cursor)).to be true
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'can seek to the end' do
|
302
|
+
j.seek(:tail)
|
303
|
+
j.move_previous
|
304
|
+
expect(j.move_next).to be false
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'can seek to the start' do
|
308
|
+
j.seek(:start)
|
309
|
+
j.move_next
|
310
|
+
expect(j.move_previous).to be false
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'can seek based on timestamp' do
|
314
|
+
j.seek(Time.parse('2013-03-28T21:07:21-04:00'))
|
315
|
+
j.move_next
|
316
|
+
|
317
|
+
entry = j.current_entry
|
318
|
+
ts = entry.realtime_timestamp
|
319
|
+
|
320
|
+
expect(entry.message).to start_with('input: ImExPS/2')
|
321
|
+
expect(ts.utc.iso8601).to eq('2013-03-29T01:07:21Z')
|
132
322
|
end
|
133
323
|
|
134
|
-
it '
|
135
|
-
|
136
|
-
expect(cat).to start_with(message_text)
|
324
|
+
it 'throws an ArgumentError for other types' do
|
325
|
+
expect { j.seek(5) }.to raise_error(ArgumentError)
|
137
326
|
end
|
138
327
|
end
|
139
328
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: systemd-journal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Ledbetter
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-07-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -119,6 +119,7 @@ extra_rdoc_files: []
|
|
119
119
|
files:
|
120
120
|
- ".gitignore"
|
121
121
|
- ".rubocop.yml"
|
122
|
+
- ".travis.yml"
|
122
123
|
- Gemfile
|
123
124
|
- LICENSE.txt
|
124
125
|
- README.md
|
@@ -142,6 +143,7 @@ files:
|
|
142
143
|
- spec/fixtures/test.journal
|
143
144
|
- spec/fixtures/test.json
|
144
145
|
- spec/spec_helper.rb
|
146
|
+
- spec/systemd/journal_entry_spec.rb
|
145
147
|
- spec/systemd/journal_spec.rb
|
146
148
|
- systemd-journal.gemspec
|
147
149
|
homepage: https://github.com/ledbettj/systemd-journal
|
@@ -172,5 +174,6 @@ test_files:
|
|
172
174
|
- spec/fixtures/test.journal
|
173
175
|
- spec/fixtures/test.json
|
174
176
|
- spec/spec_helper.rb
|
177
|
+
- spec/systemd/journal_entry_spec.rb
|
175
178
|
- spec/systemd/journal_spec.rb
|
176
179
|
has_rdoc:
|