Class: OneApm::Logger::AgentLogger

Inherits:
Object
  • Object
show all
Defined in:
lib/one_apm/logger/agent_logger.rb

Constant Summary collapse

OA_NUM_LOG_ONCE_KEYS =
1000
OA_LOG_LEVELS =
{
  "debug" => ::Logger::DEBUG,
  "info"  => ::Logger::INFO,
  "warn"  => ::Logger::WARN,
  "error" => ::Logger::ERROR,
  "fatal" => ::Logger::FATAL,
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root = "", override_logger = nil) ⇒ AgentLogger

Returns a new instance of AgentLogger.



13
14
15
16
17
18
19
20
# File 'lib/one_apm/logger/agent_logger.rb', line 13

def initialize(root = "", override_logger=nil)
  clear_already_logged
  create_log(root, override_logger)
  set_log_level!
  set_log_format!

  gather_startup_logs
end

Instance Attribute Details

#already_loggedObject (readonly)

Returns the value of attribute already_logged.



11
12
13
# File 'lib/one_apm/logger/agent_logger.rb', line 11

def already_logged
  @already_logged
end

Class Method Details

.log_level_for(level) ⇒ Object



179
180
181
# File 'lib/one_apm/logger/agent_logger.rb', line 179

def self.log_level_for(level)
  OA_LOG_LEVELS.fetch(level.to_s.downcase, ::Logger::INFO)
end

Instance Method Details

#backtrace_from_exception(e) ⇒ Object



87
88
89
90
91
92
93
94
95
# File 'lib/one_apm/logger/agent_logger.rb', line 87

def backtrace_from_exception(e)
  # We've seen that often the backtrace on a SystemStackError is bunk
  # so massage the caller instead at a known depth.
  #
  # Tests keep us honest about minmum method depth our log calls add.
  return caller.drop(5) if e.is_a?(SystemStackError)

  e.backtrace
end

#clear_already_loggedObject



149
150
151
# File 'lib/one_apm/logger/agent_logger.rb', line 149

def clear_already_logged
  @already_logged = {}
end

#create_log(root, override_logger) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/one_apm/logger/agent_logger.rb', line 115

def create_log(root, override_logger)
  if !override_logger.nil?
    @log = override_logger
  elsif OneApm::Manager.config[:agent_enabled] == false
    create_null_logger
  else
    if wants_stdout?
      @log = ::Logger.new(STDOUT)
    else
      create_log_to_file(root)
    end
  end
end

#create_log_to_file(root) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/one_apm/logger/agent_logger.rb', line 129

def create_log_to_file(root)
  path = find_or_create_file_path(OneApm::Manager.config[:log_file_path], root)
  if path.nil?
    @log = ::Logger.new(STDOUT)
    warn("Error creating log directory #{OneApm::Manager.config[:log_file_path]}, using standard out for logging.")
  else
    file_path = "#{path}/#{OneApm::Manager.config[:log_file_name]}"
    begin
      @log = ::Logger.new(file_path)
    rescue => e
      @log = ::Logger.new(STDOUT)
      warn("Failed creating logger for file #{file_path}, using standard out for logging.", e)
    end
  end
end

#create_null_loggerObject



145
146
147
# File 'lib/one_apm/logger/agent_logger.rb', line 145

def create_null_logger
  @log = ::OneApm::Logger::NullLogger.new
end

#debug(*msgs, &blk) ⇒ Object



38
39
40
# File 'lib/one_apm/logger/agent_logger.rb', line 38

def debug(*msgs, &blk)
  format_and_send(:debug, msgs, &blk)
end

#error(*msgs, &blk) ⇒ Object



26
27
28
# File 'lib/one_apm/logger/agent_logger.rb', line 26

def error(*msgs, &blk)
  format_and_send(:error, msgs, &blk)
end

#fatal(*msgs, &blk) ⇒ Object



22
23
24
# File 'lib/one_apm/logger/agent_logger.rb', line 22

def fatal(*msgs, &blk)
  format_and_send(:fatal, msgs, &blk)
end

#find_or_create_file_path(path_setting, root) ⇒ Object



157
158
159
160
161
162
163
164
165
# File 'lib/one_apm/logger/agent_logger.rb', line 157

