Module: HybridPlatformsConductor::LoggerHelpers

Overview

Gives easy logging methods to any class including this module, such as log_error, log_debug… Also define methods for UI (meaning text that should be displayed as interface, and not as logging).

Defined Under Namespace

Classes: ProgressBarLogDevice

Constant Summary collapse

LEVELS_MODIFIERS =

Sorted list of levels and their corresponding modifiers.

{
  fatal: i[red bold],
  error: i[red bold],
  warn: i[yellow bold],
  info: [:white],
  debug: [:white],
  unknown: [:white]
}
LEVELS_TO_STDERR =

List of levels that will output on stderr

i[warn error fatal]

Class Attribute Summary collapse

Instance Method Summary collapse

Class Attribute Details

.progress_bar_semaphoreObject (readonly)

Returns the value of attribute progress_bar_semaphore.



85
86
87
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 85

def progress_bar_semaphore
  @progress_bar_semaphore
end

Instance Method Details

#err(message = '') ⇒ Object

Print an error string to the command-line UI. This is different from logging because this is the UI of the CLI. Use sections indentation for better clarity.

Parameters
  • message (String): Message to be printed out [default = ”]



192
193
194
195
196
197
198
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 192

def err(message = '')
  @out_sections = [] unless defined?(@out_sections)
  message = "#{'  ' * @out_sections.size}#{message}"
  # log_debug "<Output> - #{message}"
  message = "#{message}\n" unless message.end_with?("\n")
  @logger_stderr << message
end

#init_loggers(logger, logger_stderr) ⇒ Object

Initialize loggers

Parameters
  • logger (Logger): Logger used for stdout

  • logger_stderr (Logger): Logger used for stderr



124
125
126
127
128
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 124

def init_loggers(logger, logger_stderr)
  @logger = logger
  @logger_stderr = logger_stderr
  set_loggers_format
end

#log_component=(component) ⇒ Object

Set the logging component name, to be prepend in any log message, or nil if none. By default the component is the class name.

Parameters
  • component (String or nil): Logging component, or nil for none



168
169
170
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 168

def log_component=(component)
  @log_component = component
end

#log_debug?Boolean

Are we in debug level?

Result
  • Boolean: Are we in debug level?

Returns:

  • (Boolean)


159
160
161
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 159

def log_debug?
  @logger.debug?
end

#log_level=(level) ⇒ Object

Set log level

Parameters
  • level (Symbol): Log level (used directly with the logger)



151
152
153
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 151

def log_level=(level)
  @logger.level = level
end

#out(message = '') ⇒ Object

Print a string to the command-line UI. This is different from logging because this is the UI of the CLI. Use sections indentation for better clarity.

Parameters
  • message (String): Message to be printed out [default = ”]



178
179
180
181
182
183
184
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 178

def out(message = '')
  @out_sections = [] unless defined?(@out_sections)
  message = "#{'  ' * @out_sections.size}#{message}"
  # log_debug "<Output> - #{message}"
  message = "#{message}\n" unless message.end_with?("\n")
  @logger << message
end

#section(name) ⇒ Object

Display a new section in the UI, used to group a set of operations

Parameters
  • name (String): Section name

  • Proc: Code called in the section



205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 205

def section(name)
  out "===== #{name} ==== Begin..."
  @out_sections = [] unless defined?(@out_sections)
  @out_sections << name
  begin
    yield
  ensure
    @out_sections.pop
    out "===== #{name} ==== ...End"
    out
  end
end

#set_loggers_formatObject

Set loggers to the desired format



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 131

def set_loggers_format
  [@logger, @logger_stderr].each do |logger|
    logger.formatter = proc do |severity, _datetime, progname, msg|
      # If the message already has control characters, don't colorize it
      keep_original_color = msg.include? "\u001b"
      message = "[#{Time.now.utc.strftime('%F %T')} (PID #{$PROCESS_ID} / TID #{Thread.current.object_id})] #{severity.rjust(5)} - [ #{progname} ] - "
      message << "#{msg}\n" unless keep_original_color
      LEVELS_MODIFIERS[severity.downcase.to_sym].each do |modifier|
        message = message.send(modifier)
      end
      message << "#{msg}\n" if keep_original_color
      message
    end
  end
end

#stderr_deviceObject

Get the stderr device

Result
  • IO or String: The stdout IO or file name



240
241
242
243
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 240

def stderr_device
  # TODO: Find a more elegant way to access the internal log device
  @logger_stderr.instance_variable_get(:@logdev).dev
end

#stderr_device=(log_device) ⇒ Object

