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

aiohttp.wep.Application.make_handler access_log_class support #2316

Merged
merged 13 commits into from
Oct 13, 2017

Conversation

hellysmile
Copy link
Member

@hellysmile hellysmile commented Oct 12, 2017

What do these changes do?

Adds support pluggable aiohttp.helpers.AccessLogger for aiohttp.wep.Application.make_handler

Are there changes in behavior for the user?

No breaking changes.

Related issue number

#2315

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
    • The format is <Name> <Surname>.
    • Please keep alphabetical order, the file is sorted by names.
  • Add a new news fragment into the changes folder
    • name it <issue_id>.<type> for example (588.bug)
    • if you don't have an issue_id change it to the pr id after creating the pr
    • ensure type is one of the following:
      • .feature: Signifying a new feature.
      • .bugfix: Signifying a bug fix.
      • .doc: Signifying a documentation improvement.
      • .removal: Signifying a deprecation or removal of public API.
      • .misc: A ticket has been closed, but it is not of interest to users.
    • Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files."

@hellysmile
Copy link
Member Author

hellysmile commented Oct 12, 2017

Here is example of AccessLogger which produces messages exactly same like current aiohttp.helpers.AccessLogger with default log_format

class AccessLogger(AbstractAccessLogger):

    def log(self, request, response, time):
        msg = '{request.remote} - - {t} "{r}" {response.status} {response.body_length} "{referrer}" "{user_agent}"'

        r = '%s %s HTTP/%s.%s' % tuple((request.method,
                                        request.path_qs) + request.version)

        t = datetime.datetime.utcnow().strftime('[%d/%b/%Y:%H:%M:%S +0000]')

        self.logger.info(msg.format(
            t=t,
            r=r,
            referrer=request.headers.get('Referrer', '-'),
            user_agent=request.headers.get('User-Agent', '-'),
            request=request,
            response=response,
            time=time,
        ))

I've played a bit with this AccessLogger, and server can be speeded up to 5% in comparison to the default aiohttp.helpers.AccessLogger, but it is not big deal.

Most useful example is something like:

class AccessLogger(AbstractAccessLogger):

    def log(self, request, response, time):
        self.logger.info(f'{request.remote} "{request.method}" -> {response.status}')

If proposal at least makes some sense, I'll do full test coverage/docs/etc

@codecov-io
Copy link

codecov-io commented Oct 12, 2017

Codecov Report

Merging #2316 into master will increase coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2316      +/-   ##
==========================================
+ Coverage   97.24%   97.24%   +<.01%     
==========================================
  Files          39       39              
  Lines        8195     8204       +9     
  Branches     1435     1436       +1     
==========================================
+ Hits         7969     7978       +9     
  Misses         98       98              
  Partials      128      128
Impacted Files Coverage Δ
aiohttp/helpers.py 97.27% <100%> (ø) ⬆️
aiohttp/web.py 99.66% <100%> (ø) ⬆️
aiohttp/web_protocol.py 88.27% <100%> (ø) ⬆️
aiohttp/abc.py 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4ffdb3a...db02580. Read the comment docs.

@asvetlov
Copy link
Member

Please make the feature well documented