def find_or_create_file_path(path_setting, root)
  for abs_path in [ File.expand_path(path_setting),
                    File.expand_path(File.join(root, path_setting)) ] do
    if File.directory?(abs_path) || (Dir.mkdir(abs_path) rescue nil)
      return abs_path[%r{^(.*?)/?$}]
    end
  end
  nil
end

#format_and_send(level, *msgs, &block) ⇒ Object

Allows for passing exceptions in explicitly, which format with backtrace



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/one_apm/logger/agent_logger.rb', line 98

def format_and_send(level, *msgs, &block)
  if block
    if @log.send("#{level}?")
      msgs = Array(block.call)
    else
      msgs = []
    end
  end

  msgs.flatten.each do |item|
    case item
    when Exception then log_exception(level, item, :debug)
    else @log.send(level, item)
    end
  end
end

#gather_startup_logsObject



191
192
193
# File 'lib/one_apm/logger/agent_logger.rb', line 191

def gather_startup_logs
  StartupLogger.instance.dump(self)
end

#info(*msgs, &blk) ⇒ Object



34
35
36
# File 'lib/one_apm/logger/agent_logger.rb', line 34

def info(*msgs, &blk)
  format_and_send(:info, msgs, &blk)
end

#is_startup_logger?Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/one_apm/logger/agent_logger.rb', line 67

def is_startup_logger?
  @log.is_a?(NullLogger)
end

#log_exception(level, e, backtrace_level = level) ⇒ Object

Use this when you want to log an exception with explicit control over the log level that the backtrace is logged at. If you just want the default behavior of backtraces logged at debug, use one of the methods above and pass an Exception as one of the args.



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/one_apm/logger/agent_logger.rb', line 75

def log_exception(level, e, backtrace_level=level)
  @log.send(level, "%p: %s" % [ e.class, e.message ])
  @log.send(backtrace_level) do
    backtrace = backtrace_from_exception(e)
    if backtrace
      "Debugging backtrace:\n" + backtrace.join("\n  ")
    else
      "No backtrace available."
    end
  end
end

#log_formatter=(formatter) ⇒ Object



195
196
197
# File 'lib/one_apm/logger/agent_logger.rb', line 195

def log_formatter=(formatter)
  @log.formatter = formatter
end

#log_once(level, key, *msgs) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/one_apm/logger/agent_logger.rb', line 44

def log_once(level, key, *msgs)
  # Since `already_logged` might change between calls, just grab it once
  # and use it throughout this method.
  logged = already_logged

  return if logged.include?(key)

  if logged.size >= OA_NUM_LOG_ONCE_KEYS && key.kind_of?(String)
    # The reason for preventing too many keys in `logged` is for
    # memory concerns.
    # The reason for checking the type of the key is that we always want
    # to allow symbols to log, since there are very few of them.
    # The assumption here is that you would NEVER pass dynamically-created
    # symbols, because you would never create symbols dynamically in the
    # first place, as that would already be a memory leak in most Rubies,
    # even if we didn't hang on to them all here.
    return
  end

  logged[key] = true
  self.send(level, *msgs)
end

#set_log_format!Object



183
184
185
186
187
188
189
# File 'lib/one_apm/logger/agent_logger.rb', line 183

def set_log_format!
  @hostname = OneApm::Agent::Hostname.get
  @prefix = wants_stdout? ? '** [OneApm]' : ''
  @log.formatter = Proc.new do |severity, timestamp, progname, msg|
    "#{@prefix}[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{@hostname} (#{$$})] #{severity} : #{msg}\n"
  end
end

#set_log_level!Object



167
168
169
# File 'lib/one_apm/logger/agent_logger.rb', line 167

def set_log_level!
  @log.level = AgentLogger.log_level_for(OneApm::Manager.config[:log_level])
end

#wants_stdout?Boolean

Returns:

  • (Boolean)


153
154
155
# File 'lib/one_apm/logger/agent_logger.rb', line 153

def wants_stdout?
  OneApm::Manager.config[:log_file_path].upcase == "STDOUT"
end

#warn(*msgs, &blk) ⇒ Object



30
31
32
# File 'lib/one_apm/logger/agent_logger.rb', line 30

def warn(*msgs, &blk)
  format_and_send(:warn, msgs, &blk)
end