Set the stderr device

Parameters
  • log_device (Object): The stdout log device to set



249
250
251
252
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 249

def stderr_device=(log_device)
  # TODO: Find a more elegant way to access the internal log device
  @logger_stderr.instance_variable_get(:@logdev).send(:set_dev, log_device)
end

#stderr_displayed?Boolean

Is stderr really getting to the terminal display?

Result
  • Boolean: Is stderr really getting to the terminal stdout?

Returns:

  • (Boolean)


267
268
269
270
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 267

def stderr_displayed?
  log_device = stderr_device
  log_device == $stderr || log_device == $stdout || log_device.is_a?(ProgressBarLogDevice)
end

#stdout_deviceObject

Get the stdout device

Result
  • Object: The stdout log device



222
223
224
225
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 222

def stdout_device
  # TODO: Find a more elegant way to access the internal log device
  @logger.instance_variable_get(:@logdev).dev
end

#stdout_device=(log_device) ⇒ Object

Set the stdout device

Parameters
  • log_device (Object): The stdout log device to set



231
232
233
234
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 231

def stdout_device=(log_device)
  # TODO: Find a more elegant way to access the internal log device
  @logger.instance_variable_get(:@logdev).send(:set_dev, log_device)
end

#stdout_displayed?Boolean

Is stdout really getting to the terminal display?

Result
  • Boolean: Is stdout really getting to the terminal stdout?

Returns:

  • (Boolean)


258
259
260
261
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 258

def stdout_displayed?
  log_device = stdout_device
  log_device == $stdout || log_device == $stderr || log_device.is_a?(ProgressBarLogDevice)
end

#stdouts_to_sObject

Return a string describing the stdout and stderr if they were logged into files or StringIO. Useful for debugging.

Result
  • String: The corresponding stdout and stderr info, or nil if none



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 323

def stdouts_to_s
  messages = []
  {
    'STDOUT' => stdout_device,
    'STDERR' => stderr_device
  }.each do |name, device|
    case device
    when File
      if File.exist?(device.path)
        content = File.read(device.path).strip
        messages << "----- #{name} BEGIN - #{device.path} -----\n#{content}\n----- #{name} END - #{device.path} -----" unless content.empty?
      end
    when StringIO
      content = device.string
      messages << "----- #{name} BEGIN -----\n#{content}\n----- #{name} END -----" unless content.empty?
    end
  end
  messages.empty? ? nil : messages.join("\n")
end

#with_progress_bar(nbr_total, name: nil) ⇒ Object

Create a UI progress bar and call some code with it. Ensure logging done with the progress bar does not conflict in stdout.

Parameters
  • nbr_total (Integer): Total value of the progress bar

  • name (String or nil): Name to put on the progress bar, or nil for no name [default: nil]

  • Proc: Code block called with the progress bar

    • Parameters
      • progress_bar (ProgressBar): The progress bar



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/hybrid_platforms_conductor/logger_helpers.rb', line 281

def with_progress_bar(nbr_total, name: nil)
  previous_stdout_device = nil
  previous_stderr_device = nil
  progress_bar = nil
  LoggerHelpers.progress_bar_semaphore.synchronize do
    stdout_log_device = stdout_device
    progress_bar = ProgressBar.create(
      output: stdout_log_device.is_a?(ProgressBarLogDevice) ? $stdout : stdout_log_device,
      title: 'Initializing...',
      total: nbr_total,
      format: "#{name ? "#{name} " : ''}[%j%%] - |%bC%i| - [ %t ]",
      progress_mark: ' ',
      remainder_mark: '-'
    )
    if stdout_displayed? && !stdout_log_device.is_a?(ProgressBarLogDevice)
      # Change the current logger so that when its logdev calls write it redirects to our ProgressBar#log
      previous_stdout_device = stdout_device
      self.stdout_device = ProgressBarLogDevice.new(progress_bar, previous_stdout_device)
    end
    if stderr_displayed? && !stderr_device.is_a?(ProgressBarLogDevice)
      # Change the current logger so that when its logdev calls write it redirects to our ProgressBar#log
      previous_stderr_device = stderr_device
      self.stderr_device = ProgressBarLogDevice.new(progress_bar, previous_stderr_device)
    end
  end
  begin
    yield progress_bar
  ensure
    LoggerHelpers.progress_bar_semaphore.synchronize do
      stdout_device.flush
      stderr_device.flush
      self.stdout_device = previous_stdout_device unless previous_stdout_device.nil?
      self.stderr_device = previous_stderr_device unless previous_stderr_device.nil?
    end
  end
end