{"id":385114,"date":"2024-06-29T05:54:59","date_gmt":"2024-06-29T05:54:59","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=385114"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=385114","title":{"rendered":"<span>\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 JSON-Logger \u0434\u043b\u044f FastAPI<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442. <\/p>\n<p>\u0426\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0438 \u043c\u043e\u0435\u0439 \u043b\u0438\u0447\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 &#8212; \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u044f \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 json-\u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0440\u0430\u0431\u043e\u0442\u0430\u044e, \u043d\u043e \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043c\u043d\u0435 \u0432\u043e\u043f\u043b\u043e\u0449\u0430\u0442\u044c \u043e\u0447\u0435\u043d\u044c \u0433\u0438\u0431\u043a\u043e \u0438 \u0443\u0434\u043e\u0431\u043d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0435\u0449\u0438 \u0441 \u0435\u0434\u0438\u043d\u044b\u043c \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043e\u043b\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u043b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0442\u0435\u043a\u0430\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u0430 \u0442\u0430\u043a\u0436\u0435: \u043d\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u043e \u0431\u044b \u043c\u0435\u043d\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c \u043e\u0434\u043d\u043e\u0439-\u0434\u0432\u0443\u043c\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c.<\/p>\n<p>\u041f\u043e\u0448\u0435\u043b \u0438\u0441\u043a\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432 \u043b\u043e\u0431: \u0438\u0441\u043a\u0430\u043b \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. \u041e\u0442\u044b\u0441\u043a\u0430\u043b \u043f\u0430\u0440\u0443-\u0442\u0440\u043e\u0439\u043a\u0443 \u0433\u043e\u0434\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430<a href=\"https:\/\/awesomeopensource.com\/project\/bobbui\/json-logging-python\" rel=\"noopener noreferrer nofollow\"> json-logging<\/a>. \u0412\u0441\u0451 \u0445\u043e\u0440\u043e\u0448\u043e: \u043f\u043e\u0447\u0438\u0442\u0430\u043b, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u043d\u0435 \u0433\u043b\u044f\u0434\u044f. \u0414\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u043c\u0430\u0441\u0441\u0443 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432: <\/p>\n<ul>\n<li>\n<p>\u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 <em>FastAPI, Flask, Aiohttp, Sanic,<\/em> <\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 <em>uvicorn access log<\/em>, <\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 JSON \u0444\u043e\u0440\u043c\u0430\u0442. <\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u044b\u0439 \u043b\u043e\u0433 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043b\u044f\u043c\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u043b \u0432 \u0441\u0435\u0440\u0432\u0438\u0441:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{ \"type\": \"request\", \"written_at\": \"2017-12-23T16:55:37.280Z\", \"written_ts\": 1514048137280721000, \"component_id\": \"-\", \"component_name\": \"-\", \"component_instance_idx\": 0, \"correlation_id\": \"1975a02e-e802-11e7-8971-28b2bd90b19a\", \"remote_user\": \"user_a\", \"request\": \"\/index.html\", \"referer\": \"-\", \"x_forwarded_for\": \"-\", \"protocol\": \"HTTP\/1.1\", \"method\": \"GET\", \"remote_ip\": \"127.0.0.1\", \"request_size_b\": 1234, \"remote_host\": \"127.0.0.1\", \"remote_port\": 50160, \"request_received_at\": \"2017-12-23T16:55:37.280Z\", \"response_time_ms\": 0, \"response_status\": 200, \"response_size_b\": \"122\", \"response_content_type\": \"text\/html; charset=utf-8\", \"response_sent_at\": \"2017-12-23T16:55:37.280Z\" }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041d\u043e \u0443 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b\u0438\u0441\u044c \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u0435\u0439 \u0432 \u043b\u043e\u0433\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f \u0431\u0435\u0437 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f Formatter \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044e. <\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u043b\u044c\u0437\u044f \u043d\u0438\u043a\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430 \u0431\u0435\u0437 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e <strong>Middleware<\/strong> \u0434\u043b\u044f <strong>FastAPI<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442 \u0435\u0449\u0435 \u0438 \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u043b\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u0432\u044b\u0432\u043e\u0434\u0430 \u043b\u043e\u0433\u0433\u0435\u0440\u0430 <code>uvicorn.access<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>\u042f \u043d\u0435 \u0431\u0443\u0434\u0443 \u043f\u0438\u0441\u0430\u0442\u044c, \u043a\u0430\u043a \u044f \u043f\u044b\u0442\u0430\u043b\u0441\u044f <s>\u0437\u0430\u043a\u043e\u0441\u0442\u044b\u043b\u0438\u0442\u044c<\/s> \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0435 \u0434\u0432\u0430 \u043c\u0438\u043d\u0443\u0441\u0430, \u043f\u043e\u043a\u0430 \u043d\u0435 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u043c\u043d\u0435 \u0432\u0441\u0451-\u0442\u0430\u043a\u0438 \u043d\u0443\u0436\u0435\u043d \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043b\u043e\u0433\u0435\u0440. \u042f \u043b\u0443\u0447\u0448\u0435 \u0412\u0430\u043c \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u044f \u0440\u0435\u0448\u0438\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435.<br \/>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u044f \u0440\u0435\u0448\u0438\u043b \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441\u0440\u0430\u0437\u0443:<\/p>\n<pre><code class=\"bash\">pip uninstall json-logging<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u044f \u0445\u043e\u0442\u0435\u043b \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0436\u0443\u0440\u043d\u0430\u043b\u0430. \u041c\u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u043c\u043e\u0433 <a href=\"https:\/\/pydantic-docs.helpmanual.io\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u0434\u043b\u044f \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0433\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u043e\u043f\u0440\u043e\u0441\u0430:<\/p>\n<pre><code class=\"python\">from typing import Union  from pydantic import BaseModel, Field   class BaseJsonLogSchema(BaseModel):     \"\"\"     \u0421\u0445\u0435\u043c\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0442\u0435\u043b\u0430 \u043b\u043e\u0433\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON     \"\"\"     thread: Union[int, str]     level: int     level_name: str     message: str     source: str     timestamp: str = Field(..., alias='@timestamp')     app_name: str     app_version: str     app_env: str     duration: int     exceptions: Union[list[str], str] = None     trace_id: str = None     span_id: str = None     parent_id: str = None      class Config:         allow_population_by_field_name = True   class RequestJsonLogSchema(BaseModel):     \"\"\"     \u0421\u0445\u0435\u043c\u0430 \u0447\u0430\u0441\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432-\u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043b\u043e\u0433\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON     \"\"\"     request_uri: str     request_referer: str     request_protocol: str     request_method: str     request_path: str     request_host: str     request_size: int     request_content_type: str     request_headers: str     request_body: str     request_direction: str     remote_ip: str     remote_port: str     response_status_code: int     response_size: int     response_headers: str     response_body: str     duration: int<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0431\u044b\u043b\u0430 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0430\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0424\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u0430 <strong>JSONLogFormatter<\/strong> \u0441 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e <strong>logging.Formatter<\/strong>.<br \/> \u041a\u043e\u0434 <code>test_project.utils.json_logger.py<\/code><\/p>\n<pre><code class=\"python\">import datetime import json import logging import traceback  from test_project.main.settings import APP_NAME, \\     APP_VERSION, ENVIRONMENT from test_project.utils.utils_schemas import BaseJsonLogSchema   class JSONLogFormatter(logging.Formatter):     \"\"\"     \u041a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441-\u0444\u043e\u0440\u043c\u0430\u0442\u0435\u0440 \u0434\u043b\u044f \u043b\u043e\u0433\u043e\u0432 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 json     \"\"\"      def format(self, record: logging.LogRecord, *args, **kwargs) -> str:         \"\"\"         \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0430 \u0432 json          :param record: \u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         :return: \u0441\u0442\u0440\u043e\u043a\u0430 \u0436\u0443\u0440\u043d\u0430\u043b\u0430 \u0432 JSON \u0444\u043e\u0440\u043c\u0430\u0442\u0435         \"\"\"         log_object: dict = self._format_log_object(record)         return json.dumps(log_object, ensure_ascii=False)      @staticmethod     def _format_log_object(record: logging.LogRecord) -> dict:         \"\"\"         \u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u0430\u043f\u0438\u0441\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         \u0432 json \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c \u043f\u0435\u0440\u0435\u0447\u043d\u0435\u043c \u043f\u043e\u043b\u0435\u0439          :param record: \u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         :return: \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         \"\"\"         now = datetime \\             .datetime \\             .fromtimestamp(record.created) \\             .astimezone() \\             .replace(microsecond=0) \\             .isoformat()         message = record.getMessage()         duration = record.duration \\             if hasattr(record, 'duration') \\             else record.msecs         # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0435\u043b\u0430 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         json_log_fields = BaseJsonLogSchema(             thread=record.process,             timestamp=now,             level=record.levelno,             level_name=LEVEL_TO_NAME[record.levelno],             message=message,             source=record.name,             duration=duration,             app_name=APP_NAME,             app_version=APP_VERSION,             app_env=ENVIRONMENT,         )          if hasattr(record, 'props'):             json_log_fields.props = record.props          if record.exc_info:             json_log_fields.exceptions = \\                 traceback.format_exception(*record.exc_info)          elif record.exc_text:             json_log_fields.exceptions = record.exc_text         # \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Pydantic \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c         json_log_object = json_log_fields.dict(             exclude_unset=True,             by_alias=True,         )         # \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f         if hasattr(record, 'request_json_fields'):             json_log_object.update(record.request_json_fields)          return json_log_object<\/code><\/pre>\n<p>\u041d\u0443\u0436\u043d\u043e \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c FastApi \u043b\u043e\u0432\u0438\u0442\u044c request \u0438 response, \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441 \u043d\u0430\u043b\u0438\u0447\u0438\u0435\u043c body. \u042f \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a dependency \u043f\u043e\u0434\u0445\u043e\u0434\u0443 FastApi. \u041d\u043e \u043f\u043e\u043d\u044f\u043b, \u0447\u0442\u043e \u043d\u0435 \u0445\u043e\u0447\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043c\u0435\u0442\u043e\u0434 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043a \u043d\u0430\u043c \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 <a href=\"https:\/\/fastapi.tiangolo.com\/tutorial\/middleware\/\" rel=\"noopener noreferrer nofollow\">Middleware<\/a>. <br \/>\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u0432 <strong>app<\/strong>-\u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u043e\u0442\u0432\u0435\u0442\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0445 \u043f\u043e\u043b\u0435\u0439: status code, headers, remote ip, protocol, etc. \u041a\u043b\u0430\u0441\u0441 \u044f \u043d\u0430\u0437\u0432\u0430\u043b <strong>LoggingMiddleware <\/strong>\u0438 \u0432\u0441\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043c\u0435\u0442\u043e\u0434\u0443 <code>async def __call__()<\/code><\/p>\n<p>\u041a\u043e\u0434 <code>test_project.utils.middlewares.py<\/code><\/p>\n<pre><code class=\"python\">from test_project.main.settings import PORT from test_project.utils.json_logger import EMPTY_VALUE from test_project.utils.utils_schemas import RequestJsonLogSchema   class LoggingMiddleware:     \"\"\"     Middleware \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0441 \u0446\u0435\u043b\u044c\u044e \u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f     \"\"\"     @staticmethod     async def get_protocol(request: Request) -> str:         protocol = str(request.scope.get('type', ''))         http_version = str(request.scope.get('http_version', ''))         if protocol.lower() == 'http' and http_version:             return f'{protocol.upper()}\/{http_version}'         return EMPTY_VALUE      @staticmethod     async def set_body(request: Request, body: bytes) -> None:         async def receive() -> Message:             return {'type': 'http.request', 'body': body}          request._receive = receive      async def get_body(self, request: Request) -> bytes:         body = await request.body()         await self.set_body(request, body)         return body      async def __call__(             self, request: Request, call_next: RequestResponseEndpoint,             *args, **kwargs     ):         start_time = time.time()         exception_object = None         # Request Side         try:             raw_request_body = await request.body()             # \u041f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0443\u0436\u043d\u044b,              # \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u0442\u0435\u0440\u0435\u0442\u044c \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 # \u0438 \u043d\u0435 \u0443\u0439\u0442\u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0430\u043d\u0438\u0435 event-loop'a             # \u043f\u0440\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0442\u0435\u043b\u0430 \u043e\u0442\u0432\u0435\u0442\u0430             await self.set_body(request, raw_request_body)             raw_request_body = await self.get_body(request)             request_body = raw_request_body.decode()         except Exception:             request_body = EMPTY_VALUE          server: tuple = request.get('server', ('localhost', PORT))         request_headers: dict = dict(request.headers.items())         # Response Side         try:             response = await call_next(request)         except Exception as ex:             response_body = bytes(                 http.HTTPStatus.INTERNAL_SERVER_ERROR.phrase.encode()             )             response = Response(                 content=response_body,                 status_code=http.HTTPStatus.INTERNAL_SERVER_ERROR.real,             )             exception_object = ex             response_headers = {}         else:             response_headers = dict(response.headers.items())             response_body = b''             async for chunk in response.body_iterator:                 response_body += chunk             response = Response(                 content=response_body,                 status_code=response.status_code,                 headers=dict(response.headers),                 media_type=response.media_type             )         duration: int = math.ceil((time.time() - start_time) * 1000) # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430-\u043e\u0442\u0432\u0435\u0442\u0430         request_json_fields = RequestJsonLogSchema(             request_uri=str(request.url),             request_referer=request_headers.get('referer', EMPTY_VALUE),             request_protocol=await self.get_protocol(request),             request_method=request.method,             request_path=request.url.path,             request_host=f'{server[0]}:{server[1]}',             request_size=int(request_headers.get('content-length', 0)),             request_content_type=request_headers.get(                 'content-type', EMPTY_VALUE),             request_headers=json.dumps(request_headers),             request_body=request_body,             request_direction='in',             remote_ip=request.client[0],             remote_port=request.client[1],             response_status_code=response.status_code,             response_size=int(response_headers.get('content-length', 0)),             response_headers=json.dumps(response_headers),             response_body=response_body.decode(),             duration=duration         ).dict()         # \u0425\u043e\u0447\u0435\u0442\u0441\u044f \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0447\u0438\u0442\u0430\u0442\u044c          # \u0438 \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0432 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0438 \u0441\u0430\u043c\u043e\u0435 \u0433\u043b\u0430\u0432\u043d\u043e\u0435,          # \u043f\u043e\u044d\u0442\u043e\u043c\u0443 message \u043c\u044b \u0441\u0440\u0430\u0437\u0443 \u0434\u0435\u043b\u0430\u0435\u043c \u0442\u0438\u043f\u043e\u0432\u044b\u043c         message = f'{\"\u041e\u0448\u0438\u0431\u043a\u0430\" if exception_object else \"\u041e\u0442\u0432\u0435\u0442\"} ' \\                   f'\u0441 \u043a\u043e\u0434\u043e\u043c {response.status_code} ' \\                   f'\u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 {request.method} \\\"{str(request.url)}\\\", ' \\                   f'\u0437\u0430 {duration} \u043c\u0441'         logger.info(             message,             extra={                 'request_json_fields': request_json_fields,                 'to_mask': True,             },             exc_info=exception_object,         )         return response<\/code><\/pre>\n<p>\u0423 \u043c\u0435\u043d\u044f \u0435\u0441\u0442\u044c \u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0441\u0442\u044c \u043f\u043e \u043f\u043e\u0432\u043e\u0434\u0443 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 FastApi. \u041c\u0435\u0442\u043e\u0434\u044b \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e\u0442\u043e\u043a\u0430 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c, \u0437\u0430\u043f\u0438\u0441\u044c \u0432 \u0444\u0430\u0439\u043b, logstash &#8212; \u044d\u0442\u043e \u0442\u043e\u0442 \u0436\u0435 I\/O \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043a\u0430\u043a \u0438 \u0441\u0433\u043e\u043d\u044f\u0442\u044c \u043f\u043e \u0441\u0435\u0442\u043a\u0435 \u0432 \u0411\u0414 \u0438\u043b\u0438 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441, \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u044b? \u0410 \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e I\/O \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u0442\u043e \u0432 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c FastApi \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u043d\u043e \u043d\u0435 \u0437\u0430\u043c\u0435\u0434\u043b\u044f\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0448\u0442\u0443\u043a\u043e\u0439 \u043a\u0430\u043a \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u042f \u043d\u0430\u0448\u0435\u043b \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0440\u043e\u0448\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u0430 \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043f\u0435\u0440\u0435\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u0432\u044b\u0432\u043e\u0434 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0432 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c (\u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0438 \u0441 \u0444\u0430\u0439\u043b\u0430\u043c\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0432 logstash, \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b). \u0414\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f: <a href=\"https:\/\/pypi.org\/project\/asynclog\/\" rel=\"noopener noreferrer nofollow\"><em>asynclog<\/em><\/a><em>. <\/em>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u041d\u0435 \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u0438 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c handler-\u044b: \u041d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0437\u0430\u0434\u0430\u0442\u044c handler.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043b\u043e\u0433\u0435\u0440\u0430 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u0435\u0437\u0434\u0435. \u0411\u043b\u0430\u0433\u043e, \u0432 python \u0435\u0441\u0442\u044c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u043f\u043b\u043e\u0442\u0438\u0442\u044c \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<pre><code class=\"python\">LOG_CONFIG = {     'version': 1,     'disable_existing_loggers': False,     'formatters': {         'json': {             '()': 'test_project.utils.json_logger.JSONLogFormatter',         },     },     'handlers': { # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c AsyncLogDispatcher \u0434\u043b\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e\u0442\u043e\u043a\u0430.          'json': {             'formatter': 'json',             'class': 'asynclog.AsyncLogDispatcher',             'func': 'test_project.utils.json_logger.write_log',         },     },     'loggers': {         'test_project': {             'handlers': ['json'],             'level': 'DEBUG' if DEBUG else 'INFO',             'propagate': False,         },         'uvicorn': {             'handlers': ['json'],             'level': 'INFO',             'propagate': False,         },       # \u041d\u0435 \u0434\u0430\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u043c\u0443 \u043b\u043e\u0433\u0433\u0435\u0440\u0443 fastapi \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u043e \u043f\u0443\u0441\u0442\u044f\u043a\u0430\u043c \u0438 \u0437\u0430\u043c\u0435\u0434\u043b\u044f\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430         'uvicorn.access': {             'handlers': ['json'],             'level': 'ERROR',             'propagate': False,         },     }, }<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0443 handler \u0435\u0441\u0442\u044c \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0432 \u043f\u043e\u043b\u0435 func, \u0442\u0443\u0442 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e\u0442\u043e\u043a\u0430. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c stdout, print, write file. \u0427\u0442\u043e \u0443\u0433\u043e\u0434\u043d\u043e, \u0432\u043f\u043b\u043e\u0442\u044c \u0434\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0444\u0430\u0439\u043b\u0430 \u043f\u043e \u0441\u0435\u0442\u0438 \u0432 logstash. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044f \u0440\u0435\u0448\u0438\u043b \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0432\u0435\u0441\u0442\u0438 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430. <br \/>\u041a\u043e\u0434 <code>test_project.utils.json_logger.py:<\/code><\/p>\n<pre><code class=\"python\">def write_log(msg):     print(msg)<\/code><\/pre>\n<p>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c middleware, \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c logging \u0438 \u0435\u0433\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e. \u0414\u0430\u043b\u0435\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e route \u0441 \u043b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f &#8216;Test log&#8217;.<\/p>\n<pre><code class=\"python\">import logging from logging.config import dictConfig  from fastapi import FastAPI from test_project.utils.middlewares import LoggingMiddleware dictConfig(LOG_CONFIG) logger = logging.getLogger(name) app = FastAPI() app.middleware('http')(     LoggingMiddleware() ) router = APIRouter(     tags=['root'],     responses={404: {'description': 'Not found'}}, ) @router.get('\/') async def root(request: Request):     logger.info('Test log')     return {'foo': 'bar'} app.include_router(router)<\/code><\/pre>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0440\u0430\u0437\u0443 \u0434\u0432\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u0438:<br \/>\u041f\u0435\u0440\u0432\u043e\u0435: \u043a\u0430\u043a \u0440\u0430\u0437 \u0442\u0430\u043a\u0438 \u0442\u043e\u0442 \u0432\u044b\u0432\u043e\u0434 \u0438\u0437 <code>Middleware<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u0435\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <\/p>\n<pre><code class=\"json\">{     \"thread\": 60535,     \"level\": 20,     \"level_name\": \"Information\",     \"message\": \"\u041e\u0442\u0432\u0435\u0442 \u0441 \u043a\u043e\u0434\u043e\u043c 200 \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 GET \\\"http:\/\/localhost:8080\/\\\", \u0437\u0430 4 \u043c\u0441\",     \"source\": \"test_project.utils.middlewares\",     \"@timestamp\": \"2021-08-28T01:49:23+03:00\",     \"app_name\": \"test_project\",     \"app_version\": \"1.0.0\",     \"app_env\": \"LOCAL\",     \"duration\": 4,     \"request_uri\": \"http:\/\/localhost:8080\/\",     \"request_referer\": \"\",     \"request_protocol\": \"HTTP\/1.1\",     \"request_method\": \"GET\",     \"request_path\": \"\/\",     \"request_host\": \"127.0.0.1:8080\",     \"request_size\": 0,     \"request_content_type\": \"\",     \"request_headers\": \"{\\\"host\\\": \\\"localhost:8080\\\"}\",     \"request_body\": \"\",     \"request_direction\": \"in\",     \"remote_ip\": \"127.0.0.1\",     \"remote_port\": \"50259\",     \"response_status_code\": 200,     \"response_size\": 13,     \"response_headers\": \"{\\\"content-length\\\": \\\"13\\\", \\\"content-type\\\": \\\"application\/json\\\"}\",     \"response_body\": \"{\\\"foo\\\":\\\"baz\\\"}\" }<\/code><\/pre>\n<p>\u0412\u0442\u043e\u0440\u043e\u0435: \u041d\u0430\u0448\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 <code>'Test Log'<\/code> \u0432\u043d\u0435 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0430 \u0443\u0436\u0435 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0432\u043d\u0443\u0442\u0440\u0438 <strong>route<\/strong><\/p>\n<pre><code class=\"json\">{     \"thread\": 60535,     \"level\": 20,     \"level_name\": \"Information\",     \"message\": \"Test log\",     \"source\": \"test_project.main.routes\",     \"@timestamp\": \"2021-08-28T01:49:23+03:00\",     \"app_name\": \"test_project\",     \"app_version\": \"1.0.0\",     \"app_env\": \"LOCAL\",     \"duration\": 385,     \"trace_id\": \"3bd6f269dbbbd275d46320c7e240bfe5\",     \"span_id\": \"2417303bd1af1ee6\",     \"parent_id\": null }<\/code><\/pre>\n<p><strong><em>\u0411\u043e\u043d\u0443\u0441. \u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0430.<\/em><\/strong><\/p>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u044f \u0442\u0430\u043a \u0441\u0438\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0435\u043b \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u0436\u0443\u0440\u043d\u0430\u043b\u0430? \u0421\u0435\u0439\u0447\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438 \u0442\u043e\u0433\u043e \u0436\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0441 \u043e\u0434\u043d\u0438\u043c\u0438 \u0438 \u0442\u0435\u043c\u0438 \u0436\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u043c\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430. <\/p>\n<ul>\n<li>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432: 10<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e-\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432: 200<\/p>\n<\/li>\n<li>\n<p>\u0422\u0430\u0439\u043c\u0430\u0443\u0442: 15 \u0441\u0435\u043a\u0443\u043d\u0434.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u0430: 15 \u0441\u0435\u043a\u0443\u043d\u0434 (\u0431\u043e\u043b\u044c\u0448\u0435 \u0438 \u043d\u0435 \u043d\u0430\u0434\u043e)<\/p>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438 \u0442\u043e\u0433\u043e \u0436\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f: test.<\/p>\n<\/li>\n<li>\n<p>Formatter \u043d\u0435 \u043c\u0435\u043d\u044f\u043b \u0441\u0432\u043e\u0439 \u043a\u043e\u0434.<\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<pre><code>wrk -c200 -t1 -d15s http:\/\/localhost:8080\/users\/public\/ --timeout 15<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<figure class=\"bordered\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/86a\/02b\/b75\/86a02bb759b315caaf1c309c012fd0e7.png\" width=\"503\" height=\"141\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/86a\/02b\/b75\/86a02bb759b315caaf1c309c012fd0e7.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0c5\/1fc\/bd3\/0c51fcbd364b618cc2926f244ef9f67e.png\" width=\"498\" height=\"145\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0c5\/1fc\/bd3\/0c51fcbd364b618cc2926f244ef9f67e.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0432\u044b\u0432\u043e\u0434\u0430 \u0434\u0430\u043b <strong>2294 rps<\/strong>, \u0430 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0432\u044b\u0432\u043e\u0434\u0430 \u0432\u044b\u0434\u0430\u043b \u0446\u0435\u043b\u044b\u0445 <strong>8364 rps.<\/strong> \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0446\u0435\u043b\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043b\u0430\u0441\u044c <strong>\u0432 3.64 \u0440\u0430\u0437\u0430<\/strong> \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0443 \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0443 \u043a \u043b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e. <\/p>\n<h2>\u0412\u044b\u0432\u043e\u0434<\/h2>\n<p>\u041d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0441\u043e\u0433\u043b\u0430\u0448\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0435\u0441\u043b\u0438 \u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0435, \u0447\u0442\u043e \u043e\u043d\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u0438\u0442\u044c \u0432\u0430\u0448\u0438\u0445 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u0435\u0439. \u041d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u043c\u043e\u0436\u0435\u0442 \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043f\u043b\u043e\u0445\u043e\u0439 \u0448\u0443\u0442\u043a\u043e\u0439 \u0438 \u043b\u0443\u0447\u0448\u0435 \u0441\u0440\u0430\u0437\u0443 \u043d\u0435 \u043f\u043e\u043b\u0435\u043d\u0438\u0442\u044c\u0441\u044f \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0441\u0432\u043e\u0451. \u0414\u0430, \u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432\u0440\u044f\u0434-\u043b\u0438 \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 99.99% \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c, \u0435\u0441\u0442\u044c \u043c\u043e\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u044b \u0443\u043b\u0443\u0447\u0448\u0438\u043b, \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043b \u0438 \u0441\u0434\u0435\u043b\u0430\u043b \u0431\u044b \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c\u0438. \u041a\u0430\u043a \u0440\u0430\u0437 \u043f\u0440\u043e \u044d\u0442\u043e \u044f \u0431\u044b \u0445\u043e\u0442\u0435\u043b \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u043e\u0431\u0441\u0443\u0434\u0438\u0442\u044c, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434. <\/p>\n<p>\u041f\u043e\u043a\u0430 \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0437\u0430\u043a\u0440\u044b\u0442\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u043c\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u043d\u043e \u044f \u043e\u0431\u0435\u0449\u0430\u044e, \u0447\u0442\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u044e \u044d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431, \u043b\u0438\u0431\u043e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u044e \u0443\u0436\u0435 PyPi \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0430\u0441, \u0434\u043e\u0440\u043e\u0433\u0438\u0435 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438. <\/p>\n<p>Telegram: <a class=\"mention\" href=\"\/users\/lev_key\">@Lev_key<\/a> <\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/575454\/\"> https:\/\/habr.com\/ru\/articles\/575454\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442. <\/p>\n<p>\u0426\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0438 \u043c\u043e\u0435\u0439 \u043b\u0438\u0447\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 &#8212; \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u044f \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 json-\u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0440\u0430\u0431\u043e\u0442\u0430\u044e, \u043d\u043e \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043c\u043d\u0435 \u0432\u043e\u043f\u043b\u043e\u0449\u0430\u0442\u044c \u043e\u0447\u0435\u043d\u044c \u0433\u0438\u0431\u043a\u043e \u0438 \u0443\u0434\u043e\u0431\u043d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0435\u0449\u0438 \u0441 \u0435\u0434\u0438\u043d\u044b\u043c \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043e\u043b\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u043b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0442\u0435\u043a\u0430\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u0430 \u0442\u0430\u043a\u0436\u0435: \u043d\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u043e \u0431\u044b \u043c\u0435\u043d\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c \u043e\u0434\u043d\u043e\u0439-\u0434\u0432\u0443\u043c\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c.<\/p>\n<p>\u041f\u043e\u0448\u0435\u043b \u0438\u0441\u043a\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432 \u043b\u043e\u0431: \u0438\u0441\u043a\u0430\u043b \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. \u041e\u0442\u044b\u0441\u043a\u0430\u043b \u043f\u0430\u0440\u0443-\u0442\u0440\u043e\u0439\u043a\u0443 \u0433\u043e\u0434\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430<a href=\"https:\/\/awesomeopensource.com\/project\/bobbui\/json-logging-python\" rel=\"noopener noreferrer nofollow\"> json-logging<\/a>. \u0412\u0441\u0451 \u0445\u043e\u0440\u043e\u0448\u043e: \u043f\u043e\u0447\u0438\u0442\u0430\u043b, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u043d\u0435 \u0433\u043b\u044f\u0434\u044f. \u0414\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u043c\u0430\u0441\u0441\u0443 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432: <\/p>\n<ul>\n<li>\n<p>\u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 <em>FastAPI, Flask, Aiohttp, Sanic,<\/em> <\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 <em>uvicorn access log<\/em>, <\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 JSON \u0444\u043e\u0440\u043c\u0430\u0442. <\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u044b\u0439 \u043b\u043e\u0433 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043b\u044f\u043c\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u043b \u0432 \u0441\u0435\u0440\u0432\u0438\u0441:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{ \"type\": \"request\", \"written_at\": \"2017-12-23T16:55:37.280Z\", \"written_ts\": 1514048137280721000, \"component_id\": \"-\", \"component_name\": \"-\", \"component_instance_idx\": 0, \"correlation_id\": \"1975a02e-e802-11e7-8971-28b2bd90b19a\", \"remote_user\": \"user_a\", \"request\": \"\/index.html\", \"referer\": \"-\", \"x_forwarded_for\": \"-\", \"protocol\": \"HTTP\/1.1\", \"method\": \"GET\", \"remote_ip\": \"127.0.0.1\", \"request_size_b\": 1234, \"remote_host\": \"127.0.0.1\", \"remote_port\": 50160, \"request_received_at\": \"2017-12-23T16:55:37.280Z\", \"response_time_ms\": 0, \"response_status\": 200, \"response_size_b\": \"122\", \"response_content_type\": \"text\/html; charset=utf-8\", \"response_sent_at\": \"2017-12-23T16:55:37.280Z\" }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041d\u043e \u0443 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b\u0438\u0441\u044c \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u0435\u0439 \u0432 \u043b\u043e\u0433\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f \u0431\u0435\u0437 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f Formatter \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044e. <\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u043b\u044c\u0437\u044f \u043d\u0438\u043a\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430 \u0431\u0435\u0437 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e <strong>Middleware<\/strong> \u0434\u043b\u044f <strong>FastAPI<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442 \u0435\u0449\u0435 \u0438 \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u043b\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u0432\u044b\u0432\u043e\u0434\u0430 \u043b\u043e\u0433\u0433\u0435\u0440\u0430 <code>uvicorn.access<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>\u042f \u043d\u0435 \u0431\u0443\u0434\u0443 \u043f\u0438\u0441\u0430\u0442\u044c, \u043a\u0430\u043a \u044f \u043f\u044b\u0442\u0430\u043b\u0441\u044f <s>\u0437\u0430\u043a\u043e\u0441\u0442\u044b\u043b\u0438\u0442\u044c<\/s> \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0435 \u0434\u0432\u0430 \u043c\u0438\u043d\u0443\u0441\u0430, \u043f\u043e\u043a\u0430 \u043d\u0435 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u043c\u043d\u0435 \u0432\u0441\u0451-\u0442\u0430\u043a\u0438 \u043d\u0443\u0436\u0435\u043d \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043b\u043e\u0433\u0435\u0440. \u042f \u043b\u0443\u0447\u0448\u0435 \u0412\u0430\u043c \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u044f \u0440\u0435\u0448\u0438\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435.<br \/>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u044f \u0440\u0435\u0448\u0438\u043b \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441\u0440\u0430\u0437\u0443:<\/p>\n<pre><code class=\"bash\">pip uninstall json-logging<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u044f \u0445\u043e\u0442\u0435\u043b \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0436\u0443\u0440\u043d\u0430\u043b\u0430. \u041c\u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u043c\u043e\u0433 <a href=\"https:\/\/pydantic-docs.helpmanual.io\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u0434\u043b\u044f \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0433\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u043e\u043f\u0440\u043e\u0441\u0430:<\/p>\n<pre><code class=\"python\">from typing import Union  from pydantic import BaseModel, Field   class BaseJsonLogSchema(BaseModel):     \"\"\"     \u0421\u0445\u0435\u043c\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0442\u0435\u043b\u0430 \u043b\u043e\u0433\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON     \"\"\"     thread: Union[int, str]     level: int     level_name: str     message: str     source: str     timestamp: str = Field(..., alias='@timestamp')     app_name: str     app_version: str     app_env: str     duration: int     exceptions: Union[list[str], str] = None     trace_id: str = None     span_id: str = None     parent_id: str = None      class Config:         allow_population_by_field_name = True   class RequestJsonLogSchema(BaseModel):     \"\"\"     \u0421\u0445\u0435\u043c\u0430 \u0447\u0430\u0441\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432-\u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043b\u043e\u0433\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON     \"\"\"     request_uri: str     request_referer: str     request_protocol: str     request_method: str     request_path: str     request_host: str     request_size: int     request_content_type: str     request_headers: str     request_body: str     request_direction: str     remote_ip: str     remote_port: str     response_status_code: int     response_size: int     response_headers: str     response_body: str     duration: int<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0431\u044b\u043b\u0430 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0430\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0424\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u0430 <strong>JSONLogFormatter<\/strong> \u0441 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e <strong>logging.Formatter<\/strong>.<br \/> \u041a\u043e\u0434 <code>test_project.utils.json_logger.py<\/code><\/p>\n<pre><code class=\"python\">import datetime import json import logging import traceback  from test_project.main.settings import APP_NAME, \\     APP_VERSION, ENVIRONMENT from test_project.utils.utils_schemas import BaseJsonLogSchema   class JSONLogFormatter(logging.Formatter):     \"\"\"     \u041a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441-\u0444\u043e\u0440\u043c\u0430\u0442\u0435\u0440 \u0434\u043b\u044f \u043b\u043e\u0433\u043e\u0432 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 json     \"\"\"      def format(self, record: logging.LogRecord, *args, **kwargs) -> str:         \"\"\"         \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0430 \u0432 json          :param record: \u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         :return: \u0441\u0442\u0440\u043e\u043a\u0430 \u0436\u0443\u0440\u043d\u0430\u043b\u0430 \u0432 JSON \u0444\u043e\u0440\u043c\u0430\u0442\u0435         \"\"\"         log_object: dict = self._format_log_object(record)         return json.dumps(log_object, ensure_ascii=False)      @staticmethod     def _format_log_object(record: logging.LogRecord) -> dict:         \"\"\"         \u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u0430\u043f\u0438\u0441\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         \u0432 json \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c \u043f\u0435\u0440\u0435\u0447\u043d\u0435\u043c \u043f\u043e\u043b\u0435\u0439          :param record: \u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         :return: \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         \"\"\"         now = datetime \\             .datetime \\             .fromtimestamp(record.created) \\             .astimezone() \\             .replace(microsecond=0) \\             .isoformat()         message = record.getMessage()         duration = record.duration \\             if hasattr(record, 'duration') \\             else record.msecs         # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0435\u043b\u0430 \u0436\u0443\u0440\u043d\u0430\u043b\u0430         json_log_fields = BaseJsonLogSchema(             thread=record.process,             timestamp=now,             level=record.levelno,             level_name=LEVEL_TO_NAME[record.levelno],             message=message,             source=record.name,             duration=duration,             app_name=APP_NAME,             app_version=APP_VERSION,             app_env=ENVIRONMENT,         )          if hasattr(record, 'props'):             json_log_fields.props = record.props          if record.exc_info:             json_log_fields.exceptions = \\                 traceback.format_exception(*record.exc_info)          elif record.exc_text:             json_log_fields.exceptions = record.exc_text         # \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Pydantic \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c         json_log_object = json_log_fields.dict(             exclude_unset=True,             by_alias=True,         )         # \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f         if hasattr(record, 'request_json_fields'):             json_log_object.update(record.request_json_fields)          return json_log_object<\/code><\/pre>\n<p>\u041d\u0443\u0436\u043d\u043e \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c FastApi \u043b\u043e\u0432\u0438\u0442\u044c request \u0438 response, \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441 \u043d\u0430\u043b\u0438\u0447\u0438\u0435\u043c body. \u042f \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a dependency \u043f\u043e\u0434\u0445\u043e\u0434\u0443 FastApi. \u041d\u043e \u043f\u043e\u043d\u044f\u043b, \u0447\u0442\u043e \u043d\u0435 \u0445\u043e\u0447\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043c\u0435\u0442\u043e\u0434 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043a \u043d\u0430\u043c \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 <a href=\"https:\/\/fastapi.tiangolo.com\/tutorial\/middleware\/\" rel=\"noopener noreferrer nofollow\">Middleware<\/a>. <br \/>\u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u0432 <strong>app<\/strong>-\u043e\u0431\u044a\u0435\u043a\u0442 \u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u043e\u0442\u0432\u0435\u0442\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0445 \u043f\u043e\u043b\u0435\u0439: status code, headers, remote ip, protocol, etc. \u041a\u043b\u0430\u0441\u0441 \u044f \u043d\u0430\u0437\u0432\u0430\u043b <strong>LoggingMiddleware <\/strong>\u0438 \u0432\u0441\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043c\u0435\u0442\u043e\u0434\u0443 <code>async def __call__()<\/code><\/p>\n<p>\u041a\u043e\u0434 <code>test_project.utils.middlewares.py<\/code><\/p>\n<pre><code class=\"python\">from test_project.main.settings import PORT from test_project.utils.json_logger import EMPTY_VALUE from test_project.utils.utils_schemas import RequestJsonLogSchema   class LoggingMiddleware:     \"\"\"     Middleware \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0441 \u0446\u0435\u043b\u044c\u044e \u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f     \"\"\"     @staticmethod     async def get_protocol(request: Request) -> str:         protocol = str(request.scope.get('type', ''))         http_version = str(request.scope.get('http_version', ''))         if protocol.lower() == 'http' and http_version:             return f'{protocol.upper()}\/{http_version}'         return EMPTY_VALUE      @staticmethod     async def set_body(request: Request, body: bytes) -> None:         async def receive() -> Message:             return {'type': 'http.request', 'body': body}          request._receive = receive      async def get_body(self, request: Request) -> bytes:         body = await request.body()         await self.set_body(request, body)         return body      async def __call__(             self, request: Request, call_next: RequestResponseEndpoint,             *args, **kwargs     ):         start_time = time.time()         exception_object = None         # Request Side         try:             raw_request_body = await request.body()             # \u041f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0443\u0436\u043d\u044b,              # \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u0442\u0435\u0440\u0435\u0442\u044c \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 # \u0438 \u043d\u0435 \u0443\u0439\u0442\u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0430\u043d\u0438\u0435 event-loop'a             # \u043f\u0440\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0442\u0435\u043b\u0430 \u043e\u0442\u0432\u0435\u0442\u0430             await self.set_body(request, raw_request_body)             raw_request_body = await self.get_body(request)             request_body = raw_request_body.decode()         except Exception:             request_body = EMPTY_VALUE          server: tuple = request.get('server', ('localhost', PORT))         request_headers: dict = dict(request.headers.items())         # Response Side         try:             response = await call_next(request)         except Exception as ex:             response_body = bytes(                 http.HTTPStatus.INTERNAL_SERVER_ERROR.phrase.encode()             )             response = Response(                 content=response_body,                 status_code=http.HTTPStatus.INTERNAL_SERVER_ERROR.real,             )             exception_object = ex             response_headers = {}         else:             response_headers = dict(response.headers.items())             response_body = b''             async for chunk in response.body_iterator:                 response_body += chunk             response = Response(                 content=response_body,                 status_code=response.status_code,                 headers=dict(response.headers),                 media_type=response.media_type             )         duration: int = math.ceil((time.time() - start_time) * 1000) # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430-\u043e\u0442\u0432\u0435\u0442\u0430         request_json_fields = RequestJsonLogSchema(             request_uri=str(request.url),             request_referer=request_headers.get('referer', EMPTY_VALUE),<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-385114","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/385114","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=385114"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/385114\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=385114"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=385114"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=385114"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}