1# This file is dual licensed under the terms of the Apache License, Version 2# 2.0, and the MIT License. See the LICENSE file in the root of this 3# repository for complete details. 4 5from __future__ import absolute_import, division, print_function 6 7import sys 8 9import pytest 10 11from six.moves import cStringIO as StringIO 12 13from structlog._loggers import ( 14 WRITE_LOCKS, 15 PrintLogger, 16 PrintLoggerFactory, 17 ReturnLogger, 18 ReturnLoggerFactory, 19) 20from structlog.stdlib import _NAME_TO_LEVEL 21 22 23def test_return_logger(): 24 obj = ["hello"] 25 assert obj is ReturnLogger().msg(obj) 26 27 28STDLIB_MSG_METHODS = [m for m in _NAME_TO_LEVEL if m != "notset"] 29 30 31class TestPrintLogger(object): 32 def test_prints_to_stdout_by_default(self, capsys): 33 """ 34 Instantiating without arguments gives conveniently a logger to standard 35 out. 36 """ 37 PrintLogger().msg("hello") 38 39 out, err = capsys.readouterr() 40 assert "hello\n" == out 41 assert "" == err 42 43 def test_prints_to_correct_file(self, tmpdir, capsys): 44 """ 45 Supplied files are respected. 46 """ 47 f = tmpdir.join("test.log") 48 fo = f.open("w") 49 PrintLogger(fo).msg("hello") 50 out, err = capsys.readouterr() 51 52 assert "" == out == err 53 fo.close() 54 assert "hello\n" == f.read() 55 56 def test_repr(self): 57 """ 58 __repr__ makes sense. 59 """ 60 assert repr(PrintLogger()).startswith("<PrintLogger(file=") 61 62 def test_lock(self): 63 """ 64 Creating a logger adds a lock to WRITE_LOCKS. 65 """ 66 sio = StringIO() 67 68 assert sio not in WRITE_LOCKS 69 70 PrintLogger(sio) 71 72 assert sio in WRITE_LOCKS 73 74 @pytest.mark.parametrize("method", STDLIB_MSG_METHODS) 75 def test_stdlib_methods_support(self, method): 76 """ 77 PrintLogger implements methods of stdlib loggers. 78 """ 79 sio = StringIO() 80 81 getattr(PrintLogger(sio), method)("hello") 82 83 assert "hello" in sio.getvalue() 84 85 86class TestPrintLoggerFactory(object): 87 def test_does_not_cache(self): 88 """ 89 Due to doctest weirdness, we must not re-use PrintLoggers. 90 """ 91 f = PrintLoggerFactory() 92 93 assert f() is not f() 94 95 def test_passes_file(self): 96 """ 97 If a file is passed to the factory, it get passed on to the logger. 98 """ 99 pl = PrintLoggerFactory(sys.stderr)() 100 101 assert sys.stderr is pl._file 102 103 def test_ignores_args(self): 104 """ 105 PrintLogger doesn't take positional arguments. If any are passed to 106 the factory, they are not passed to the logger. 107 """ 108 PrintLoggerFactory()(1, 2, 3) 109 110 111class ReturnLoggerTest(object): 112 @pytest.mark.parametrize("method", STDLIB_MSG_METHODS) 113 def test_stdlib_methods_support(self, method): 114 """ 115 ReturnLogger implements methods of stdlib loggers. 116 """ 117 v = getattr(ReturnLogger(), method)("hello") 118 119 assert "hello" == v 120 121 122class TestReturnLoggerFactory(object): 123 def test_builds_returnloggers(self): 124 f = ReturnLoggerFactory() 125 126 assert isinstance(f(), ReturnLogger) 127 128 def test_caches(self): 129 """ 130 There's no need to have several loggers so we return the same one on 131 each call. 132 """ 133 f = ReturnLoggerFactory() 134 135 assert f() is f() 136 137 def test_ignores_args(self): 138 """ 139 ReturnLogger doesn't take positional arguments. If any are passed to 140 the factory, they are not passed to the logger. 141 """ 142 ReturnLoggerFactory()(1, 2, 3) 143