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

Conversation

bdunne
Copy link
Member

@bdunne bdunne commented Jun 16, 2017

  • Add a StdoutLogger and ContainerFormatter for use in container environment
  • Add a multicast logger allowing a caller to write to a single "logger" interface and have the message forwarded to multiple backing loggers
  • $log remains the interface to the callers, but it can now be backed by a MulticastLogger in the container environment which will write to both evm.log in the old format and the StdoutLogger in the format that will be parsed by fluentd and sent to elasticsearch.

@@ -0,0 +1,47 @@
class ContainerFormatter < Logger::Formatter
Copy link
Member

Choose a reason for hiding this comment

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

Should this be something like FluentFormatter instead? Is this format constant across all container logging aggregators?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, naming is hard. I don't think this format is specific to fluent though...

@carbonin
Copy link
Member

This looks really good. A few questions though.

  1. Should we add the messages that are going to other log files to stdout as well? How would we do this without also sending those messages to evm.log?
  2. It seems like this has some duplicated code from the base VMDBLogger. Should we wait for Extract vmdb-logger code from manageiq-gems-pending and insert it into manageiq #15358 and refactor some of that (create our existing formatter and such) as a first step?

@gtanzillo
Copy link
Member

This is really cool!

file2 = StringIO.new
multi_logger = described_class.new(Logger.new(file1), Logger.new(file2))

expect(file1).to receive(:write) do |string|
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you consider expect(file1.string).to match(/etc../) here?

@NickLaMuro
Copy link
Member

$log =
if ENV["CONTAINER"]
stdout_log = StdoutLogger.new.tap { |l| l.formatter = ContainerFormatter.new }
MulticastLogger.new($evm_log, stdout_log)
Copy link
Member

Choose a reason for hiding this comment

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

This is more question than request for change: Is the logging output that is more visible to the user when in a container environment the evm.log file or STDOUT (this is without a common logging interface to view the logs)?

The reason I ask is because in my humble opinion, viewing json formatted logs directly is not user readable, and requires something like kibana to view and digest them sensibly. Obviously json formatted logs makes it machine readable without introducing a parser, but makes the human parseability much less so.

So my follow up to that is do we see users, when the common logging stack is unavailable, using the stdout log or the evm.log to monitor the logs within a container/pod? My request would be to format the more user facing one (which might be what you already have here) with the traditional log formatter, and the one to be parsed by fluentd/logstash be formatted in json.

Copy link
Member Author

Choose a reason for hiding this comment

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

When looking at the container in Openshift, there is a tab for logs (container STDOUT) where this JSON output woud be visible. There is a separate tab for the Terminal.

private

def build_message(msg)
msg = 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.

msg2str 👍

if $_miq_worker_current_msg && !$_miq_worker_current_msg.task_id.nil?
prefix = "Q-task_id([#{$_miq_worker_current_msg.task_id}])"
msg = "#{prefix} #{msg}" unless msg.include?(prefix)
end
Copy link
Member

Choose a reason for hiding this comment

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

Can we piggyback on the existing formatter to do this for us? Or perhaps change the VmdbLogger::Formatter to piggyback on this? I don't really like that this has to be duplicated.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@Fryguy
Copy link
Member

Fryguy commented Jun 19, 2017

Does this do the changes as we discussed on the white board? That is, having a single $stdout_logger always on debug, and then the other loggers are more like TeeLoggers (and not writing to N loggers).

@bdunne bdunne changed the title Logging to STDOUT in JSON format for containers [WIP] Logging to STDOUT in JSON format for containers Jun 19, 2017
@miq-bot miq-bot added the wip label Jun 19, 2017
@miq-bot
Copy link
Member

miq-bot commented Jun 19, 2017

This pull request is not mergeable. Please rebase and repush.

@bdunne bdunne changed the title [WIP] Logging to STDOUT in JSON format for containers Logging to STDOUT in JSON format for containers Jun 20, 2017
@bdunne bdunne removed the wip label Jun 20, 2017
@bdunne
Copy link
Member Author

