Module: Tempest::DebugLog
- Defined in:
- lib/tempest/debug_log.rb
Overview
Structured diagnostic logging for tempest.
‘Tempest::DebugLog.build` returns a `Channel` that fans messages out to one or more underlying `::Logger` instances. The format is logfmt-flavored single-line:
2026-05-18T01:23:45+09:00 level=warn module=watchdog event=stalled_detected elapsed_seconds=612.3 threshold_seconds=600
The fixed leading keys (‘level=`, `module=`, `event=`) are produced by the formatter from the level + progname + first-positional arguments, so call sites just write the variable fields as keyword arguments:
@logger.warn("watchdog", event: "stalled_detected", elapsed_seconds: 612.3, threshold_seconds: 600)
Output destinations:
* `info.log` — INFO and above, always written when logging is enabled.
* `debug.log` — DEBUG and above, written only when `--debug` (or the
equivalent flag passed to `build(debug: true)`) is on.
Default base directory is ‘$XDG_STATE_HOME/tempest` (falling back to `~/.local/state/tempest`). Override via `TEMPEST_LOG_DIR=/path` for the whole tree, or set `TEMPEST_NO_LOG=1` to disable both files entirely (used by tests). The legacy `TEMPEST_DEBUG_LOG=/path/to/file` env var still works and routes everything (DEBUG and above) to a single file regardless of the other settings.
All file destinations use size-based rotation (5 MiB x 5 files) so a long-running session can’t fill the disk.
Defined Under Namespace
Classes: Channel
Constant Summary collapse
- LEVELS =
{ "DEBUG" => Logger::DEBUG, "INFO" => Logger::INFO, "WARN" => Logger::WARN, "ERROR" => Logger::ERROR, "FATAL" => Logger::FATAL, }.freeze
- DEFAULT_ROTATION_COUNT =
5- DEFAULT_ROTATION_SIZE =
5 * 1024 * 1024
Class Method Summary collapse
- .build(env:, debug: false) ⇒ Object
- .build_file_logger(path, level:) ⇒ Object
- .encode_value(value) ⇒ Object
- .formatter ⇒ Object
- .log_dir(env) ⇒ Object
- .null_channel ⇒ Object
- .resolve_level(value) ⇒ Object
Class Method Details
.build(env:, debug: false) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/tempest/debug_log.rb', line 49 def build(env:, debug: false) loggers = [] legacy = env["TEMPEST_DEBUG_LOG"] if legacy && !legacy.empty? loggers << build_file_logger(legacy, level: resolve_level(env["TEMPEST_DEBUG_LOG_LEVEL"]) || Logger::DEBUG) end unless env["TEMPEST_NO_LOG"] == "1" dir = log_dir(env) loggers << build_file_logger(File.join(dir, "info.log"), level: Logger::INFO) loggers << build_file_logger(File.join(dir, "debug.log"), level: Logger::DEBUG) if debug end Channel.new(loggers: loggers) end |
.build_file_logger(path, level:) ⇒ Object
98 99 100 101 102 103 104 105 |
# File 'lib/tempest/debug_log.rb', line 98 def build_file_logger(path, level:) path = File.(path) FileUtils.mkdir_p(File.dirname(path)) logger = Logger.new(path, DEFAULT_ROTATION_COUNT, DEFAULT_ROTATION_SIZE) logger.level = level logger.formatter = formatter logger end |
.encode_value(value) ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/tempest/debug_log.rb', line 107 def encode_value(value) case value when nil "nil" when true, false, Integer, Float, Symbol value.to_s when Time value.iso8601 else s = value.to_s if s.empty? '""' elsif s.match?(/[\s"=]/) '"' + s.gsub('\\', '\\\\\\\\').gsub('"', '\\"') + '"' else s end end end |
.formatter ⇒ Object
70 71 72 73 74 75 76 77 78 |
# File 'lib/tempest/debug_log.rb', line 70 def formatter proc do |severity, time, progname, msg| parts = [] parts << "level=#{severity.downcase}" parts << "module=#{progname}" if progname && !progname.to_s.empty? parts << msg if msg && !msg.to_s.empty? "#{time.iso8601} #{parts.join(' ')}\n" end end |
.log_dir(env) ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/tempest/debug_log.rb', line 85 def log_dir(env) override = env["TEMPEST_LOG_DIR"] return override if override && !override.empty? xdg = env["XDG_STATE_HOME"] base = if xdg && !xdg.empty? xdg else File.join(env["HOME"] || Dir.home, ".local", "state") end File.join(base, "tempest") end |
.null_channel ⇒ Object
66 67 68 |
# File 'lib/tempest/debug_log.rb', line 66 def null_channel Channel.new(loggers: []) end |
.resolve_level(value) ⇒ Object
80 81 82 83 |
# File 'lib/tempest/debug_log.rb', line 80 def resolve_level(value) return nil if value.nil? || value.empty? LEVELS[value.to_s.upcase] end |