| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 |
- # lib/log.rb
- # Minimal structured logger for the scraping pipeline.
- #
- # Usage:
- # require_relative "log"
- # Log.info "geocode", "geocoded DA0306/2025 -> 42 Main St, Hobart"
- # Log.warn "enrich", "lookup failed for da_foo DA123: connection refused"
- # Log.debug "migrate", "column address_std already exists — skipped"
- # Log.error "db", "prepare failed: unknown column 'foo'"
- #
- # Output format (no timestamp — Docker/systemd adds one):
- # INFO [geocode] geocoded DA0306/2025 -> 42 Main St, Hobart
- # WARN [enrich] lookup failed for da_foo DA123: connection refused
- #
- # Verbosity is controlled by the LOG_LEVEL environment variable:
- # LOG_LEVEL=debug — all messages
- # LOG_LEVEL=info — info, warn, error (default)
- # LOG_LEVEL=warn — warn and error only
- # LOG_LEVEL=error — errors only
- #
- # INFO and DEBUG go to $stdout; WARN and ERROR go to $stderr so that
- # docker compose logs shows them on the correct stream.
- module Log
- LEVELS = { debug: 0, info: 1, warn: 2, error: 3 }.freeze
- # Flush immediately — important in Docker where stdout may be block-buffered.
- $stdout.sync = true
- $stderr.sync = true
- def self.debug(component, msg) = emit(:debug, component, msg)
- def self.info(component, msg) = emit(:info, component, msg)
- def self.warn(component, msg) = emit(:warn, component, msg)
- def self.error(component, msg) = emit(:error, component, msg)
- # ---------------------------------------------------------------------------
- def self.min_level
- key = ENV.fetch("LOG_LEVEL", "info").strip.downcase.to_sym
- LEVELS.fetch(key, LEVELS[:info])
- end
- private_class_method :min_level
- def self.emit(level, component, msg)
- return if LEVELS.fetch(level) < min_level
- label = level.to_s.upcase.ljust(5)
- stream = (level == :debug || level == :info) ? $stdout : $stderr
- stream.puts "#{label} [#{component}] #{msg}"
- end
- private_class_method :emit
- end
|