"""Test logging.""" import json import logging import uuid from datetime import datetime from typing import Any from unittest import mock import pytest import owslogger from owslogger import logger def test_handler_creation() -> None: """Test the creation of the handler.""" dsn = "dsn value" environment = "qa" service_name = "service_name" service_version = "1.0.0" handler = logger.DSNHandler(dsn, environment, service_name, service_version) assert handler.dsn == dsn assert handler.environment == environment assert handler.service_name == service_name assert handler.service_version == service_version def test_get_full_message() -> None: """Test getting a full message.""" record = mock.Mock() message = "Message" record.msg = message record.levelno = 10 record.exc_info = None handler = logger.DSNHandler("dsn", "qa", "logging", "1.0.0") assert handler.get_full_message(record) == message @mock.patch("traceback.format_exception") def test_get_full_message_with_exception(trace: mock.Mock) -> None: """Test getting message from exception.""" record = mock.Mock() record.levelno = 50 record.exc_info = ["one", "two"] trace.return_value = ["trace", "two"] handler = logger.DSNHandler("dsn", "qa", "logging", "1.0.0") assert handler.get_full_message(record) trace.assert_called_once_with("one", "two") @mock.patch("owslogger.logger.requests_session") def test_session_post_called_on_message_emit(requests_session: mock.Mock) -> None: """Test session post called when a message is emitted.""" record = mock.Mock() message = "Message" correlation_id = uuid.uuid1() record.levelno = 10 record.msg = message record.created = datetime.now().timestamp() record.exc_info = None record.correlation_id = correlation_id handler = logger.DSNHandler("dsn", "qa", "logging", "1.0.0") handler.emit(record) assert requests_session.post.called @mock.patch("owslogger.logger.socket") def test_udp_send_called_on_message_emit(socket: mock.Mock) -> None: """Test session post called when a message is emitted.""" s = socket.socket.return_value = mock.Mock() s.sendto = mock.Mock() class TestClass: """Test class.""" message = "Message" correlation_id = uuid.uuid1() levelno = 10 msg = message created = datetime.now().timestamp() exc_info = None correlation_id = correlation_id filename = None funcName = None lineno = None name = None record = TestClass() handler = logger.DSNHandler("udp://hostname.com:5160", "qa", "logging", "1.0.0") handler.emit(record) # type: ignore s.sendto.assert_called() @mock.patch("owslogger.logger.requests_session") def test_session_post_called_on_message_emit_no_correlation_id( requests_session: mock.Mock, ) -> None: """Test session post called when a message is emitted.""" class LogRecord: levelno = 10 msg = "Message" created = datetime.now().timestamp() exc_info = None filename = None funcName = None lineno = None name = None record = LogRecord() handler = logger.DSNHandler("dsn", "qa", "logging", "1.0.0") handler.emit(record) # type: ignore assert requests_session.post.called @mock.patch("owslogger.logger.requests_session") def test_emit_message_on_post_failure(requests_session: mock.Mock) -> None: """Test emitting a message with the session post triggering an exception.""" # SystemExit won't be an error that will be thrown, it will just # be an exception. To test this is requests_session.post = mock.Mock(side_effect=SystemExit()) record = mock.Mock() record.msg = "Message" record.levelno = 10 record.created = datetime.now().timestamp() record.exc_info = None record.correlation_id = uuid.uuid1() handler = logger.DSNHandler("dsn", "qa", "logging", "1.0.0") with pytest.raises(SystemExit): handler.emit(record) @mock.patch("owslogger.logger.requests_session") def test_emit_message_with_normal_exception(requests_session: mock.Mock) -> None: """Test emiting a message with an exception thrown from post request.""" requests_session.post = mock.Mock(side_effect=Exception()) record = mock.Mock() record.msg = "Message" record.levelno = 10 record.exc_info = None record.correlation_id = uuid.uuid1() handler = logger.DSNHandler("dsn", "qa", "logging", "1.0.0") handler.handleError = mock.Mock() # type: ignore handler.emit(record) handler.handleError.assert_called_with(record) def test_configure_handler_without_dsn() -> None: """Test configuration of the handler.""" current_logging = logging.getLogger("test_logger") logger.configure_handler(current_logging, "qa", "logging", "1.0.0") assert isinstance(current_logging.handlers[0], logging.StreamHandler) def test_configure_handler_with_dsn() -> None: """Test configuration of the handler.""" current_logging = logging.getLogger("test_logger_with_dsn") logger.configure_handler(current_logging, "qa", "logging", "1.0.0", dsn="dsn") assert isinstance(current_logging.handlers[0], logger.DSNHandler) assert current_logging.handlers[0].dsn == "dsn" @pytest.mark.parametrize( ("level", "expected"), [ (10, ("DEBUG", 100)), (20, ("INFO", 200)), (25, ("NOTICE", 250)), (30, ("WARNING", 300)), (40, ("ERROR", 400)), (50, ("CRITICAL", 500)), (1, ("DEBUG", 100)), (600, ("CRITICAL", 500)), ], ) def test_get_standard_level_from_record(level: int, expected: tuple[str, int]) -> None: """Test the standardization of the python levels.""" record = mock.Mock() record.levelno = level response = logger.get_standard_level_from_record(record) assert response == expected @mock.patch("os.getpid", return_value=123) @mock.patch("threading.get_ident", return_value=456) def test_dd_handler(_: Any, __: Any, capsys: pytest.CaptureFixture[str]) -> None: """Test configuration of ddjsonformatter handler.""" current_logging = logging.getLogger("dd_logger") current_logging.setLevel("INFO") logger.configure_handler(current_logging, "qa", "logging", "1.0.0") current_logging.info("hello world") captured = capsys.readouterr() result = json.loads(captured.out) assert result["message"] == "hello world" assert result["thread_id"] == 456 assert result["process_id"] == 123 assert result["timestamp"] is not None assert result["level"] == "INFO" assert result["service"] == "logging" assert result["environment"] == "qa" assert result["log_type"] == "application" assert result["logger_version"] == owslogger.__version__ assert result["tag"] == "ows1" assert result["dd"] assert result["dd"]["version"] is not None assert result["logger"]["name"] == "dd_logger" assert result["logger"]["pathname"].endswith("tests/test_logging.py") assert result["logger"]["file_name"] == "test_logging.py" assert result["logger"]["method_name"] == "test_dd_handler" assert result["logger"]["lineno"] == 194 assert result["logger"]["thread_name"] @pytest.mark.parametrize( ("clear_handlers", "expected"), [ (True, 0), (False, 1), ], ) def test_setup(clear_handlers: bool, expected: int) -> None: """Test setup method.""" # simulate ddtrace behavior root_logger = logging.getLogger() root_logger.handlers.clear() # remove any existing handlers test_handler = logging.StreamHandler() root_logger.addHandler(test_handler) assert len(root_logger.handlers) == 1 logger.setup( "qa", "logging", "INFO", "logging", "1.0", clear_handlers=clear_handlers, ) assert len(root_logger.handlers) == expected