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

Logging to STDOUT in JSON format for containers #15392

Merged
merged 6 commits into from
Jun 26, 2017
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rubocop_local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ GlobalVars:
- $api_log
- $aws_log
- $azure_log
- $container_log
- $datawarehouse_log
- $fog_log
- $lenovo_log
Expand Down
41 changes: 24 additions & 17 deletions lib/vmdb/loggers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,39 @@ def self.apply_config(config)
def self.create_loggers
path_dir = ManageIQ.root.join("log")

$log = VMDBLogger.new(path_dir.join("evm.log"))
$rails_log = VMDBLogger.new(path_dir.join("#{ManageIQ.env}.log"))
$audit_log = AuditLogger.new(path_dir.join("audit.log"))
$fog_log = FogLogger.new(path_dir.join("fog.log"))
$policy_log = VMDBLogger.new(path_dir.join("policy.log"))
$vim_log = VMDBLogger.new(path_dir.join("vim.log"))
$rhevm_log = VMDBLogger.new(path_dir.join("rhevm.log"))
$aws_log = VMDBLogger.new(path_dir.join("aws.log"))
$lenovo_log = VMDBLogger.new(path_dir.join("lenovo.log"))
$kube_log = VMDBLogger.new(path_dir.join("kubernetes.log"))
$mw_log = VMDBLogger.new(path_dir.join("middleware.log"))
$datawarehouse_log = VMDBLogger.new(path_dir.join("datawarehouse.log"))
$scvmm_log = VMDBLogger.new(path_dir.join("scvmm.log"))
$azure_log = VMDBLogger.new(path_dir.join("azure.log"))
$api_log = VMDBLogger.new(path_dir.join("api.log"))
$websocket_log = VMDBLogger.new(path_dir.join("websocket.log"))
$miq_ae_logger = VMDBLogger.new(path_dir.join("automation.log"))
$container_log = ContainerLogger.new
$log = create_multicast_logger(path_dir.join("evm.log"))
$rails_log = create_multicast_logger(path_dir.join("#{Rails.env}.log"))
$fog_log = create_multicast_logger(path_dir.join("fog.log"), FogLogger)
$policy_log = create_multicast_logger(path_dir.join("policy.log"))
$vim_log = create_multicast_logger(path_dir.join("vim.log"))
$rhevm_log = create_multicast_logger(path_dir.join("rhevm.log"))
$aws_log = create_multicast_logger(path_dir.join("aws.log"))
$lenovo_log = create_multicast_logger(path_dir.join("lenovo.log"))
$kube_log = create_multicast_logger(path_dir.join("kubernetes.log"))
$mw_log = create_multicast_logger(path_dir.join("middleware.log"))
$datawarehouse_log = create_multicast_logger(path_dir.join("datawarehouse.log"))
$scvmm_log = create_multicast_logger(path_dir.join("scvmm.log"))
$azure_log = create_multicast_logger(path_dir.join("azure.log"))
$api_log = create_multicast_logger(path_dir.join("api.log"))
$websocket_log = create_multicast_logger(path_dir.join("websocket.log"))
$miq_ae_logger = create_multicast_logger(path_dir.join("automation.log"))

configure_external_loggers
end

def self.create_multicast_logger(log_file_path, logger_class = VMDBLogger)
MulticastLogger.new(logger_class.new(log_file_path)).tap do |l|
l.loggers << $container_log if ENV["CONTAINER"]
end
end
private_class_method :create_multicast_logger

def self.configure_external_loggers
require 'awesome_spawn'
AwesomeSpawn.logger = $log
end

private_class_method :configure_external_loggers


Expand Down
57 changes: 57 additions & 0 deletions lib/vmdb/loggers/container_logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
module Vmdb::Loggers
class ContainerLogger < VMDBLogger
def initialize(logdev = STDOUT, *args)
super
self.level = DEBUG
self.formatter = Formatter.new
end

def level=(_new_level)
super(DEBUG) # We want everything written to the ContainerLogger written to STDOUT
end

