Add support for setting a logger

Adds support for setting `Stripe.logger` to a logger that's compatible
with `Logger` from Ruby's standard library. In set, the library will no
longer log to stdout, and instead emit straight to the logger and defer
decision on what log level to print to it.

Addresses a request in #566.
This commit is contained in:
Brandur 2017-08-11 11:16:29 -07:00
parent 20ec883e8b
commit cb111a8e74
3 changed files with 85 additions and 9 deletions

View File

@ -84,6 +84,7 @@ module Stripe
@uploads_base = 'https://uploads.stripe.com' @uploads_base = 'https://uploads.stripe.com'
@log_level = nil @log_level = nil
@logger = nil
@max_network_retries = 0 @max_network_retries = 0
@max_network_retry_delay = 2 @max_network_retry_delay = 2
@ -151,6 +152,9 @@ module Stripe
# what it's doing. For example, it'll produce information about requests, # what it's doing. For example, it'll produce information about requests,
# responses, and errors that are received. Valid log levels are `debug` and # responses, and errors that are received. Valid log levels are `debug` and
# `info`, with `debug` being a little more verbose in places. # `info`, with `debug` being a little more verbose in places.
#
# Use of this configuration is only useful when `.logger` is _not_ set. When
# it is, the decision what levels to print is entirely deferred to the logger.
def self.log_level def self.log_level
@log_level @log_level
end end
@ -162,6 +166,21 @@ module Stripe
@log_level = val @log_level = val
end end
# Sets a logger to which logging output will be sent. The logger should
# support the same interface as the `Logger` class that's part of Ruby's
# standard library (hint, anything in `Rails.logger` will likely be
# suitable).
#
# If `.logger` is set, the value of `.log_level` is ignored. The decision on
# what levels to print is entirely deferred to the logger.
def self.logger
@logger
end
def self.logger=(val)
@logger = val
end
def self.max_network_retries def self.max_network_retries
@max_network_retries @max_network_retries
end end

View File

@ -90,16 +90,19 @@ module Stripe
end end
def self.log_info(message, data = {}) def self.log_info(message, data = {})
if Stripe.log_level == Stripe::LEVEL_DEBUG ||Stripe.log_level == Stripe::LEVEL_INFO if !Stripe.logger.nil? ||
Stripe.log_level == Stripe::LEVEL_DEBUG ||
Stripe.log_level == Stripe::LEVEL_INFO
log_internal(message, data, color: :cyan, log_internal(message, data, color: :cyan,
level: Stripe::LEVEL_INFO, out: $stdout) level: Stripe::LEVEL_INFO, logger: Stripe.logger, out: $stdout)
end end
end end
def self.log_debug(message, data = {}) def self.log_debug(message, data = {})
if Stripe.log_level == Stripe::LEVEL_DEBUG if !Stripe.logger.nil? ||
Stripe.log_level == Stripe::LEVEL_DEBUG
log_internal(message, data, color: :blue, log_internal(message, data, color: :blue,
level: Stripe::LEVEL_DEBUG, out: $stdout) level: Stripe::LEVEL_DEBUG, logger: Stripe.logger, out: $stdout)
end end
end end
@ -340,16 +343,24 @@ module Stripe
# TODO: Make these named required arguments when we drop support for Ruby # TODO: Make these named required arguments when we drop support for Ruby
# 2.0. # 2.0.
def self.log_internal(message, data = {}, color: nil, level: nil, out: nil) def self.log_internal(message, data = {}, color: nil, level: nil, logger: nil, out: nil)
data_str = data.select { |k,v| !v.nil? }. data_str = data.select { |k,v| !v.nil? }.
map { |(k,v)| map { |(k,v)|
"%s=%s" % [ "%s=%s" % [
colorize(k, color, out.isatty), colorize(k, color, !out.nil? && out.isatty),
wrap_logfmt_value(v) wrap_logfmt_value(v)
] ]
}.join(" ") }.join(" ")
if out.isatty if !logger.nil?
str = "message=%s %s" % [wrap_logfmt_value(message), data_str]
case level
when Stripe::LEVEL_DEBUG
logger.debug(str)
else # Stripe::LEVEL_INFO (there should be no other values)
logger.info(str)
end
elsif out.isatty
out.puts "%s %s %s" % out.puts "%s %s %s" %
[colorize(level[0, 4].upcase, color, out.isatty), message, data_str] [colorize(level[0, 4].upcase, color, out.isatty), message, data_str]
else else

View File

@ -1,3 +1,4 @@
require "logger"
require File.expand_path('../../test_helper', __FILE__) require File.expand_path('../../test_helper', __FILE__)
module Stripe module Stripe
@ -216,6 +217,35 @@ module Stripe
end end
end end
context ".log_* with a logger" do
setup do
@out = StringIO.new
logger = ::Logger.new(@out)
# Set a really simple formatter to make matching output as easy as
# possible.
logger.formatter = proc { |_severity, _datetime, _progname, message|
message
}
Stripe.logger = logger
end
context ".log_debug" do
should "log to the logger" do
Util.log_debug("foo")
assert_equal "message=foo ", @out.string
end
end
context ".log_info" do
should "log to the logger" do
Util.log_info("foo")
assert_equal "message=foo ", @out.string
end
end
end
context ".normalize_headers" do context ".normalize_headers" do
should "normalize the format of a header key" do should "normalize the format of a header key" do
assert_equal({ "Request-Id" => nil }, assert_equal({ "Request-Id" => nil },
@ -267,7 +297,7 @@ module Stripe
end end
Util.send(:log_internal, "message", { foo: "bar" }, Util.send(:log_internal, "message", { foo: "bar" },
color: :green, level: Stripe::LEVEL_DEBUG, out: out) color: :green, level: Stripe::LEVEL_DEBUG, logger: nil, out: out)
assert_equal "\e[0;32;49mDEBU\e[0m message \e[0;32;49mfoo\e[0m=bar\n", assert_equal "\e[0;32;49mDEBU\e[0m message \e[0;32;49mfoo\e[0m=bar\n",
out.string out.string
end end
@ -275,10 +305,26 @@ module Stripe
should "log in a data friendly way" do should "log in a data friendly way" do
out = StringIO.new out = StringIO.new
Util.send(:log_internal, "message", { foo: "bar" }, Util.send(:log_internal, "message", { foo: "bar" },
color: :green, level: Stripe::LEVEL_DEBUG, out: out) color: :green, level: Stripe::LEVEL_DEBUG, logger: nil, out: out)
assert_equal "message=message level=debug foo=bar\n", assert_equal "message=message level=debug foo=bar\n",
out.string out.string
end end
should "log to a logger if set" do
out = StringIO.new
logger = ::Logger.new(out)
# Set a really simple formatter to make matching output as easy as
# possible.
logger.formatter = proc { |_severity, _datetime, _progname, message|
message
}
Util.send(:log_internal, "message", { foo: "bar" },
color: :green, level: Stripe::LEVEL_DEBUG, logger: logger, out: nil)
assert_equal "message=message foo=bar",
out.string
end
end end
context ".wrap_logfmt_value" do context ".wrap_logfmt_value" do