220 lines
5.9 KiB
Python
220 lines
5.9 KiB
Python
import logging
|
|
import os
|
|
import uuid
|
|
|
|
from importlib import reload
|
|
from io import StringIO
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
import sanic
|
|
|
|
from sanic import Sanic
|
|
from sanic.log import LOGGING_CONFIG_DEFAULTS, logger
|
|
from sanic.response import text
|
|
from sanic.testing import SanicTestClient
|
|
|
|
|
|
logging_format = """module: %(module)s; \
|
|
function: %(funcName)s(); \
|
|
message: %(message)s"""
|
|
|
|
|
|
def reset_logging():
|
|
logging.shutdown()
|
|
reload(logging)
|
|
|
|
|
|
def test_log(app):
|
|
log_stream = StringIO()
|
|
for handler in logging.root.handlers[:]:
|
|
logging.root.removeHandler(handler)
|
|
logging.basicConfig(
|
|
format=logging_format, level=logging.DEBUG, stream=log_stream
|
|
)
|
|
log = logging.getLogger()
|
|
rand_string = str(uuid.uuid4())
|
|
|
|
@app.route("/")
|
|
def handler(request):
|
|
log.info(rand_string)
|
|
return text("hello")
|
|
|
|
request, response = app.test_client.get("/")
|
|
log_text = log_stream.getvalue()
|
|
assert rand_string in log_text
|
|
|
|
|
|
def test_logging_defaults():
|
|
# reset_logging()
|
|
app = Sanic("test_logging")
|
|
|
|
for fmt in [h.formatter for h in logging.getLogger("sanic.root").handlers]:
|
|
assert (
|
|
fmt._fmt
|
|
== LOGGING_CONFIG_DEFAULTS["formatters"]["generic"]["format"]
|
|
)
|
|
|
|
for fmt in [
|
|
h.formatter for h in logging.getLogger("sanic.error").handlers
|
|
]:
|
|
assert (
|
|
fmt._fmt
|
|
== LOGGING_CONFIG_DEFAULTS["formatters"]["generic"]["format"]
|
|
)
|
|
|
|
for fmt in [
|
|
h.formatter for h in logging.getLogger("sanic.access").handlers
|
|
]:
|
|
assert (
|
|
fmt._fmt
|
|
== LOGGING_CONFIG_DEFAULTS["formatters"]["access"]["format"]
|
|
)
|
|
|
|
|
|
def test_logging_pass_customer_logconfig():
|
|
# reset_logging()
|
|
|
|
modified_config = LOGGING_CONFIG_DEFAULTS
|
|
modified_config["formatters"]["generic"][
|
|
"format"
|
|
] = "%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s"
|
|
modified_config["formatters"]["access"][
|
|
"format"
|
|
] = "%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s"
|
|
|
|
app = Sanic("test_logging", log_config=modified_config)
|
|
|
|
for fmt in [h.formatter for h in logging.getLogger("sanic.root").handlers]:
|
|
assert fmt._fmt == modified_config["formatters"]["generic"]["format"]
|
|
|
|
for fmt in [
|
|
h.formatter for h in logging.getLogger("sanic.error").handlers
|
|
]:
|
|
assert fmt._fmt == modified_config["formatters"]["generic"]["format"]
|
|
|
|
for fmt in [
|
|
h.formatter for h in logging.getLogger("sanic.access").handlers
|
|
]:
|
|
assert fmt._fmt == modified_config["formatters"]["access"]["format"]
|
|
|
|
|
|
@pytest.mark.parametrize("debug", (True, False))
|
|
def test_log_connection_lost(app, debug, monkeypatch):
|
|
""" Should not log Connection lost exception on non debug """
|
|
stream = StringIO()
|
|
root = logging.getLogger("sanic.root")
|
|
root.addHandler(logging.StreamHandler(stream))
|
|
monkeypatch.setattr(sanic.server, "logger", root)
|
|
|
|
@app.route("/conn_lost")
|
|
async def conn_lost(request):
|
|
response = text("Ok")
|
|
request.transport.close()
|
|
return response
|
|
|
|
req, res = app.test_client.get("/conn_lost", debug=debug)
|
|
assert res is None
|
|
|
|
log = stream.getvalue()
|
|
|
|
if debug:
|
|
assert "Connection lost before response written @" in log
|
|
else:
|
|
assert "Connection lost before response written @" not in log
|
|
|
|
|
|
def test_logger(caplog):
|
|
rand_string = str(uuid.uuid4())
|
|
|
|
app = Sanic(name=__name__)
|
|
|
|
@app.get("/")
|
|
def log_info(request):
|
|
logger.info(rand_string)
|
|
return text("hello")
|
|
|
|
with caplog.at_level(logging.INFO):
|
|
request, response = app.test_client.get("/")
|
|
|
|
port = request.server_port
|
|
|
|
# Note: testing with random port doesn't show the banner because it doesn't
|
|
# define host and port. This test supports both modes.
|
|
if caplog.record_tuples[0] == (
|
|
"sanic.root",
|
|
logging.INFO,
|
|
f"Goin' Fast @ http://127.0.0.1:{port}",
|
|
):
|
|
caplog.record_tuples.pop(0)
|
|
|
|
assert caplog.record_tuples[0] == (
|
|
"sanic.root",
|
|
logging.INFO,
|
|
f"http://127.0.0.1:{port}/",
|
|
)
|
|
assert caplog.record_tuples[1] == ("sanic.root", logging.INFO, rand_string)
|
|
assert caplog.record_tuples[-1] == (
|
|
"sanic.root",
|
|
logging.INFO,
|
|
"Server Stopped",
|
|
)
|
|
|
|
|
|
def test_logger_static_and_secure(caplog):
|
|
# Same as test_logger, except for more coverage:
|
|
# - test_client initialised separately for static port
|
|
# - using ssl
|
|
rand_string = str(uuid.uuid4())
|
|
|
|
app = Sanic(name=__name__)
|
|
|
|
@app.get("/")
|
|
def log_info(request):
|
|
logger.info(rand_string)
|
|
return text("hello")
|
|
|
|
current_dir = os.path.dirname(os.path.realpath(__file__))
|
|
ssl_cert = os.path.join(current_dir, "certs/selfsigned.cert")
|
|
ssl_key = os.path.join(current_dir, "certs/selfsigned.key")
|
|
|
|
ssl_dict = {"cert": ssl_cert, "key": ssl_key}
|
|
|
|
test_client = SanicTestClient(app, port=42101)
|
|
with caplog.at_level(logging.INFO):
|
|
request, response = test_client.get(
|
|
f"https://127.0.0.1:{test_client.port}/",
|
|
server_kwargs=dict(ssl=ssl_dict),
|
|
)
|
|
|
|
port = test_client.port
|
|
|
|
assert caplog.record_tuples[0] == (
|
|
"sanic.root",
|
|
logging.INFO,
|
|
f"Goin' Fast @ https://127.0.0.1:{port}",
|
|
)
|
|
assert caplog.record_tuples[1] == (
|
|
"sanic.root",
|
|
logging.INFO,
|
|
f"https://127.0.0.1:{port}/",
|
|
)
|
|
assert caplog.record_tuples[2] == ("sanic.root", logging.INFO, rand_string)
|
|
assert caplog.record_tuples[-1] == (
|
|
"sanic.root",
|
|
logging.INFO,
|
|
"Server Stopped",
|
|
)
|
|
|
|
|
|
def test_logging_modified_root_logger_config():
|
|
# reset_logging()
|
|
|
|
modified_config = LOGGING_CONFIG_DEFAULTS
|
|
modified_config["loggers"]["sanic.root"]["level"] = "DEBUG"
|
|
|
|
app = Sanic("test_logging", log_config=modified_config)
|
|
|
|
assert logging.getLogger("sanic.root").getEffectiveLevel() == logging.DEBUG
|