-
Notifications
You must be signed in to change notification settings - Fork 898
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
Conversation
bdunne
commented
Jun 16, 2017
•
edited
Loading
edited
- 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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...
This looks really good. A few questions though.
|
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| |
There was a problem hiding this comment.
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?
@carbonin Few things around #15358 and such (since it got mentioned):
|
lib/vmdb/loggers.rb
Outdated
$log = | ||
if ENV["CONTAINER"] | ||
stdout_log = StdoutLogger.new.tap { |l| l.formatter = ContainerFormatter.new } | ||
MulticastLogger.new($evm_log, stdout_log) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
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). |
This pull request is not mergeable. Please rebase and repush. |
@Fryguy It's now updated with the changes discussed. I think the Formatter name still needs work, but I don't have any ideas. |
d7ab538
to
e2c3305
Compare
lib/vmdb/loggers/multicast_logger.rb
Outdated
|
||
def initialize(*loggers) | ||
require 'set' | ||
@loggers = Set.new |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
49f62d9
to
86a70fc
Compare
|
||
[:log_backtrace, :log_hashes].each do |method| | ||
define_method(method) do |*args| | ||
loggers.map { |l| l.send(method, *args) }.first |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
lib/vmdb/loggers.rb
Outdated
$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"] } |
There was a problem hiding this comment.
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.
lib/vmdb/loggers/container_logger.rb
Outdated
:level => translate_error(severity), | ||
:message => prefix_task_id(msg2str(msg)), | ||
:pid => $PROCESS_ID, | ||
:tid => Thread.current.object_id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.to_s(16)
?
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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)), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
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
@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? 😉 |
88152aa
to
15f5160
Compare
- 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
Checked commits bdunne/manageiq@97ff263~...5f76088 with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0 spec/lib/vmdb/loggers/container_logger_spec.rb
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
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.