Class: SemanticLogger::Log

Inherits:
Object
  • Object
show all
Defined in:
lib/semantic_logger/log.rb

Overview

Log

Class to hold all log entry information

level

Log level of the supplied log call
:trace, :debug, :info, :warn, :error, :fatal

thread_name

Name of the thread in which the logging call was called

name

Class name supplied to the logging instance

message

Text message to be logged

payload

Optional Hash or Ruby Exception object to be logged

time

The time at which the log entry was created

duration

The time taken to complete a measure call

tags

Any tags active on the thread when the log call was made

level_index

Internal index of the log level

exception

Ruby Exception object to log

metric [Object]

Object supplied when measure_x was called

backtrace [Array<String>]

The backtrace captured at source when the log level >= SemanticLogger.backtrace_level

metric_amount [Numeric]

Used for numeric or counter metrics.
For example, the number of inquiries or, the amount purchased etc.

context [Hash]

Named contexts that were captured when the log entry was created.

Defined Under Namespace

Classes: DeprecatedLogger

Constant Summary collapse

MAX_EXCEPTIONS_TO_UNWRAP =
5
CALLER_REGEXP =
/^(.*):(\d+).*/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, level, index = nil) ⇒ Log

Returns a new instance of Log.



52
53
54
55
56
57
58
59
60
# File 'lib/semantic_logger/log.rb', line 52

def initialize(name, level, index = nil)
  @level       = level
  @thread_name = Thread.current.name
  @name        = name
  @time        = Time.now
  @tags        = SemanticLogger.tags
  @named_tags  = SemanticLogger.named_tags
  @level_index = index.nil? ? SemanticLogger.level_to_index(level) : index
end

Instance Attribute Details

#backtraceObject

Returns the value of attribute backtrace.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def backtrace
  @backtrace
end

#contextObject

Returns the value of attribute context.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def context
  @context
end

#durationObject

Returns the value of attribute duration.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def duration
  @duration
end

#exceptionObject

Returns the value of attribute exception.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def exception
  @exception
end

#levelObject

Returns the value of attribute level.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def level
  @level
end

#level_indexObject

Returns the value of attribute level_index.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def level_index
  @level_index
end

#messageObject

Returns the value of attribute message.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def message
  @message
end

#metricObject

Returns the value of attribute metric.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def metric
  @metric
end

#metric_amountObject

Returns the value of attribute metric_amount.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def metric_amount
  @metric_amount
end

#nameObject

Returns the value of attribute name.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def name
  @name
end

#named_tagsObject

Returns the value of attribute named_tags.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def named_tags
  @named_tags
end

#payloadObject

Returns the value of attribute payload.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def payload
  @payload
end

#tagsObject

Returns the value of attribute tags.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def tags
  @tags
end

#thread_nameObject

Returns the value of attribute thread_name.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def thread_name
  @thread_name
end

#timeObject

Returns the value of attribute time.



50
51
52
# File 'lib/semantic_logger/log.rb', line 50

def time
  @time
end

Instance Method Details

#assign(message: nil, payload: nil, min_duration: 0.0, exception: nil, metric: nil, metric_amount: 1, duration: nil, backtrace: nil, log_exception: :full, on_exception_level: nil) ⇒ Object

Assign named arguments to this log entry, supplying defaults where applicable

Returns [true|false] whether this log entry should be logged

Example:

logger.info(name: 'value')


68
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/semantic_logger/log.rb', line 68

def assign(message: nil, payload: nil, min_duration: 0.0, exception: nil, metric: nil, metric_amount: 1, duration: nil, backtrace: nil, log_exception: :full, on_exception_level: nil)
  # Elastic logging: Log when :duration exceeds :min_duration
  # Except if there is an exception when it will always be logged
  if duration
    self.duration = duration
    return false if (duration < min_duration) && exception.nil?
  end

  self.message = message
  self.payload = payload

  if exception
    case log_exception
    when :full
      self.exception = exception
    when :partial
      self.message = "#{message} -- Exception: #{exception.class}: #{exception.message}"
    when nil, :none
      # Log the message without the exception that was raised
    else
      raise(ArgumentError, "Invalid value:#{log_exception.inspect} for argument :log_exception")
    end
    # On exception change the log level
    if on_exception_level
      self.level       = on_exception_level
      self.level_index = SemanticLogger.level_to_index(level)
    end
  end

  if backtrace
    self.backtrace = self.class.cleanse_backtrace(backtrace)
  elsif level_index >= SemanticLogger.backtrace_level_index
    self.backtrace = self.class.cleanse_backtrace
  end

  if metric
    self.metric        = metric
    self.metric_amount = metric_amount
  end

  self.payload = payload if payload && (payload.size > 0)
  true
end

#assign_positional(message = nil, payload = nil, exception = nil) ⇒ Object

Assign positional arguments to this log entry, supplying defaults where applicable

Returns [true|false] whether this log entry should be logged

Example:

logger.info('value', :debug, 0, "hello world")


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/semantic_logger/log.rb', line 118

def assign_positional(message = nil, payload = nil, exception = nil)
  # Exception being logged?
  # Under JRuby a java exception is not a Ruby Exception
  #   Java::JavaLang::ClassCastException.new.is_a?(Exception) => false
  if exception.nil? && payload.nil? && message.respond_to?(:backtrace) && message.respond_to?(:message)
    exception = message
    message   = nil
  elsif exception.nil? && payload && payload.respond_to?(:backtrace) && payload.respond_to?(:message)
    exception = payload
    payload   = nil
  end

  # Add result of block as message or payload if not nil
  if block_given? && (result = yield)
    if result.is_a?(String)
      message = message.nil? ? result : "#{message} -- #{result}"
      assign(message: message, payload: payload, exception: exception)
    elsif message.nil? && result.is_a?(Hash)
      assign(result)
    elsif payload && payload.respond_to?(:merge)
      assign(message: message, payload: payload.merge(result), exception: exception)
    else
      assign(message: message, payload: result, exception: exception)
    end
  else
    assign(message: message, payload: payload, exception: exception)
  end
