simplify logging setup

* use Syslog::Logger instead of writing our own
 * extract and share the log formatting between Syslog and file logging

Change-Id: I266713e4c390f11d1650afcf9005059995aca922
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/341759
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Jacob Burroughs <jburroughs@instructure.com>
QA-Review: Cody Cutrer <cody@instructure.com>
Product-Review: Cody Cutrer <cody@instructure.com>
This commit is contained in:
Cody Cutrer 2024-02-29 09:08:02 -07:00
parent c29d816cd5
commit 474233f086
3 changed files with 32 additions and 210 deletions

View File

@ -79,16 +79,13 @@ module CanvasRails
log_config = Rails.root.join("config/logging.yml").file? && Rails.application.config_for(:logging).with_indifferent_access
log_config = { "logger" => "rails", "log_level" => "debug" }.merge(log_config || {})
opts = {}
require "canvas_logger"
config.log_level = log_config["log_level"]
log_level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase)
opts[:skip_thread_context] = true if log_config["log_context"] == false
log_level = Logger.const_get(config.log_level.to_s.upcase)
case log_config["logger"]
when "syslog"
require "syslog_wrapper"
require "syslog/logger"
log_config["app_ident"] ||= "canvas-lms"
log_config["daemon_ident"] ||= "canvas-lms-daemon"
facilities = 0
@ -96,17 +93,43 @@ module CanvasRails
facilities |= Syslog.const_get :"LOG_#{facility.to_s.upcase}"
end
ident = (ENV["RUNNING_AS_DAEMON"] == "true") ? log_config["daemon_ident"] : log_config["app_ident"]
opts[:include_pid] = true if log_config["include_pid"] == true
config.logger = SyslogWrapper.new(ident, facilities, opts)
config.logger.level = log_level
config.logger = Syslog::Logger.new(ident, facilities)
syslog_options = (log_config["include_pid"] == true) ? Syslog::LOG_PID : 0
if (Syslog.instance.options & Syslog::LOG_PID) != syslog_options
config.logger.syslog = Syslog.reopen(Syslog.instance.ident,
(Syslog.instance.options & ~Syslog::LOG_PID) | syslog_options,
Syslog.instance.facility)
end
else
require "canvas_logger"
log_path = config.paths["log"].first
if ENV["RUNNING_AS_DAEMON"] == "true"
log_path = Rails.root.join("log/delayed_job.log")
end
config.logger = CanvasLogger.new(log_path, log_level, opts)
FileUtils.mkdir_p(File.dirname(log_path))
config.logger = CanvasLogger.new(log_path, log_level)
end
config.logger.level = log_level
unless log_config["log_context"] == false
class ContextFormatter < Logger::Formatter
def initialize(parent_formatter)
super()
@parent_formatter = parent_formatter
end
def call(severity, time, progname, msg)
msg = @parent_formatter.call(severity, time, progname, msg)
context = Thread.current[:context] || {}
"[#{context[:session_id] || "-"} #{context[:request_id] || "-"}] #{msg}"
end
end
config.logger.formatter = ContextFormatter.new(config.logger.formatter)
end
# Activate observers that should always be running

View File

@ -18,42 +18,6 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
class CanvasLogger < ActiveSupport::Logger
attr_reader :log_path
def initialize(log_path, level = DEBUG, options = {})
unless File.exist?(log_path)
FileUtils.mkdir_p(File.dirname(log_path))
end
super(log_path, level)
@log_path = log_path
@skip_thread_context = options[:skip_thread_context]
end
def add(severity, message = nil, progname = nil)
return if level > severity
message = (message || (block_given? && yield) || progname).to_s
# If a newline is necessary then create a new message ending with a newline.
# Ensures that the original message is not mutated.
unless @skip_thread_context
context = Thread.current[:context] || {}
message = "[#{context[:session_id] || "-"} #{context[:request_id] || "-"}] #{message}"
end
super(severity, message, progname)
end
def reopen(log_path)
unless File.exist?(log_path)
FileUtils.mkdir_p(File.dirname(log_path))
end
@log_path = log_path
old_logdev = @logdev
@logdev = ::Logger::LogDevice.new(log_path, shift_age: 0, shift_size: 1_048_576)
old_logdev.close
end
def capture_messages(&)
CanvasLogger.prepend Capture unless CanvasLogger.include?(Capture)
capture_messages(&)

