Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow logs to be tagged without a block #38850

Merged
merged 1 commit into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions activesupport/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
* Calling `ActiveSupport::TaggedLogging#tagged` without a block now returns a tagged logger.

```ruby
logger.tagged("BCX").info("Funky time!") # => [BCX] Funky time!
```

*Eugene Kenny*

* Align `Range#cover?` extension behavior with Ruby behavior for backwards ranges.

`(1..10).cover?(5..3)` now returns `false`, as it does in plain Ruby.
Expand Down
26 changes: 25 additions & 1 deletion activesupport/lib/active_support/tagged_logging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@
module ActiveSupport
# Wraps any standard Logger object to provide tagging capabilities.
#
# May be called with a block:
#
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
# logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
# logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
# logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
#
# If called without a block, a new logger will be returned with applied tags:
#
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
# logger.tagged("BCX").info "Stuff" # Logs "[BCX] Stuff"
# logger.tagged("BCX", "Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
# logger.tagged("BCX").tagged("Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
#
# This is used by the default Rails.logger as configured by Railties to make
# it easy to stamp log lines with subdomains, request ids, and anything else
# to aid debugging of multi-user production applications.
Expand Down Expand Up @@ -66,6 +75,14 @@ def tags_text
end
end

module LocalTagStorage # :nodoc:
attr_accessor :current_tags

def self.extended(base)
base.current_tags = []
end
end

def self.new(logger)
logger = logger.dup

Expand All @@ -83,7 +100,14 @@ def self.new(logger)
delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter

def tagged(*tags)
formatter.tagged(*tags) { yield self }
if block_given?
formatter.tagged(*tags) { yield self }
else
logger = ActiveSupport::TaggedLogging.new(self)
logger.formatter.extend LocalTagStorage
logger.push_tags(*formatter.current_tags, *tags)
logger
end
end

def flush
Expand Down
77 changes: 77 additions & 0 deletions activesupport/test/tagged_logging_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,80 @@ def flush(*)
assert_equal "[BCX] [Jason] Funky time\n[BCX] Junky time!\n", @output.string
end
end

class TaggedLoggingWithoutBlockTest < ActiveSupport::TestCase
setup do
@output = StringIO.new
@logger = ActiveSupport::TaggedLogging.new(Logger.new(@output))
end

test "tagged once" do
@logger.tagged("BCX").info "Funky time"
assert_equal "[BCX] Funky time\n", @output.string
end

test "tagged twice" do
@logger.tagged("BCX").tagged("Jason").info "Funky time"
assert_equal "[BCX] [Jason] Funky time\n", @output.string
end

test "tagged thrice at once" do
@logger.tagged("BCX", "Jason", "New").info "Funky time"
assert_equal "[BCX] [Jason] [New] Funky time\n", @output.string
end

test "tagged are flattened" do
@logger.tagged("BCX", %w(Jason New)).info "Funky time"
assert_equal "[BCX] [Jason] [New] Funky time\n", @output.string
end

test "tagged once with blank and nil" do
@logger.tagged(nil, "", "New").info "Funky time"
assert_equal "[New] Funky time\n", @output.string
end

test "shares tags across threads" do
logger = @logger.tagged("BCX")

Thread.new do
logger.info "Dull story"
logger.tagged("OMG").info "Cool story"
end.join

logger.info "Funky time"

assert_equal "[BCX] Dull story\n[BCX] [OMG] Cool story\n[BCX] Funky time\n", @output.string
end

test "keeps each tag in their own instance" do
other_output = StringIO.new
other_logger = ActiveSupport::TaggedLogging.new(Logger.new(other_output))

tagged_logger = @logger.tagged("OMG")
other_tagged_logger = other_logger.tagged("BCX")
tagged_logger.info "Cool story"
other_tagged_logger.info "Funky time"

assert_equal "[OMG] Cool story\n", @output.string
assert_equal "[BCX] Funky time\n", other_output.string
end

test "does not share the same formatter instance of the original logger" do
other_logger = ActiveSupport::TaggedLogging.new(@logger)

tagged_logger = @logger.tagged("OMG")
other_tagged_logger = other_logger.tagged("BCX")
tagged_logger.info "Cool story"
other_tagged_logger.info "Funky time"

assert_equal "[OMG] Cool story\n[BCX] Funky time\n", @output.string
end

test "mixed levels of tagging" do
logger = @logger.tagged("BCX")
logger.tagged("Jason").info "Funky time"
logger.info "Junky time!"

assert_equal "[BCX] [Jason] Funky time\n[BCX] Junky time!\n", @output.string
end
end