@@ -83,6 +83,7 @@ def __init__(self, manager, *, loop=None,
tcp_keepalive=True,
slow_request_timeout=None,
logger=server_logger,
access_log_factory=helpers.AccessLogger,
Copy link
Member

Choose a reason for hiding this comment

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

It's not a factory, but a class. Otherwise need to document what factory should return - I think, that's abstraction level is redundant.

Copy link
Member Author

@hellysmile hellysmile Oct 12, 2017

Choose a reason for hiding this comment

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

What about access_log_cls or access_log_class ?

Copy link
Member

Choose a reason for hiding this comment

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

access_log_class is good one.

class AbstractAccessLogger(ABC):

def __init__(self, logger, log_format):
self.logger = logger
Copy link
Member

Choose a reason for hiding this comment

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

log_format seems to be lost.

Copy link
Member Author

@hellysmile hellysmile Oct 12, 2017

Choose a reason for hiding this comment

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

Yes, but it is exactly how current AccessLogger works, as well default log_format is AccessLogger.LOG_FORMAT not sure is it needs to be stored as self.log_format. My idea was that, logger itself is only one required attribute, log_format is actually optional

Copy link
Member

Choose a reason for hiding this comment

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

So, if I'm making own AccessLogger I should keep in mind, that super requires two arguments, one of which it will set to attributes and one that it will ignore? A bit not friendly behavior. The AccessLogger may have overloaded __init__ to accept log_format, but if the AbstractAccessLogger doesn't uses it in anyway there is no point to keep it there.

aiohttp/abc.py Outdated
self.logger = logger

@abstractmethod
def log(self, request, response, time):
Copy link
Member

Choose a reason for hiding this comment

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

Need to document which objects and types that method accepts.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, it will be documented

@hellysmile
Copy link
Member Author

hellysmile commented Oct 12, 2017

@kxepal What do You think to make self._log @abstractmethod and self.log will be:

def log(self, request, response, time):
    try:
        self._log(request, response, time)
    except Exception as e:
        self.logger.exception("Error in logging", exc_info=e)

@kxepal
Copy link
Member

kxepal commented Oct 12, 2017

@hellysmile
Good idea if we don't want to let logger crash the request. Though, it may hide errors for awhile. Also, no need to pass exc_info for logger.exception - it already catches it by default.

@hellysmile
Copy link
Member Author

hellysmile commented Oct 12, 2017

There is one more question, it will be nice to place somewhere

assert issubclass(access_log_class, AbstractAccessLogger), access_log_class

but where to place is, actually access_logger will be created lazy on first request

@asvetlov
Copy link
Member

@hellysmile push the check into app.make_handler().

@hellysmile hellysmile changed the title aiohttp.wep.Application.make_handler access_log_factory support aiohttp.wep.Application.make_handler access_log_class support Oct 12, 2017
aiohttp/web.py Outdated
@@ -231,6 +231,14 @@ def middlewares(self):
return self._middlewares

def make_handler(self, *, loop=None, **kwargs):
if 'access_log_class' in kwargs:
Copy link
Member

Choose a reason for hiding this comment

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

Just add access_log_class=helpers.AccessLogger into method signature.

aiohttp/web.py Outdated
if 'access_log_class' in kwargs:
access_log_class = kwargs['access_log_class']

assert issubclass(access_log_class, AbstractAccessLogger), (
Copy link
Member

Choose a reason for hiding this comment

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

Should be TypeError, not AssertionError.

aiohttp/abc.py Outdated
"""Emit log to logger"""

try:
self._log(request, response, time)
Copy link
Member

Choose a reason for hiding this comment

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

Let's drop _log() and make log() abstract method.
On one hand formatting logic always should be wrapped by catching exception obviously.
On other -- let's not make any decision about implementation details.
Implementor might want to do other things than just registering caught exception.
Let's keep ABC very abstract, my guts feel that there are many contradictory might occur if we will bake a code in abstract class.

aiohttp/abc.py Outdated
def _log(self, request, response, time):
"""

:param request: Request object.
Copy link
Member

Choose a reason for hiding this comment

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

Please don't use sphinx formatting in docstrings -- it is a text for readers.
I'd like to rip it out everywhere and use only hand written documentation.

asvetlov
asvetlov previously approved these changes Oct 13, 2017
Copy link
Member

@asvetlov asvetlov left a comment

Choose a reason for hiding this comment

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

LGTM
@kxepal ?

Copy link
Member

@kxepal kxepal left a comment

Choose a reason for hiding this comment

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

One little bit about error message. The rest looks fine.

aiohttp/web.py Outdated

if not issubclass(access_log_class, AbstractAccessLogger):
raise TypeError('access_log_class must be subclass of '
'aiohttp.abc.AbstractAccessLogger')
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice to add got {} to error message to simplify understanding what's wrong with the logger.

Copy link
Member

@kxepal kxepal left a comment

Choose a reason for hiding this comment

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

Cool, thanks!

@asvetlov asvetlov merged commit e11c82a into aio-libs:master Oct 13, 2017
@lock
Copy link

lock bot commented Oct 28, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a [new issue] for related bugs.
If you feel like there's important points made in this discussion, please include those exceprts into that [new issue].
[new issue]: https://github.com/aio-libs/aiohttp/issues/new

@lock lock bot added the outdated label Oct 28, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 28, 2019
@psf-chronographer psf-chronographer bot added the bot:chronographer:provided There is a change note present in this PR label Oct 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bot:chronographer:provided There is a change note present in this PR outdated
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants