Coverage for tests/test_logging.py: 9%

84 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-03-14 10:19 -0700

1# This file is part of utils. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import logging 

23import time 

24import unittest 

25 

26from lsst.utils.logging import PeriodicLogger, getLogger, trace_set_at 

27 

28 

29class TestLogging(unittest.TestCase): 

30 """Simple unit tests for Task logging.""" 

31 

32 def testLogLevels(self): 

33 """Check that the new log levels look reasonable.""" 

34 root = getLogger() 

35 

36 self.assertEqual(root.DEBUG, logging.DEBUG) 

37 self.assertGreater(root.VERBOSE, logging.DEBUG) 

38 self.assertLess(root.VERBOSE, logging.INFO) 

39 self.assertLess(root.TRACE, logging.DEBUG) 

40 

41 def testLogCommands(self): 

42 """Check that all the log commands work.""" 

43 root = getLogger() 

44 

45 with self.assertLogs(level=root.TRACE) as cm: 

46 root.trace("Trace") 

47 root.debug("Debug") 

48 root.verbose("Verbose") 

49 root.verbose("Verbose with stacklevel", stacklevel=1) 

50 root.info("Info") 

51 root.warning("Warning") 

52 root.fatal("Fatal") 

53 root.critical("Critical") 

54 root.error("Error") 

55 

56 self.assertEqual(len(cm.records), 9) 

57 

58 # Check that each record has an explicit level name rather than 

59 # "Level N" and comes from this file (and not the logging.py). 

60 for record in cm.records: 

61 self.assertRegex(record.levelname, "^[A-Z]+$") 

62 self.assertEqual(record.filename, "test_logging.py") 

63 

64 with self.assertLogs(level=root.DEBUG) as cm: 

65 # Should only issue the INFO message. 

66 with root.temporary_log_level(root.INFO): 

67 root.info("Info") 

68 root.debug("Debug") 

69 self.assertEqual(len(cm.records), 1) 

70 

71 child = root.getChild("child") 

72 self.assertEqual(child.getEffectiveLevel(), root.getEffectiveLevel()) 

73 

74 # The root logger could be modified by the test environment. 

75 # We need to pick a level that is different. 

76 child.setLevel(root.getEffectiveLevel() - 5) 

77 self.assertNotEqual(child.getEffectiveLevel(), root.getEffectiveLevel()) 

78 

79 def testTraceSetAt(self): 

80 log_name = "lsst.afw" 

81 root_level = logging.getLogger().getEffectiveLevel() 

82 trace_set_at(log_name, 2) 

83 trace2_log = getLogger(f"TRACE2.{log_name}") 

84 trace3_log = getLogger(f"TRACE3.{log_name}") 

85 self.assertEqual(trace2_log.getEffectiveLevel(), logging.DEBUG) 

86 self.assertEqual(trace3_log.getEffectiveLevel(), logging.INFO) 

87 

88 # Check that child loggers are affected. 

89 log_name = "lsst.daf" 

90 child3_log = getLogger("TRACE3.lsst.daf") 

91 child2_log = getLogger("TRACE2.lsst.daf") 

92 self.assertEqual(child3_log.getEffectiveLevel(), root_level) 

93 self.assertEqual(child2_log.getEffectiveLevel(), root_level) 

94 trace_set_at("lsst", 2) 

95 self.assertEqual(child3_log.getEffectiveLevel(), logging.INFO) 

96 self.assertEqual(child2_log.getEffectiveLevel(), logging.DEBUG) 

97 

98 # Also check the root logger. 

99 trace_set_at("", 3) 

100 self.assertEqual(trace3_log.getEffectiveLevel(), logging.INFO) 

101 self.assertEqual(getLogger("TRACE3.test").getEffectiveLevel(), logging.DEBUG) 

102 

103 def test_periodic(self): 

104 logger = getLogger("test.periodicity") 

105 periodic = PeriodicLogger(logger) 

106 

107 # First message will not be issued. 

108 periodic.log("Message") 

109 self.assertEqual(periodic.num_issued, 0) 

110 

111 # Create a new periodic logger with no delay. 

112 # Every message should be issued. 

113 periodic = PeriodicLogger(logger, interval=0.0) 

114 with self.assertLogs(logger.name, level=logger.VERBOSE) as cm: 

115 periodic.log("Message") 

116 periodic.log("Message %d", 1) 

117 self.assertEqual(len(cm.output), 2) 

118 self.assertEqual(periodic.num_issued, 2) 

119 self.assertEqual(cm.output[0], f"VERBOSE:{logger.name}:Message") 

120 self.assertEqual(cm.output[1], f"VERBOSE:{logger.name}:Message 1") 

121 self.assertEqual(cm.records[0].filename, "test_logging.py", str(cm.records[0])) 

122 

123 # Create a new periodic logger with small delay. 

124 # One message should be issued. 

125 periodic = PeriodicLogger(logger, interval=0.2, level=logger.INFO) 

126 with self.assertLogs(logger.name, level=logger.INFO) as cm: 

127 periodic.log("Message") 

128 time.sleep(0.5) 

129 issued = periodic.log("Message %d", 1) 

130 self.assertTrue(issued) 

131 issued = periodic.log("Message %d", 2) 

132 self.assertFalse(issued) 

133 self.assertEqual(periodic.num_issued, 1) 

134 self.assertEqual(cm.output[0], f"INFO:{logger.name}:Message 1") 

135 

136 # Again with a standard python Logger. 

137 pylog = logging.getLogger("python.logger") 

138 periodic = PeriodicLogger(pylog, interval=0.0, level=logging.DEBUG) 

139 with self.assertLogs(pylog.name, level=logging.DEBUG) as cm: 

140 periodic.log("Message") 

141 self.assertEqual(cm.records[0].filename, "test_logging.py", str(cm.records[0])) 

142 

143 

144if __name__ == "__main__": 

145 unittest.main()