Module: Roby::DRoby::Logfile
- Extended by:
- Logger::Hierarchy
- Defined in:
- lib/roby/droby/logfile.rb,
lib/roby/droby/logfile/index.rb,
lib/roby/droby/logfile/client.rb,
lib/roby/droby/logfile/reader.rb,
lib/roby/droby/logfile/server.rb,
lib/roby/droby/logfile/writer.rb
Overview
Module containing all the logfile-related code
Note that unlike other Roby facilities, this file is not meant to be required directly. Require either logfile/reader or logfile/writer depending on what you need
Defined Under Namespace
Classes: Client, Index, IndexInvalid, IndexMissing, InvalidFileError, InvalidFormatVersion, Reader, Server, TruncatedFileError, UnknownFormatVersion, Writer
Constant Summary collapse
- MAGIC_CODE =
"ROBYLOG"- PROLOGUE_SIZE =
MAGIC_CODE.size + 4
- FORMAT_VERSION =
5
Class Method Summary collapse
-
.decode_one_chunk(chunk) ⇒ Object
Decode a chunk loaded with Logfile.read_one_chunk.
-
.guess_version(input) ⇒ Object
Guess the log file format version for the given IO.
-
.guess_version_from_marshalled_header(input) ⇒ Object
private
Guess the version of older files that had a single marshalled object as header.
-
.read_one_chunk(io) ⇒ Object
Load a chunk of data from an event file.
-
.read_prologue(io) ⇒ Object
Read a file prologue, validating that it is of the latest file format.
-
.validate_format(format) ⇒ Object
private
Validate the given format version.
-
.write_entry(io, chunk) ⇒ Object
Write a single entry in the log file.
-
.write_header(io, version: FORMAT_VERSION, **options) ⇒ Object
Write a log file header.
-
.write_prologue(io, version: FORMAT_VERSION) ⇒ Object
Write the file’s prologue, specifying the file magic and format version.
Class Method Details
.decode_one_chunk(chunk) ⇒ Object
Decode a chunk loaded with read_one_chunk
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/roby/droby/logfile.rb', line 161 def self.decode_one_chunk(chunk) begin ::Marshal.load_with_missing_constants(chunk) rescue ArgumentError => e if e. == "marshal data too short" raise TruncatedFileError, "marshal data invalid" end raise end rescue TypeError => e case e. when /^struct Roby::Actions::Models::Action::Argument not compatible/ Roby::Actions::Models::Action.const_set( :Argument, Struct.new(:name, :doc, :required, :default) ) retry else raise e, "#{e.message}, running roby-log repair "\ "might repair the file", e.backtrace end rescue Exception => e # rubocop:disable Lint/RescueException raise e, "#{e.message}, running roby-log repair "\ "might repair the file", e.backtrace end |
.guess_version(input) ⇒ Object
Guess the log file format version for the given IO
55 56 57 58 59 60 61 62 63 |
# File 'lib/roby/droby/logfile.rb', line 55 def self.guess_version(input) input.rewind return unless (magic = input.read(Logfile::MAGIC_CODE.size)) return input.read(4)&.unpack1("L<") if magic == Logfile::MAGIC_CODE guess_version_from_marshalled_header(input) ensure input.rewind end |
.guess_version_from_marshalled_header(input) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Guess the version of older files that had a single marshalled object as header
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/roby/droby/logfile.rb', line 69 def self.guess_version_from_marshalled_header(input) input.rewind header = begin ::Marshal.load(input) # rubocop:disable Security/MarshalLoad rescue TypeError return end case header when Hash header[:log_format] when Symbol first_chunk = begin ::Marshal.load(input) # rubocop:disable Security/MarshalLoad rescue TypeError return end 0 if first_chunk.kind_of?(Array) when Array 1 if header[-2] == :cycle_end end ensure input.rewind end |
.read_one_chunk(io) ⇒ Object
Load a chunk of data from an event file. buffer, if given, must be a String object that will be used as intermediate buffer in the process
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/roby/droby/logfile.rb', line 139 def self.read_one_chunk(io) data_size = io.read(4) return unless data_size actual_pos = io.tell unless (data_size = data_size.unpack1("L<")) raise TruncatedFileError, "file truncated, expected 4 bytes at #{actual_pos}" end buffer = io.read(data_size) || "" if buffer.size < data_size raise TruncatedFileError, "truncated file, expected a chunk of size #{data_size} "\ "at #{actual_pos + 4}, but got only "\ "#{buffer ? buffer.size : '0'} bytes" end buffer end |
.read_prologue(io) ⇒ Object
Read a file prologue, validating that it is of the latest file format
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/roby/droby/logfile.rb', line 99 def self.read_prologue(io) magic = io.read(MAGIC_CODE.size) if magic != MAGIC_CODE raise InvalidFileError, "no magic code at beginning of file" end unless (log_format = io.read(4)&.unpack1("L<")) raise InvalidFileError, "file truncated, cannot read prologue" end validate_format(log_format) rescue InvalidFormatVersion raise rescue StandardError raise unless (format = guess_version(io)) validate_format(format) end |
.validate_format(format) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Validate the given format version
123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/roby/droby/logfile.rb', line 123 def self.validate_format(format) if format < FORMAT_VERSION raise InvalidFormatVersion, "this is an outdated format (#{format}, current is "\ "#{FORMAT_VERSION}). Please run roby-log upgrade-format" elsif format > FORMAT_VERSION raise InvalidFormatVersion, "this is an unknown format version #{format}: "\ "expected #{FORMAT_VERSION}. This file can be read "\ "only by newer versions of Roby" end end |
.write_entry(io, chunk) ⇒ Object
Write a single entry in the log file
188 189 190 191 |
# File 'lib/roby/droby/logfile.rb', line 188 def self.write_entry(io, chunk) io.write([chunk.size].pack("L<")) io.write chunk end |
.write_header(io, version: FORMAT_VERSION, **options) ⇒ Object
Write a log file header
The created log file will always have FORMAT_VERSION as its version field
47 48 49 50 51 52 |
# File 'lib/roby/droby/logfile.rb', line 47 def self.write_header(io, version: FORMAT_VERSION, **) write_prologue(io, version: version) = ::Marshal.dump() io.write [.size].pack("L<") io.write end |
.write_prologue(io, version: FORMAT_VERSION) ⇒ Object
Write the file’s prologue, specifying the file magic and format version
The version ID can be specified here mostly for testing purposes.
38 39 40 41 |
# File 'lib/roby/droby/logfile.rb', line 38 def self.write_prologue(io, version: FORMAT_VERSION) io.write(MAGIC_CODE) io.write([version].pack("L<")) end |