def filename
"STDOUT"
end

class Formatter < VMDBLogger::Formatter
SEVERITY_MAP = {
"DEBUG" => "debug",
"INFO" => "info",
"WARN" => "warning",
"ERROR" => "err",
"FATAL" => "crit",
"UNKNOWN" => "unknown"
# Others that don't match up: alert emerg notice trace
}.freeze

def call(severity, time, progname, msg)
# From https://github.com/ViaQ/elasticsearch-templates/releases Downloads asciidoc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to follow this URL, but I can't find what you are trying to point to here. What is the Downloads asciidoc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow the link, under "Downloads" you'll see an asciidoc which contains the "schema"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh...that wasn't clear to me, as it was a full java-namespace looking thing

{
:@timestamp => format_datetime(time),
:hostname => hostname,
:level => translate_error(severity),
:message => prefix_task_id(msg2str(msg)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this need a PR to gems pending?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:pid => $PROCESS_ID,
:tid => thread_id,
:service => progname,
# :tags => "tags string",
}.to_json << "\n"
end

private

def hostname
@hostname ||= ENV["HOSTNAME"]
end

def thread_id
Thread.current.object_id.to_s(16)
end

def translate_error(level)
SEVERITY_MAP[level] || "unknown"
end
end
end
end
31 changes: 31 additions & 0 deletions lib/vmdb/loggers/multicast_logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class MulticastLogger < Logger
attr_accessor :loggers

def initialize(*loggers)
require 'set'
@loggers = Set.new(loggers)
@level = DEBUG
end

def level=(new_level)
loggers.each { |l| l.level = new_level }
super
end

def filename
loggers.first.filename
end

[:log_backtrace, :log_hashes].each do |method|
define_method(method) do |*args|
loggers.map { |l| l.send(method, *args) }.first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, this implies all loggers passed in through .new have a log_backtrace and log_hashes method which only our loggers have. I wonder if we should check if the provided logger implement those methods and only forward the call to them if they implement it. Maybe that's too much work? Maybe only support our loggers? Or just don't care...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we care right now. We only have one place using it and it is always backed by one of our loggers.

end
end

def add(*args, &block)
severity = args.first || UNKNOWN
return true if severity < @level
loggers.each { |l| l.send(:add, *args, &block) }
true
end
end
15 changes: 15 additions & 0 deletions spec/lib/vmdb/loggers/container_logger_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
describe Vmdb::Loggers::ContainerLogger::Formatter do
it "stuff" do
time = Time.now
result = described_class.new.call("INFO", time, "some_program", "testing 1, 2, 3")
expect(JSON.parse(result)).to have_attributes(
"@timestamp" => time.strftime("%Y-%m-%dT%H:%M:%S.%6N "),
"hostname" => ENV["HOSTNAME"],
"level" => "info",
"message" => "testing 1, 2, 3",
"pid" => $PROCESS_ID,
"service" => "some_program",
"tid" => Thread.current.object_id.to_s(16),
)
end
end
29 changes: 29 additions & 0 deletions spec/lib/vmdb/loggers/multicast_logger_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
describe MulticastLogger do
let(:logger1) { Logger.new(StringIO.new) }
let(:logger2) { Logger.new(StringIO.new) }
subject { described_class.new(logger1, logger2) }

context "#add" do
it "forwards to the other loggers" do
expect(logger1).to receive(:add).with(1, nil, "test message")
expect(logger2).to receive(:add).with(1, nil, "test message")

subject.info("test message")
end

it "only forwards the message if the severity is correct" do
subject.level = 1
logger1.level = 0

[logger1, logger2].each { |l| expect(l).not_to receive(:add) }

subject.debug("test message")
end
end

it "#level= updates the log level on all backing devices" do
[logger1, logger2, subject].each { |l| expect(l.level).to eq(0) }
subject.level = 3
[logger1, logger2, subject].each { |l| expect(l.level).to eq(3) }
end
end