the path? ◦ Which domain? ◦ Which user? ◦ What browser/client are they using? ◦ What is their IP? ◦ What did they ask us? • Which server are we? • Which version of the app is this? • How did we even get here? Context
the path? request.path ◦ Which domain? request.host ◦ Which user? request.user ◦ What browser/client are they using? request.user_agent ◦ What is their IP? request.remote_addr ◦ What did they ask us? request.args • Which server are we? get_hostname() • Which version of the app is this? get_git_revision() • How did we even get here? traceback Context
15:05:05,468 slides:5 Hello PyConWeb Adapter str str LogRecord LogRecord LogRecord str Filter Filters LogRecord Insert Context Here Insert Context Here Insert Context Here Insert Context Here Insert Context Here Insert Context Here Insert Context Here
get_context() # This should be from a ContextVar/threading.local record.username = context.username record.domain = context.domain record.path = context.path … return True handler.addFilter(ContextFilter())
= old_factory(*args, **kwargs) context = get_context() # This should be from a ContextVar/threading.local record.username = context.username record.domain = context.domain record.path = context.path … return record logging.setLogRecordFactory(record_factory)
OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36 154.142.78.32 brian00 INFO 2019-05-26 15:05:05,468 slides:11 Hello PyConWeb
never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout. - 12factor.net/logs Yes! But.. what if we don’t?
Duration 15:20:04 52615044216 brian00 GET /v1/hello INFO 200 701 15:20:03 52615038721 manuel POST /v1/foo INFO 200 227 15:20:03 52615033207 basil GET /v1/hello INFO 500 112 15:20:03 52615034562 aurthur GET /v1/search INFO 404 247 15:20:02 52615029475 the_animator GET /v1/search CRITICAL 500 60
You never go debugging individual requests ◦ You care more about statistics than individual requests • Maybe you shouldn’t log directly to a database IF: ◦ Your app doesn’t already use a database ◦ You have already (necessarily) optimised your app to minimise database access • You should cleanup your logs after some time ◦ To avoid wasting space ◦ To comply with user data retention policies/laws • All database writes for logging should be time limited and error handled so an error in logging doesn’t bring down the whole app
(even those generated by libraries) • Format logs as JSON • Write logs to a database • Write request-response metadata to database • Always keep the stderr handlers (StreamHandler) enabled, just in case • Write more log messages • Review the usefulness of your logs periodically
request_id is particularly useful for web apps • logging is very flexible but confusing • Working with formatters, handlers and filters keeps things flexible • Writing logs to a database provides many advantages • More readable logs encourages writing more useful logs -> easier debugging