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

Fixes #15874 - added logging of 'X_REQUEST_ID' http headers #475

Merged
merged 1 commit into from Nov 16, 2016
Merged

Fixes #15874 - added logging of 'X_REQUEST_ID' http headers #475

merged 1 commit into from Nov 16, 2016

Conversation

dmitri-d
Copy link
Member

No description provided.

@dmitri-d
Copy link
Member Author

Some notes:

  • for standard logger I replaced process id with request_id. I can restore the process id if it's important. Should I surround request id with <>'s (to help with parsing)?
  • I can return request id with smart-proxy responses if this would be useful

Format = "%s, [%s%s] %5s -- %s: %s\n".freeze

def call(severity, time, progname, msg)
Format % [severity, format_datetime(time), request_id[0..5], severity, progname, msg2str(msg)]
Copy link
Contributor

Choose a reason for hiding this comment

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

The first severity field used to be a single letter (severity[0..0] in stdlib) so it's now duplicated and affects the indentation. Previously:

D, [2016-11-10T13:34:04.946305 #20953] DEBUG -- : require_ssl_client_verification: skipping, non-HTTPS request
I, [2016-11-10T13:34:04.946767 #20953]  INFO -- : 192.168.122.1 - - [10/Nov/2016 13:34:04] "GET /tftp/serverName HTTP/1.1" 200 30 0.0007

Now:

DEBUG, [2016-11-15T13:12:52.052872 ] DEBUG -- : Rack::Handler::WEBrick is mounted on /.
INFO, [2016-11-15T13:12:52.052942 ]  INFO -- : WEBrick::HTTPServer#start: pid=4326 port=8443

time.strftime("%Y-%m-%dT%H:%M:%S.%6N ".freeze)
end

def msg2str(msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this inherit Logger::Formatter to avoid copying this implementation?

Copy link
Member Author

Choose a reason for hiding this comment

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

No point, as the methods that I need access to are private (grrr).

Copy link
Contributor

Choose a reason for hiding this comment

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

A subclass should be able to call the private method (without needing send).

Copy link
Member Author

Choose a reason for hiding this comment

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

You are indeed correct.

@@ -14,6 +14,7 @@ class Proxy::RootApi < Sinatra::Base

get "/version" do
begin

Copy link
Contributor

Choose a reason for hiding this comment

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

Not needed?

def call(env)
Thread.current.thread_variable_set(:request_id, env['HTTP_X_REQUEST_ID']) if env.has_key?('HTTP_X_REQUEST_ID')
to_ret = @app.call(env)
Thread.current.thread_variable_set(:request_id, nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to break logging of the request ID in a default WEBrick configuration, I can't see the request ID at all in the logs.

It appears from adding debug that the thread var's being cleared by this line before the log formatter's called, so the thread variable is always nil. Commenting it out fixes the logging, but will obviously lead to data leaking between requests.

Copy link
Member Author

Choose a reason for hiding this comment

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

This isn't going to work for logging outside of rack app: the middleware is injected after webrick passes control to it, but before the controller code is invoked. There isn't much I can about this short of changing webrick, which I'd like to avoid.

What I could do however is to log the request (probably via additional middleware) and disable debug logging of requests in webrick/rack?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I think I see the issue now, that the Rack::CommonLogger defers logging until the very last minute by using a BodyProxy class. The CommonLogger middleware passes back a BodyProxy class wrapping the original body with a block that logs when the body is closed. By this point, this RequestIdMiddleware has long since completed.

While I'm not completely adverse to reimplementing request logging, it might be simpler to either:

  1. Use Rack::BodyProxy here too - pass a BodyProxy with block back out of call that will wipe the thread variable, which should then be after CommonLogger.
  2. Subclass Rack::CommonLogger, override log to wipe the thread variable.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for looking into this -- I didn't think to look into BodyProxy, but it explains why it's always one of the last thing to get logged. I also think using BodyProxy to clear the thread variable is probably the best approach.

@domcleal
Copy link
Contributor

for standard logger I replaced process id with request_id. I can restore the process id if it's important. Should I surround request id with <>'s (to help with parsing)?

I don't think so, it seems fine to me as-is.

@dmitri-d
Copy link
Member Author

  • LoggerFactory::LogFormatter now inherits from ::Logger::Formatter, which eliminated most of the code duplication
  • request_id thread variable is now cleared when request is closed (via Rack::BodyProxy)

Note: I should point out that only the first 6 digits of the request id are logged, the whole thing (64 digits) would take too much space.

Format = "%s, [%s%s] %5s -- %s: %s\n".freeze

def call(severity, time, progname, msg)
Format % [severity[0..0], format_datetime(time), request_id[0..5], severity, progname, msg2str(msg)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you use eight characters of the request ID? Mostly to match what Foreman currently logs - if the same ID is used then it'll be identical in both log files.

@dmitri-d
Copy link
Member Author

Eight characters of the request id are now being logged. Rebased.

Copy link
Contributor

@domcleal domcleal left a comment

Choose a reason for hiding this comment

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

Still using the first six rather than eight characters?

@dmitri-d
Copy link
Member Author

Actually pushed the change now.

@domcleal
Copy link
Contributor

Thanks, that's better

There's a comment on the ticket stating:

Make sure this gets recorded in structured format in logger buffer as well, so we can easily pull out logs (e.g. errors) via the API and show them.

Would you be able to add this?

@dmitri-d
Copy link
Member Author

Make sure this gets recorded in structured format in logger buffer as well, so we can easily pull out logs (e.g. errors) via the API and show them.

Request_ids are now being stored when available in the log buffer.

@domcleal domcleal merged commit b39011e into theforeman:develop Nov 16, 2016
@domcleal
Copy link
Contributor

Thank you @witlessbird, merged.

@dmitri-d dmitri-d deleted the 15874-request-id-logging branch January 19, 2017 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants