Class: Rage::Logger

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

Overview

All logs in rage consist of two parts: keys and tags. A sample log entry might look like this:

[fecbba0735355738] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info message=hello

In the log entry above, timestamp, pid, level, and message are keys, while fecbba0735355738 is a tag.

Use #tagged to add custom tags to an entry:

Rage.logger.tagged("ApiCall") do
  perform_api_call
  Rage.logger.info "success"
end
# => [fecbba0735355738][ApiCall] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info message=success

#with_context can be used to add custom keys:

cache_key = "mykey"
Rage.logger.with_context(cache_key: cache_key) do
  get_from_cache(cache_key)
  Rage.logger.info "cache miss"
end
# => [fecbba0735355738] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info cache_key=mykey message=cache miss

Rage::Logger also implements the interface of Ruby's native Logger:

Rage.logger.info("Initializing")
Rage.logger.debug { "This is a " + potentially + " expensive operation" }

Using the logger

The recommended approach to logging with Rage is to make sure your code always logs the same message no matter what the input is. You can achieve this by using the #with_context and #tagged methods. So, a code like this:

def process_purchase(user_id:, product_id:)
  Rage.logger.info "processing purchase with user_id = #{user_id}; product_id = #{product_id}"
end

turns into this:

def process_purchase(user_id:, product_id:)
  Rage.logger.with_context(user_id: user_id, product_id: product_id) do
    Rage.logger.info "processing purchase"
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(log, level: Logger::DEBUG, formatter: Rage::TextFormatter.new, shift_age: 0, shift_size: 104857600, shift_period_suffix: "%Y%m%d", binmode: false) ⇒ Logger

Create a new logger.

Parameters:

  • log (Object)

    a filename (String), IO object (typically STDOUT, STDERR, or an open file), nil (it writes nothing) or File::NULL (same as nil)

  • level (Integer) (defaults to: Logger::DEBUG)

    logging severity threshold

  • formatter (#call) (defaults to: Rage::TextFormatter.new)

    logging formatter

  • shift_age (Integer, String) (defaults to: 0)

    number of old log files to keep, or frequency of rotation ("daily", "weekly" or "monthly"). Default value is 0, which disables log file rotation

  • shift_size (Integer) (defaults to: 104857600)

    maximum log file size in bytes (only applies when shift_age is a positive Integer)

  • shift_period_suffix (String) (defaults to: "%Y%m%d")

    the log file suffix format for daily, weekly or monthly rotation

  • binmode (defaults to: false)

    sets whether the logger writes in binary mode



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

def initialize(log, level: Logger::DEBUG, formatter: Rage::TextFormatter.new, shift_age: 0, shift_size: 104857600, shift_period_suffix: "%Y%m%d", binmode: false)
  @logdev = if log && log != File::NULL
    Logger::LogDevice.new(log, shift_age:, shift_size:, shift_period_suffix:, binmode:)
  end

  if Rage.env.development? && log.respond_to?(:sync=)
    log.sync = true
  end

  @formatter = formatter
  @level = @logdev ? level : Logger::UNKNOWN
  define_log_methods
end

Instance Attribute Details

#formatterObject

Returns the value of attribute formatter.



64
65
66
# File 'lib/rage/logger/logger.rb', line 64

def formatter
  @formatter
end

#levelObject

Returns the value of attribute level.



64
65
66
# File 'lib/rage/logger/logger.rb', line 64

def level
  @level
end

Instance Method Details

#debug?Boolean

Returns:

  • (Boolean)


136
# File 'lib/rage/logger/logger.rb', line 136

def debug? = @level <= Logger::DEBUG

#error?Boolean

Returns:

  • (Boolean)


137
# File 'lib/rage/logger/logger.rb', line 137

def error? = @level <= Logger::ERROR

#fatal?Boolean

Returns:

  • (Boolean)


138
# File 'lib/rage/logger/logger.rb', line 138

def fatal? = @level <= Logger::FATAL

#info?Boolean

Returns:

  • (Boolean)


139
# File 'lib/rage/logger/logger.rb', line 139

def info? = @level <= Logger::INFO

#tagged(tag) ⇒ Object Also known as: with_tag

Add a custom tag to an entry.

Examples:

Rage.logger.tagged("ApiCall") do
  Rage.logger.info "success"
end

Parameters:

  • tag (String)

    the tag to add to an entry



127
128
129
130
131
132
# File 'lib/rage/logger/logger.rb', line 127

def tagged(tag)
  (Thread.current[:rage_logger] ||= { tags: [], context: {} })[:tags] << tag
  yield(self)
ensure
  Thread.current[:rage_logger][:tags].pop
end

#warn?Boolean

Returns:

  • (Boolean)


140
# File 'lib/rage/logger/logger.rb', line 140

def warn? = @level <= Logger::WARN

#with_context(context) ⇒ Object

Add custom keys to an entry.

Examples:

Rage.logger.with_context(key: "mykey") do
  Rage.logger.info "cache miss"
end

Parameters:

  • context (Hash)

    a hash of custom keys



106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/rage/logger/logger.rb', line 106

def with_context(context)
  old_context = (Thread.current[:rage_logger] ||= { tags: [], context: {} })[:context]

  if old_context.empty? # there's nothing in the context yet
    Thread.current[:rage_logger][:context] = context
  else # it's not the first `with_context` call in the chain
    Thread.current[:rage_logger][:context] = old_context.merge(context)
  end

  yield(self)
ensure
  Thread.current[:rage_logger][:context] = old_context
end