|
4 | 4 | import base64
|
5 | 5 | import binascii
|
6 | 6 | import cgi
|
7 |
| -import datetime |
8 | 7 | import functools
|
9 | 8 | import inspect
|
10 |
| -import logging |
11 | 9 | import netrc
|
12 | 10 | import os
|
13 | 11 | import re
|
|
19 | 17 | from math import ceil
|
20 | 18 | from pathlib import Path
|
21 | 19 | from types import TracebackType
|
22 |
| -from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, |
23 |
| - List, Mapping, Optional, Pattern, Tuple, Type, TypeVar, |
24 |
| - Union, cast) |
| 20 | +from typing import (Any, Callable, Dict, Iterable, Iterator, List, # noqa |
| 21 | + Mapping, Optional, Pattern, Tuple, Type, TypeVar, Union, |
| 22 | + cast) |
25 | 23 | from urllib.parse import quote
|
26 | 24 | from urllib.request import getproxies
|
27 | 25 |
|
|
31 | 29 | from yarl import URL
|
32 | 30 |
|
33 | 31 | from . import hdrs
|
34 |
| -from .abc import AbstractAccessLogger |
35 | 32 | from .log import client_logger
|
36 | 33 | from .typedefs import PathLike # noqa
|
37 | 34 |
|
|
51 | 48 | from typing_extensions import ContextManager
|
52 | 49 |
|
53 | 50 |
|
54 |
| -if TYPE_CHECKING: # pragma: no cover |
55 |
| - # run in mypy mode only to prevent circular imports |
56 |
| - from .web_request import BaseRequest # noqa |
57 |
| - from .web_response import StreamResponse # noqa |
58 |
| - |
59 |
| - |
60 | 51 | _T = TypeVar('_T')
|
61 | 52 |
|
62 | 53 |
|
|
67 | 58 | # for compatibility with older versions
|
68 | 59 | DEBUG = (getattr(sys.flags, 'dev_mode', False) or
|
69 | 60 | (not sys.flags.ignore_environment and
|
70 |
| - bool(os.environ.get('PYTHONASYNCIODEBUG')))) |
| 61 | + bool(os.environ.get('PYTHONASYNCIODEBUG')))) # type: bool |
71 | 62 |
|
72 | 63 |
|
73 | 64 | CHAR = set(chr(i) for i in range(0, 128))
|
@@ -323,231 +314,6 @@ def content_disposition_header(disptype: str,
|
323 | 314 | return value
|
324 | 315 |
|
325 | 316 |
|
326 |
| -KeyMethod = namedtuple('KeyMethod', 'key method') |
327 |
| - |
328 |
| - |
329 |
| -class AccessLogger(AbstractAccessLogger): |
330 |
| - """Helper object to log access. |
331 |
| -
|
332 |
| - Usage: |
333 |
| - log = logging.getLogger("spam") |
334 |
| - log_format = "%a %{User-Agent}i" |
335 |
| - access_logger = AccessLogger(log, log_format) |
336 |
| - access_logger.log(request, response, time) |
337 |
| -
|
338 |
| - Format: |
339 |
| - %% The percent sign |
340 |
| - %a Remote IP-address (IP-address of proxy if using reverse proxy) |
341 |
| - %t Time when the request was started to process |
342 |
| - %P The process ID of the child that serviced the request |
343 |
| - %r First line of request |
344 |
| - %s Response status code |
345 |
| - %b Size of response in bytes, including HTTP headers |
346 |
| - %T Time taken to serve the request, in seconds |
347 |
| - %Tf Time taken to serve the request, in seconds with floating fraction |
348 |
| - in .06f format |
349 |
| - %D Time taken to serve the request, in microseconds |
350 |
| - %{FOO}i request.headers['FOO'] |
351 |
| - %{FOO}o response.headers['FOO'] |
352 |
| - %{FOO}e os.environ['FOO'] |
353 |
| -
|
354 |
| - """ |
355 |
| - LOG_FORMAT_MAP = { |
356 |
| - 'a': 'remote_address', |
357 |
| - 't': 'request_start_time', |
358 |
| - 'P': 'process_id', |
359 |
| - 'r': 'first_request_line', |
360 |
| - 's': 'response_status', |
361 |
| - 'b': 'response_size', |
362 |
| - 'T': 'request_time', |
363 |
| - 'Tf': 'request_time_frac', |
364 |
| - 'D': 'request_time_micro', |
365 |
| - 'i': 'request_header', |
366 |
| - 'o': 'response_header', |
367 |
| - } |
368 |
| - |
369 |
| - LOG_FORMAT = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"' |
370 |
| - FORMAT_RE = re.compile(r'%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbOD]|Tf?)') |
371 |
| - CLEANUP_RE = re.compile(r'(%[^s])') |
372 |
| - _FORMAT_CACHE = {} # type: Dict[str, Tuple[str, List[KeyMethod]]] |
373 |
| - |
374 |
| - def __init__(self, logger: logging.Logger, |
375 |
| - log_format: str=LOG_FORMAT) -> None: |
376 |
| - """Initialise the logger. |
377 |
| -
|
378 |
| - logger is a logger object to be used for logging. |
379 |
| - log_format is an string with apache compatible log format description. |
380 |
| -
|
381 |
| - """ |
382 |
| - super().__init__(logger, log_format=log_format) |
383 |
| - |
384 |
| - _compiled_format = AccessLogger._FORMAT_CACHE.get(log_format) |
385 |
| - if not _compiled_format: |
386 |
| - _compiled_format = self.compile_format(log_format) |
387 |
| - AccessLogger._FORMAT_CACHE[log_format] = _compiled_format |
388 |
| - |
389 |
| - self._log_format, self._methods = _compiled_format |
390 |
| - |
391 |
| - def compile_format(self, log_format: str) -> Tuple[str, List[KeyMethod]]: |
392 |
| - """Translate log_format into form usable by modulo formatting |
393 |
| -
|
394 |
| - All known atoms will be replaced with %s |
395 |
| - Also methods for formatting of those atoms will be added to |
396 |
| - _methods in appropriate order |
397 |
| -
|
398 |
| - For example we have log_format = "%a %t" |
399 |
| - This format will be translated to "%s %s" |
400 |
| - Also contents of _methods will be |
401 |
| - [self._format_a, self._format_t] |
402 |
| - These method will be called and results will be passed |
403 |
| - to translated string format. |
404 |
| -
|
405 |
| - Each _format_* method receive 'args' which is list of arguments |
406 |
| - given to self.log |
407 |
| -
|
408 |
| - Exceptions are _format_e, _format_i and _format_o methods which |
409 |
| - also receive key name (by functools.partial) |
410 |
| -
|
411 |
| - """ |
412 |
| - # list of (key, method) tuples, we don't use an OrderedDict as users |
413 |
| - # can repeat the same key more than once |
414 |
| - methods = list() |
415 |
| - |
416 |
| - for atom in self.FORMAT_RE.findall(log_format): |
417 |
| - if atom[1] == '': |
418 |
| - format_key1 = self.LOG_FORMAT_MAP[atom[0]] |
419 |
| - m = getattr(AccessLogger, '_format_%s' % atom[0]) |
420 |
| - key_method = KeyMethod(format_key1, m) |
421 |
| - else: |
422 |
| - format_key2 = (self.LOG_FORMAT_MAP[atom[2]], atom[1]) |
423 |
| - m = getattr(AccessLogger, '_format_%s' % atom[2]) |
424 |
| - key_method = KeyMethod(format_key2, |
425 |
| - functools.partial(m, atom[1])) |
426 |
| - |
427 |
| - methods.append(key_method) |
428 |
| - |
429 |
| - log_format = self.FORMAT_RE.sub(r'%s', log_format) |
430 |
| - log_format = self.CLEANUP_RE.sub(r'%\1', log_format) |
431 |
| - return log_format, methods |
432 |
| - |
433 |
| - @staticmethod |
434 |
| - def _format_i(key: str, |
435 |
| - request: 'BaseRequest', |
436 |
| - response: 'StreamResponse', |
437 |
| - time: float) -> str: |
438 |
| - if request is None: |
439 |
| - return '(no headers)' |
440 |
| - |
441 |
| - # suboptimal, make istr(key) once |
442 |
| - return request.headers.get(key, '-') |
443 |
| - |
444 |
| - @staticmethod |
445 |
| - def _format_o(key: str, |
446 |
| - request: 'BaseRequest', |
447 |
| - response: 'StreamResponse', |
448 |
| - time: float) -> str: |
449 |
| - # suboptimal, make istr(key) once |
450 |
| - return response.headers.get(key, '-') |
451 |
| - |
452 |
| - @staticmethod |
453 |
| - def _format_a(request: 'BaseRequest', |
454 |
| - response: 'StreamResponse', |
455 |
| - time: float) -> str: |
456 |
| - if request is None: |
457 |
| - return '-' |
458 |
| - ip = request.remote |
459 |
| - return ip if ip is not None else '-' |
460 |
| - |
461 |
| - @staticmethod |
462 |
| - def _format_t(request: 'BaseRequest', |
463 |
| - response: 'StreamResponse', |
464 |
| - time: float) -> str: |
465 |
| - now = datetime.datetime.utcnow() |
466 |
| - start_time = now - datetime.timedelta(seconds=time) |
467 |
| - return start_time.strftime('[%d/%b/%Y:%H:%M:%S +0000]') |
468 |
| - |
469 |
| - @staticmethod |
470 |
| - def _format_P(request: 'BaseRequest', |
471 |
| - response: 'StreamResponse', |
472 |
| - time: float) -> str: |
473 |
| - return "<%s>" % os.getpid() |
474 |
| - |
475 |
| - @staticmethod |
476 |
| - def _format_r(request: 'BaseRequest', |
477 |
| - response: 'StreamResponse', |
478 |
| - time: float) -> str: |
479 |
| - if request is None: |
480 |
| - return '-' |
481 |
| - return '%s %s HTTP/%s.%s' % (request.method, request.path_qs, |
482 |
| - request.version.major, |
483 |
| - request.version.minor) |
484 |
| - |
485 |
| - @staticmethod |
486 |
| - def _format_s(request: 'BaseRequest', |
487 |
| - response: 'StreamResponse', |
488 |
| - time: float) -> str: |
489 |
| - return response.status |
490 |
| - |
491 |
| - @staticmethod |
492 |
| - def _format_b(request: 'BaseRequest', |
493 |
| - response: 'StreamResponse', |
494 |
| - time: float) -> str: |
495 |
| - return response.body_length |
496 |
| - |
497 |
| - @staticmethod |
498 |
| - def _format_T(request: 'BaseRequest', |
499 |
| - response: 'StreamResponse', |
500 |
| - time: float) -> str: |
501 |
| - return str(round(time)) |
502 |
| - |
503 |
| - @staticmethod |
504 |
| - def _format_Tf(request: 'BaseRequest', |
505 |
| - response: 'StreamResponse', |
506 |
| - time: float) -> str: |
507 |
| - return '%06f' % time |
508 |
| - |
509 |
| - @staticmethod |
510 |
| - def _format_D(request: 'BaseRequest', |
511 |
| - response: 'StreamResponse', |
512 |
| - time: float) -> str: |
513 |
| - return str(round(time * 1000000)) |
514 |
| - |
515 |
| - def _format_line(self, |
516 |
| - request: 'BaseRequest', |
517 |
| - response: 'StreamResponse', |
518 |
| - time: float) -> Iterable[Tuple[str, |
519 |
| - Callable[['BaseRequest', |
520 |
| - 'StreamResponse', |
521 |
| - float], |
522 |
| - str]]]: |
523 |
| - return [(key, method(request, response, time)) |
524 |
| - for key, method in self._methods] |
525 |
| - |
526 |
| - def log(self, |
527 |
| - request: 'BaseRequest', |
528 |
| - response: 'StreamResponse', |
529 |
| - time: float) -> None: |
530 |
| - try: |
531 |
| - fmt_info = self._format_line(request, response, time) |
532 |
| - |
533 |
| - values = list() |
534 |
| - extra = dict() |
535 |
| - for key, value in fmt_info: |
536 |
| - values.append(value) |
537 |
| - |
538 |
| - if key.__class__ is str: |
539 |
| - extra[key] = value |
540 |
| - else: |
541 |
| - k1, k2 = key |
542 |
| - dct = extra.get(k1, {}) |
543 |
| - dct[k2] = value # type: ignore |
544 |
| - extra[k1] = dct # type: ignore |
545 |
| - |
546 |
| - self.logger.info(self._log_format % tuple(values), extra=extra) |
547 |
| - except Exception: |
548 |
| - self.logger.exception("Error in logging") |
549 |
| - |
550 |
| - |
551 | 317 | class reify:
|
552 | 318 | """Use as a class method decorator. It operates almost exactly like
|
553 | 319 | the Python `@property` decorator, but it puts the result of the
|
|
0 commit comments