Module: Legion::Logging::Builder

Included in:
Legion::Logging, Logger
Defined in:
lib/legion/logging/builder.rb

Instance Method Summary collapse

Instance Method Details

#async?Boolean

Returns:

  • (Boolean)


147
148
149
# File 'lib/legion/logging/builder.rb', line 147

def async?
  (@async == true && @async_writer&.alive?) || false
end

#build_runner_trace(loc = caller_locations(6, 1)&.first) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/legion/logging/builder.rb', line 72

def build_runner_trace(loc = caller_locations(6, 1)&.first)
  return unless loc

  path = loc.to_s.split('/').last(2)
  {
    type:        path[0],
    file:        File.basename(loc.path, '.*'),
    function:    loc.base_label,
    line_number: loc.lineno
  }
end

#json?Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/legion/logging/builder.rb', line 17

def json?
  @format == :json
end

#json_format(include_pid: false) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/legion/logging/builder.rb', line 21

def json_format(include_pid: false)
  log.formatter = proc do |severity, datetime, _progname, msg|
    entry = {
      timestamp: datetime.utc.iso8601(3),
      level:     severity.downcase,
      message:   msg.is_a?(String) ? msg.gsub(/\e\[[0-9;]*m/, '') : msg.to_s,
      thread:    Thread.current.object_id
    }
    entry[:pid] = ::Process.pid if include_pid
    segments = Thread.current[:legion_log_segments]
    entry[:segments] = segments if segments
    method_ctx = Thread.current[:legion_log_method]
    entry[:method] = method_ctx if method_ctx
    "#{::JSON.generate(entry)}\n"
  rescue StandardError => e
    warn("Legion::Logging::Builder#json_format formatter failed: #{e.message}")
    "{\"timestamp\":\"#{datetime}\",\"level\":\"#{severity}\",\"message\":#{msg.to_s.dump}}\n"
  end
end

#levelObject



120
121
122
# File 'lib/legion/logging/builder.rb', line 120

def level
  log.level
end

#logObject



88
89
90
# File 'lib/legion/logging/builder.rb', line 88

def log
  @log ||= set_log
end

#log_format(format: :text, include_pid: false) ⇒ Object



8
9
10
11
12
13
14
15
# File 'lib/legion/logging/builder.rb', line 8

def log_format(format: :text, include_pid: false, **)
  @format = format.to_sym
  if @format == :json
    json_format(include_pid: include_pid)
  else
    text_format(include_pid: include_pid, **)
  end
end

#log_level(level = 'debug') ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/legion/logging/builder.rb', line 124

def log_level(level = 'debug')
  log.level = case level
              when 'trace', 'debug'
                ::Logger::DEBUG
              when 'info'
                ::Logger::INFO
              when 'warn'
                ::Logger::WARN
              when 'error'
                ::Logger::ERROR
              when 'fatal'
                ::Logger::FATAL
              when nil
                42
              else
                if level.is_a? Integer
                  level
                else
                  1
                end
              end
end

#output(**options) ⇒ Object



84
85
86
# File 'lib/legion/logging/builder.rb', line 84

def output(**options)
  set_log(logfile: options[:log_file], log_stdout: options[:log_stdout])
end

#prepare_log_path(path) ⇒ Object



114
115
116
117
118
# File 'lib/legion/logging/builder.rb', line 114

def prepare_log_path(path)
  expanded = File.expand_path(path)
  FileUtils.mkdir_p(File.dirname(expanded))
  expanded
end

#resolve_lex_tag(options) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/legion/logging/builder.rb', line 57

def resolve_lex_tag(options)
  segments = Thread.current[:legion_log_segments]
  tag = if segments
          segments.map { |s| "[#{s}]" }.join
        elsif options.key?(:lex_segments)
          options[:lex_segments].map { |s| "[#{s}]" }.join
        elsif options.key?(:lex) && !options[:lex].nil?
          "[#{options[:lex]}]"
        end

  method_ctx = Thread.current[:legion_log_method]
  tag = "#{tag}{#{method_ctx}}" if tag && method_ctx
  tag
end

#set_log(logfile: nil, log_stdout: nil) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/legion/logging/builder.rb', line 92

def set_log(logfile: nil, log_stdout: nil, **)
  previous_log = @log

  if logfile && log_stdout != false
    path = prepare_log_path(logfile)
    require_relative 'multi_io'
    file = File.new(path, 'a')
    file.sync = true
    io = MultiIO.new($stdout, file)
    @log = ::Logger.new(io)
  elsif logfile
    file = File.new(prepare_log_path(logfile), 'a')
    file.sync = true
    @log = ::Logger.new(file)
  else
    @log = ::Logger.new($stdout)
  end

  close_replaced_log(previous_log)
  @log
end

#start_async_writer(buffer_size: 10_000) ⇒ Object

rubocop:disable Naming/PredicateMethod



152
153
154
155
156
157
158
159
160
# File 'lib/legion/logging/builder.rb', line 152

def start_async_writer(buffer_size: 10_000)
  require_relative 'async_writer'
  return false if @async_writer&.alive? && stop_async_writer == false

  @async_writer = AsyncWriter.new(log, buffer_size: buffer_size)
  @async_writer.start
  @async = true
  true
end

#stop_async_writerObject



162
163
164
165
166
167
168
169
170
171
# File 'lib/legion/logging/builder.rb', line 162

def stop_async_writer
  writer = @async_writer
  stopped = writer&.stop
  return false if stopped == false

  close_replaced_log(writer.logger) if writer.respond_to?(:logger)
  @async_writer = nil if @async_writer.equal?(writer)
  @async = false
  true
end

#text_format(include_pid: false, **options) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/legion/logging/builder.rb', line 41

def text_format(include_pid: false, **options)
  log.formatter = proc do |severity, datetime, _progname, msg|
    lex_name = resolve_lex_tag(options)
    runner_trace = Thread.current[:legion_log_caller] || build_runner_trace if lex_name

    string = "[#{datetime}]"
    string.concat("[#{::Process.pid}]") if include_pid
    string.concat(lex_name) if lex_name
    if runner_trace.is_a?(Hash) && (options[:extended] || severity == 'debug')
      string.concat("[#{runner_trace[:type]}:#{runner_trace[:file]}:#{runner_trace[:function]}:#{runner_trace[:line_number]}]")
    end
    string.concat(" #{severity} #{msg}\n")
    string
  end
end