View File

@ -1,165 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require "syslog"
class SyslogWrapper
attr_accessor :level, :datetime_format, :formatter
@@silencer = true
def self.silencer
@@silencer
end
def silencer
@@silencer
end
def self.silencer=(obj)
@@silencer = obj
end
def silencer=(obj)
@@silencer = obj
end
def silence(temporary_level = Logger::ERROR)
if silencer
begin
old_logger_level, @level = @level, temporary_level
yield self
ensure
@level = old_logger_level
end
else
yield self
end
end
alias_method :quietly, :silence
# facility is a logical-or-ed collection of the following constants in Syslog
# LOG_AUTHPRIV - security or authorization messages which should be kept private
# LOG_CONSOLE - system console message
# LOG_CRON - system task scheduler (cron or at)
# LOG_DAEMON - a system daemon which has no facility value of its own
# LOG_FTP - an ftp server
# LOG_LRP - line printer subsystem
# LOG_MAIL - mail delivery or transport subsystem
# LOG_NEWS - usenet news system
# LOG_NTP - network time protocol server
# LOG_SECURITY - general security message
# LOG_SYSLOG - messages generated internally by syslog
# LOG_USER - generic user-level message
# LOG_UUCP - uucp subsystem
# LOG_LOCAL0 through LOG_LOCAL7 - locally defined facilities
# example: SyslogWrapper.new("canvas", Syslog::LOG_USER, :include_pid => true)
def initialize(ident, facility = 0, options = {})
unless $syslog
flags = 0
flags |= Syslog::LOG_CONS if options[:bail_to_console]
flags |= Syslog::LOG_NDELAY if options[:ndelay]
flags |= Syslog::LOG_PERROR if options[:perror]
flags |= Syslog::LOG_PID if options[:include_pid]
$syslog = Syslog.open(ident, flags, facility)
end
@level = 0
@skip_thread_context = options[:skip_thread_context]
@datetime_format = nil # ignored completely
end
def close; end
SEVERITY_MAP = {
Logger::DEBUG => :debug,
Logger::INFO => :info,
Logger::WARN => :warning,
Logger::ERROR => :err,
Logger::FATAL => :crit,
Logger::UNKNOWN => :notice
}.freeze
def add(severity, message = nil, progname = nil)
severity ||= Logger::UNKNOWN
return if @level > severity
if message.nil?
message = if block_given?
yield
else
progname
end
end
message = message.to_s.strip.gsub(/\e\[([0-9]+(;|))+m/, "")
unless @skip_thread_context
context = Thread.current[:context] || {}
message = "[#{context[:session_id] || "-"} #{context[:request_id] || "-"}] #{message}"
end
$syslog.send(SEVERITY_MAP[severity], "%s", message)
end
alias_method :log, :add
def <<(msg)
add(@level, msg)
end
def debug(progname = nil, &)
add(Logger::DEBUG, nil, progname, &)
end
def info(progname = nil, &)
add(Logger::INFO, nil, progname, &)
end
def warn(progname = nil, &)
add(Logger::WARN, nil, progname, &)
end
def error(progname = nil, &)
add(Logger::ERROR, nil, progname, &)
end
def fatal(progname = nil, &)
add(Logger::FATAL, nil, progname, &)
end
def unknown(progname = nil, &)
add(Logger::UNKNOWN, nil, progname, &)
end
def debug?
@level <= Logger::DEBUG
end
def info?
@level <= Logger::INFO
end
def warn?
@level <= Logger::WARN
end
def error?
@level <= Logger::ERROR
end
def fatal?
@level <= Logger::FATAL
end
end