Class: Sapience::Log

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

Overview

rubocop:disable LineLength

Constant Summary collapse

MAX_EXCEPTIONS_TO_UNWRAP =
5
MILLISECONDS_IN_SECOND =
1_000
MILLISECONDS_IN_MINUTE =
60_000
MILLISECONDS_IN_HOUR =
3_600_000
MILLISECONDS_IN_DAY =
86_400_000
CALLER_REGEXP =
/^(.*):(\d+).*/

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#backtraceObject

Returns the value of attribute backtrace



48
49
50
# File 'lib/sapience/log.rb', line 48

def backtrace
  @backtrace
end

#durationObject

Returns the value of attribute duration



48
49
50
# File 'lib/sapience/log.rb', line 48

def duration
  @duration
end

#exceptionObject

Returns the value of attribute exception



48
49
50
# File 'lib/sapience/log.rb', line 48

def exception
  @exception
end

#levelObject

Returns the value of attribute level



48
49
50
# File 'lib/sapience/log.rb', line 48

def level
  @level
end

#level_indexObject

Returns the value of attribute level_index



48
49
50
# File 'lib/sapience/log.rb', line 48

def level_index
  @level_index
end

#messageObject

Returns the value of attribute message



48
49
50
# File 'lib/sapience/log.rb', line 48

def message
  @message
end

#metricObject

Returns the value of attribute metric



48
49
50
# File 'lib/sapience/log.rb', line 48

def metric
  @metric
end

#metric_amountObject

Returns the value of attribute metric_amount



48
49
50
# File 'lib/sapience/log.rb', line 48

def metric_amount
  @metric_amount
end

#nameObject

Returns the value of attribute name



48
49
50
# File 'lib/sapience/log.rb', line 48

def name
  @name
end

#payloadObject

This filtering is specifically designed for Rack-based payloads which may have sensitive information such as “password” or “credit_card” in its hash. We need to obfuscate these fields.



48
49
50
# File 'lib/sapience/log.rb', line 48

def payload
  @payload
end

#tagsObject

Returns the value of attribute tags



48
49
50
# File 'lib/sapience/log.rb', line 48

def tags
  @tags
end

#thread_nameObject

Returns the value of attribute thread_name



48
49
50
# File 'lib/sapience/log.rb', line 48

def thread_name
  @thread_name
end

#timeObject

Returns the value of attribute time



48
49
50
# File 'lib/sapience/log.rb', line 48

def time
  @time
end

Instance Method Details

#backtrace_to_sObject

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



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/sapience/log.rb', line 56

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



133
134
135
# File 'lib/sapience/log.rb', line 133

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

#duration_humanObject

Returns [String] the duration in human readable form



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/sapience/log.rb', line 76

def duration_human # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity
  return nil unless duration
  days, ms    = duration.divmod(MILLISECONDS_IN_DAY)
  hours, ms   = ms.divmod(MILLISECONDS_IN_HOUR)
  minutes, ms = ms.divmod(MILLISECONDS_IN_MINUTE)
  seconds, ms = ms.divmod(MILLISECONDS_IN_SECOND)

  str = ""
  str << "#{days}d" if days > 0
  str << " #{hours}h" if hours > 0
  str << " #{minutes}m" if minutes > 0
  str << " #{seconds}s" if seconds > 0
  str << " #{ms}ms" if ms > 0

  if days > 0 || hours > 0 || minutes > 0
    str.strip
  else
    if seconds >= 1.0
      format "%.3fs", duration / MILLISECONDS_IN_SECOND.to_f
    else
      duration_to_s
    end
  end
end

#duration_to_sObject

Returns [String] duration of the log entry as a string Returns nil if their is no duration



70
71
72
73
# File 'lib/sapience/log.rb', line 70

def duration_to_s
  return unless duration
  format((duration < 10.0 ? "%.3fms" : "%.1fms"), duration)
end

#extract_file_and_line(stack, short_name = false) ⇒ Object

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



119
120
121
122
# File 'lib/sapience/log.rb', line 119

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



126
127
128
129
130
# File 'lib/sapience/log.rb', line 126

def file_name_and_line(short_name = false) # rubocop:disable CyclomaticComplexity
  return unless backtrace || (exception && exception.backtrace)
  stack = backtrace || exception.backtrace
  extract_file_and_line(stack, short_name) if stack && stack.size > 0
end

#formatted_timeObject

Return the Time as a formatted string Ruby MRI supports micro seconds DEPRECATED



169
170
171
# File 'lib/sapience/log.rb', line 169

def formatted_time
  format("#{time.strftime("%Y-%m-%d %H:%M:%S")}.%06d", time.usec)
end

#level_to_sObject

Returns [String] single character upper case log level



102
103
104
# File 'lib/sapience/log.rb', line 102

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

#payload?Boolean

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



162
163
164
# File 'lib/sapience/log.rb', line 162

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

#payload_to_sObject

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



139
140
141
# File 'lib/sapience/log.rb', line 139

def payload_to_s
  payload.inspect if payload?
end

#process_info(thread_name_length = 30) ⇒ Object

Returns [String] the available process info Example:

18934:thread 23 test_logging.rb:51


109
110
111
112
113
114
# File 'lib/sapience/log.rb', line 109

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

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

#to_h(host = Sapience.config.host, app_name = Sapience.app_name) ⇒ Object

Returns [Hash] representation of this log entry



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/sapience/log.rb', line 174

def to_h(host = Sapience.config.host, app_name = Sapience.app_name) # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity, LineLength
  # Header
  h               = {
    name:        name,
    pid:         $PROCESS_ID,
    thread:      thread_name,
    time:        time,
    level:       level,
    level_index: level_index,
  }
  h[:host]     = host if host
  h[:app_name] = app_name if app_name
  file, line   = file_name_and_line
  if file
    h[:file] = file
    h[:line] = line.to_i
  end

  # Tags
  h[:tags] = tags if tags && (tags.size > 0)

  # Duration
  if duration
    h[:duration_ms] = duration
    h[:duration]    = duration_human
  end

  # Log message
  h[:message] = cleansed_message if message

  # Payload
  if payload
    if payload.is_a?(Hash)
      h.merge!(payload)
    else
      h[:payload] = payload
    end
  end

  # Exceptions
  if exception
    root = h
    each_exception do |exception, i|
      name       = i == 0 ? :exception : :cause
      root[name] = {
        name:        exception.class.name,
        message:     exception.message,
        stack_trace: exception.backtrace,
      }
      root = root[name]
    end
  end

  # Metric
  h[:metric] = metric if metric
  h
end