end

#backtrace_to_sObject

Returns [String] the exception backtrace including all of the child / caused by exceptions



171
172
173
174
175
176
177
178
179
180
181
# File 'lib/semantic_logger/log.rb', line 171

def backtrace_to_s
  trace = ''
  each_exception do |exception, i|
    if i == 0
      trace = (exception.backtrace || []).join("\n")
    else
      trace << "\nCause: #{exception.class.name}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
    end
  end
  trace
end

#cleansed_messageObject

Strip the standard Rails colorizing from the logged message



247
248
249
# File 'lib/semantic_logger/log.rb', line 247

def cleansed_message
  message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, '').strip
end

#duration_humanObject

Returns [String] the duration in human readable form



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/semantic_logger/log.rb', line 198

def duration_human
  return nil unless duration
  seconds = duration / 1000
  if seconds >= 86400.0 # 1 day
    "#{(seconds / 86400).to_i}d #{Time.at(seconds).strftime('%-Hh %-Mm')}"
  elsif seconds >= 3600.0 # 1 hour
    Time.at(seconds).strftime('%-Hh %-Mm')
  elsif seconds >= 60.0 # 1 minute
    Time.at(seconds).strftime('%-Mm %-Ss')
  elsif seconds >= 1.0 # 1 second
    "#{'%.3f' % seconds}s"
  else
    duration_to_s
  end
end

#duration_to_sObject



187
188
189
# File 'lib/semantic_logger/log.rb', line 187

def duration_to_s
  "#{duration.to_i}ms" if duration
end

#each_exceptionObject

Call the block for exception and any nested exception



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/semantic_logger/log.rb', line 149

def each_exception
  # With thanks to https://github.com/bugsnag/bugsnag-ruby/blob/6348306e44323eee347896843d16c690cd7c4362/lib/bugsnag/notification.rb#L81
  depth      = 0
  exceptions = []
  ex         = exception
  while ex != nil && !exceptions.include?(ex) && exceptions.length < MAX_EXCEPTIONS_TO_UNWRAP
    exceptions << ex
    yield(ex, depth)

    depth += 1
    ex    =
      if ex.respond_to?(:cause) && ex.cause
        ex.cause
      elsif ex.respond_to?(:continued_exception) && ex.continued_exception
        ex.continued_exception
      elsif ex.respond_to?(:original_exception) && ex.original_exception
        ex.original_exception
      end
  end
end

#extract_file_and_line(stack, short_name = false) ⇒ Object

Extract the filename and line number from the last entry in the supplied backtrace



232
233
234
235
# File 'lib/semantic_logger/log.rb', line 232

def extract_file_and_line(stack, short_name = false)
  match = CALLER_REGEXP.match(stack.first)
  [short_name ? File.basename(match[1]) : match[1], match[2].to_i]
end

#file_name_and_line(short_name = false) ⇒ Object

Returns [String, String] the file_name and line_number from the backtrace supplied in either the backtrace or exception



239
240
241
242
243
244
# File 'lib/semantic_logger/log.rb', line 239

def file_name_and_line(short_name = false)
  if backtrace || (exception && exception.backtrace)
    stack = backtrace || exception.backtrace
    extract_file_and_line(stack, short_name) if stack && stack.size > 0
  end
end

#formatted_timeObject

DEPRECATED



263
264
265
# File 'lib/semantic_logger/log.rb', line 263

def formatted_time
  time.strftime(Formatters::Base::TIME_FORMAT)
end

#has_payload?Boolean

Returns [true|false] whether the log entry has a payload

Returns:

  • (Boolean)


258
259
260
# File 'lib/semantic_logger/log.rb', line 258

def has_payload?
  !(payload.nil? || (payload.respond_to?(:empty?) && payload.empty?))
end

#level_to_sObject

Returns [String] single character upper case log level



215
216
217
# File 'lib/semantic_logger/log.rb', line 215

def level_to_s
  level.to_s[0..0].upcase
end

#payload_to_sObject

Return the payload in text form Returns nil if payload is missing or empty



253
254
255
# File 'lib/semantic_logger/log.rb', line 253

def payload_to_s
  payload.inspect if has_payload?
end

#process_info(thread_name_length = 30) ⇒ Object

Returns [String] the available process info Example:

18934:thread 23 test_logging.rb:51


222
223
224
225
226
227
# File 'lib/semantic_logger/log.rb', line 222

def process_info(thread_name_length = 30)
  file, line = file_name_and_line(true)
  file_name  = " #{file}:#{line}" if file

  "#{$$}:#{"%.#{thread_name_length}s" % thread_name}#{file_name}"
end

#set_context(key, value) ⇒ Object

Lazy initializes the context hash and assigns a key value pair.



276
277
278
# File 'lib/semantic_logger/log.rb', line 276

def set_context(key, value)
  (self.context ||= {})[key] = value
end

#to_h(host = SemanticLogger.host, application = SemanticLogger.application) ⇒ Object

DEPRECATED: Use SemanticLogger::Formatters::Raw



270
271
272
273
# File 'lib/semantic_logger/log.rb', line 270

def to_h(host = SemanticLogger.host, application = SemanticLogger.application)
  logger = DeprecatedLogger.new(host, application)
  SemanticLogger::Formatters::Raw.new.call(self, logger)
end