Path: blob/trunk/rb/lib/selenium/webdriver/common/logger.rb
4085 views
# frozen_string_literal: true12# Licensed to the Software Freedom Conservancy (SFC) under one3# or more contributor license agreements. See the NOTICE file4# distributed with this work for additional information5# regarding copyright ownership. The SFC licenses this file6# to you under the Apache License, Version 2.0 (the7# "License"); you may not use this file except in compliance8# with the License. You may obtain a copy of the License at9#10# http://www.apache.org/licenses/LICENSE-2.011#12# Unless required by applicable law or agreed to in writing,13# software distributed under the License is distributed on an14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY15# KIND, either express or implied. See the License for the16# specific language governing permissions and limitations17# under the License.1819require 'forwardable'20require 'logger'2122module Selenium23module WebDriver24#25# @example Enable full logging26# Selenium::WebDriver.logger.level = :debug27#28# @example Log to file29# Selenium::WebDriver.logger.output = 'selenium.log'30#31# @example Use logger manually32# Selenium::WebDriver.logger.info('This is info message')33# Selenium::WebDriver.logger.warn('This is warning message')34#35class Logger36extend Forwardable3738def_delegators :@logger,39:close,40:debug?,41:info?,42:warn?,43:error?,44:fatal, :fatal?,45:level4647#48# @param [String] progname Allow child projects to use Selenium's Logger pattern49#50def initialize(progname = 'Selenium', default_level: nil, ignored: nil, allowed: nil)51default_level ||= $DEBUG || ENV.key?('DEBUG') ? :debug : :warn5253@logger = create_logger(progname, level: default_level)54@ignored = Array(ignored)55@allowed = Array(allowed)56@first_warning = false57@level_forced = false58@output_forced = false59end6061#62# Forces debug level and prevents it from being overridden.63#64def debug!65@level_forced = true66@logger.level = :debug67end6869#70# Forces output to stderr and prevents it from being overridden.71#72def stderr!73@output_forced = true74@logger.reopen($stderr)75end7677def level=(level)78if @level_forced79warn('Logger level is forced; ignoring override', id: :logger)80return81end8283if level == :info && @logger.level == :info84info(':info is now the default log level, to see additional logging, set log level to :debug')85end8687@logger.level = level88end8990#91# Changes logger output to a new IO.92#93# @param [String] io94#95def output=(io)96if @output_forced97warn('Logger output is forced; ignoring override', id: :logger)98return99end100101@logger.reopen(io)102end103104#105# Returns IO object used by logger internally.106#107# Normally, we would have never needed it, but we want to108# use it as IO object for all child processes to ensure their109# output is redirected there.110#111# It is only used in debug level, in other cases output is suppressed.112#113# @api private114#115def io116@logger.instance_variable_get(:@logdev).dev117end118119#120# Will not log the provided ID.121#122# @param [Array, Symbol] ids123#124def ignore(*ids)125@ignored += Array(ids).flatten126end127128#129# Will only log the provided ID.130#131# @param [Array, Symbol] ids132#133def allow(*ids)134@allowed += Array(ids).flatten135end136137#138# Used to supply information of interest for debugging a problem139# Overrides default #debug to skip ignored messages by provided id140#141# @param [String] message142# @param [Symbol, Array<Symbol>] id143# @yield see #deprecate144#145def debug(message, id: [], &block)146discard_or_log(:debug, message, id, &block)147end148149#150# Used to supply information of general interest151#152# @param [String] message153# @param [Symbol, Array<Symbol>] id154# @yield see #deprecate155#156def info(message, id: [], &block)157discard_or_log(:info, message, id, &block)158end159160#161# Used to supply information that suggests an error occurred162#163# @param [String] message164# @param [Symbol, Array<Symbol>] id165# @yield see #deprecate166#167def error(message, id: [], &block)168discard_or_log(:error, message, id, &block)169end170171#172# Used to supply information that suggests action be taken by user173#174# @param [String] message175# @param [Symbol, Array<Symbol>] id176# @yield see #deprecate177#178def warn(message, id: [], &block)179discard_or_log(:warn, message, id, &block)180end181182#183# Marks code as deprecated with/without replacement.184#185# @param [String] old186# @param [String, nil] new187# @param [Symbol, Array<Symbol>] id188# @param [String] reference189# @yield appends additional message to end of provided template190#191def deprecate(old, new = nil, id: [], reference: '', &block)192id = Array(id)193return if @ignored.include?(:deprecations)194195id << :deprecations if @allowed.include?(:deprecations)196197message = "[DEPRECATION] #{old} is deprecated"198message << if new199". Use #{new} instead."200else201' and will be removed in a future release.'202end203message << " See explanation for this deprecation: #{reference}." unless reference.empty?204205discard_or_log(:warn, message, id, &block)206end207208private209210def create_logger(name, level:)211logger = ::Logger.new($stderr)212logger.progname = name213logger.level = level214logger.formatter = proc do |severity, time, progname, msg|215"#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n".force_encoding('UTF-8')216end217218logger219end220221def discard_or_log(level, message, id)222id = Array(id)223return if @ignored.intersect?(id)224return if @allowed.any? && !@allowed.intersect?(id)225226return if ::Logger::Severity.const_get(level.upcase) < @logger.level227228unless @first_warning229@first_warning = true230info("Details on how to use and modify Selenium logger:\n", id: [:logger_info]) do231"https://selenium.dev/documentation/webdriver/troubleshooting/logging\n"232end233end234235msg = id.empty? ? message : "[#{id.map(&:inspect).join(', ')}] #{message} "236msg += " #{yield}" if block_given?237238@logger.send(level) { msg }239end240end # Logger241end # WebDriver242end # Selenium243244245