Skip to content

Commit

Permalink
Merge pull request #3987 from fluent/console-logger-adapter
Browse files Browse the repository at this point in the history
Adapt Console::Logger to Fluent::Log
  • Loading branch information
ashie authored Jan 23, 2023
2 parents 8df759b + fb97f56 commit c48ca04
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 2 deletions.
66 changes: 66 additions & 0 deletions lib/fluent/log/console_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'console/terminal/logger'

module Fluent
class Log
# Async gem which is used by http_server helper switched logger mechanism to
# Console gem which isn't complatible with Ruby's standard Logger (since
# v1.17). This class adapts it to Fluentd's logger mechanism.
class ConsoleAdapter < Console::Terminal::Logger
def self.wrap(logger)
_, level = Console::Logger::LEVELS.find { |key, value|
if logger.level <= 0
key == :debug
else
value == logger.level - 1
end
}
Console::Logger.new(ConsoleAdapter.new(logger), level: level)
end

def initialize(logger)
@logger = logger
# When `verbose` is `true`, following items will be added as a prefix or
# suffix of the subject:
# * Severity
# * Object ID
# * PID
# * Time
# Severity and Time are added by Fluentd::Log too so they are redundant.
# PID is the worker's PID so it's also redundant.
# Object ID will be too verbose in usual cases.
# So set it as `false` here to suppress redundant items.
super(StringIO.new, verbose: false)
end

def call(subject = nil, *arguments, name: nil, severity: 'info', **options, &block)
if LEVEL_TEXT.include?(severity.to_s)
level = severity
else
@logger.warn("Unknown severity: #{severity}")
level = 'warn'
end

@io.seek(0)
@io.truncate(0)
super
@logger.send(level, @io.string.chomp)
end
end
end
end
3 changes: 2 additions & 1 deletion lib/fluent/plugin_helper/http_server/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
require 'fluent/plugin_helper/http_server/app'
require 'fluent/plugin_helper/http_server/router'
require 'fluent/plugin_helper/http_server/methods'
require 'fluent/log/console_adapter'

module Fluent
module PluginHelper
Expand All @@ -38,7 +39,7 @@ def initialize(addr:, port:, logger:, default_app: nil, tls_context: nil)
scheme = tls_context ? 'https' : 'http'
@uri = URI("#{scheme}://#{@addr}:#{@port}").to_s
@router = Router.new(default_app)
@reactor = Async::Reactor.new(nil, logger: @logger)
@reactor = Async::Reactor.new(nil, logger: Fluent::Log::ConsoleAdapter.wrap(@logger))

opts = if tls_context
{ ssl_context: tls_context }
Expand Down
110 changes: 110 additions & 0 deletions test/log/test_console_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
require_relative '../helper'

require 'fluent/log'
require 'fluent/log/console_adapter'

class ConsoleAdapterTest < Test::Unit::TestCase
def setup
@timestamp = Time.parse("2023-01-01 15:32:41 +0000")
@timestamp_str = @timestamp.strftime("%Y-%m-%d %H:%M:%S %z")
Timecop.freeze(@timestamp)

@logdev = Fluent::Test::DummyLogDevice.new
@logger = ServerEngine::DaemonLogger.new(@logdev)
@fluent_log = Fluent::Log.new(@logger)
@console_logger = Fluent::Log::ConsoleAdapter.wrap(@fluent_log)
end

def teardown
Timecop.return
end

def test_expected_log_levels
assert_equal({debug: 0, info: 1, warn: 2, error: 3, fatal: 4},
Console::Logger::LEVELS)
end

data(trace: [Fluent::Log::LEVEL_TRACE, :debug],
debug: [Fluent::Log::LEVEL_DEBUG, :debug],
info: [Fluent::Log::LEVEL_INFO, :info],
warn: [Fluent::Log::LEVEL_WARN, :warn],
error: [Fluent::Log::LEVEL_ERROR, :error],
fatal: [Fluent::Log::LEVEL_FATAL, :fatal])
def test_reflect_log_level(data)
level, expected = data
@fluent_log.level = level
console_logger = Fluent::Log::ConsoleAdapter.wrap(@fluent_log)
assert_equal(Console::Logger::LEVELS[expected],
console_logger.level)
end

data(debug: :debug,
info: :info,
warn: :warn,
error: :error,
fatal: :fatal)
def test_string_subject(level)
@console_logger.send(level, "subject")
assert_equal(["#{@timestamp_str} [#{level}]: 0.0s: subject\n"],
@logdev.logs)
end

data(debug: :debug,
info: :info,
warn: :warn,
error: :error,
fatal: :fatal)
def test_args(level)
@console_logger.send(level, "subject", 1, 2, 3)
assert_equal([
"#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
" | 1\n" +
" | 2\n" +
" | 3\n"
],
@logdev.logs)
end

data(debug: :debug,
info: :info,
warn: :warn,
error: :error,
fatal: :fatal)
def test_options(level)
@console_logger.send(level, "subject", kwarg1: "opt1", kwarg2: "opt2")
assert_equal([
"#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
" | {\"kwarg1\":\"opt1\",\"kwarg2\":\"opt2\"}\n"
],
@logdev.logs)
end

data(debug: :debug,
info: :info,
warn: :warn,
error: :error,
fatal: :fatal)
def test_block(level)
@console_logger.send(level, "subject") { "block message" }
assert_equal([
"#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
" | block message\n"
],
@logdev.logs)
end

data(debug: :debug,
info: :info,
warn: :warn,
error: :error,
fatal: :fatal)
def test_multiple_entries(level)
@console_logger.send(level, "subject1")
@console_logger.send(level, "line2")
assert_equal([
"#{@timestamp_str} [#{level}]: 0.0s: subject1\n",
"#{@timestamp_str} [#{level}]: 0.0s: line2\n"
],
@logdev.logs)
end
end
2 changes: 1 addition & 1 deletion test/plugin_helper/test_http_server_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def start_https_request(addr, port, verify: true, cert_path: nil, selfsigned: tr
end

client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse("https://#{addr}:#{port}", ssl_context: context))
reactor = Async::Reactor.new(nil, logger: NULL_LOGGER)
reactor = Async::Reactor.new(nil, logger: Fluent::Log::ConsoleAdapter.wrap(NULL_LOGGER))

resp = nil
error = nil
Expand Down

0 comments on commit c48ca04

Please sign in to comment.