bdunne commented Jun 20, 2017

@Fryguy It's now updated with the changes discussed. I think the Formatter name still needs work, but I don't have any ideas.

@bdunne bdunne force-pushed the stdout_logger branch 3 times, most recently from d7ab538 to e2c3305 Compare June 20, 2017 15:53

def initialize(*loggers)
require 'set'
@loggers = Set.new
Copy link
Member

Choose a reason for hiding this comment

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

You can just do Set.new(loggers), and you don't need to loop over them.

Copy link
Member

Choose a reason for hiding this comment

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

On that note, since you aren't doing any set notation, nor lookups, it's much more efficient to just use a normal Array.

Copy link
Member Author

Choose a reason for hiding this comment

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

My concern is that you wouldn't want the same logger added more than once to avoid getting duplicate messages.

@bdunne bdunne force-pushed the stdout_logger branch 3 times, most recently from 49f62d9 to 86a70fc Compare June 22, 2017 14:46

[: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.

$websocket_log = VMDBLogger.new(path_dir.join("websocket.log"))
$miq_ae_logger = VMDBLogger.new(path_dir.join("automation.log"))
$container_log = ContainerLogger.new
$log = MulticastLogger.new(VMDBLogger.new(path_dir.join("evm.log"))).tap { |l| l.loggers << $container_log if ENV["CONTAINER"] }
Copy link
Member

Choose a reason for hiding this comment

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

Minor, but I feel this should be DRYed up into a method.

:level => translate_error(severity),
:message => prefix_task_id(msg2str(msg)),
:pid => $PROCESS_ID,
:tid => Thread.current.object_id,
Copy link
Member

Choose a reason for hiding this comment

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

.to_s(16) ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds like I should make it a method on VMDBLogger::Formatter, although I have no idea how it is already .to_s(16) there...

Copy link
Member

Choose a reason for hiding this comment

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

the format (aka %) call in the old logger converted the thread to a string using "%x"

:@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.

}.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

@Fryguy
Copy link
Member

Fryguy commented Jun 23, 2017

@bdunne Can you please add the $container_log to the rubocop yml. While you're at it, can you DRY this up, so it doesn't go from A to F? 😉

@bdunne bdunne force-pushed the stdout_logger branch 2 times, most recently from 88152aa to 15f5160 Compare June 23, 2017 22:24
- Allow a caller to write to a single "logger" interface and have the
message forwarded to multiple backing loggers
For example: $log remains the interface to the callers, but it can now be
backed by a MulticastLogger and in the container environment it will write
to both evm.log in the old format and the ContainerLogger (STDOUT) in the
format that will be parsed by fluentd and sent to elasticsearch.

The AuditLogger already writes to $log which will forward the message
@miq-bot
Copy link
Member

miq-bot commented Jun 24, 2017

Checked commits bdunne/manageiq@97ff263~...5f76088 with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0
5 files checked, 1 offense detected

spec/lib/vmdb/loggers/container_logger_spec.rb

  • ❗ - Line 3, Col 17 - Rails/TimeZone - Do not use Time.now without zone. Use one of Time.zone.now, Time.current, Time.now.in_time_zone, Time.now.utc, Time.now.getlocal, Time.now.iso8601, Time.now.jisx0301, Time.now.rfc3339, Time.now.to_i, Time.now.to_f instead.

Copy link
Member

@carbonin carbonin left a comment

Choose a reason for hiding this comment

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

👍

@Fryguy Fryguy merged commit c21568d into ManageIQ:master Jun 26, 2017
@Fryguy Fryguy added this to the Sprint 64 Ending Jul 3, 2017 milestone Jun 26, 2017
@bdunne bdunne deleted the stdout_logger branch June 26, 2017 19:11
imtayadeway added a commit to imtayadeway/manageiq that referenced this pull request Jul 5, 2017
With the collision of ManageIQ#15058 and ManageIQ#15392 these specs are now
failing. Since there's no way to sensibly implement `#reopen` on a
multicast logger opaquely, instead we'll add a logger to the multicast
loggers and verify against that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants