Coverage for tests/test_logFormatter.py: 100%
134 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-19 09:05 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-19 09:05 +0000
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://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 software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28"""Tests for ButlerLogRecordsFormatter."""
30import json
31import logging
32import os
33import sys
34import tempfile
35import unittest
36from logging import FileHandler
38from lsst.daf.butler import Butler, DatasetRef, DatasetType, FileDataset
39from lsst.daf.butler.logging import (
40 ButlerLogRecordHandler,
41 ButlerLogRecords,
42 JsonLogFormatter,
43 _ButlerLogRecordsModelV1,
44)
45from lsst.daf.butler.tests._repo_template_cache import make_repo_for_test
46from lsst.daf.butler.tests.utils import makeTestTempDir, removeTestTempDir
48TESTDIR = os.path.abspath(os.path.dirname(__file__))
51class ButlerLogRecordsFormatterTestCase(unittest.TestCase):
52 """Test for ButlerLogRecords put/get."""
54 def setUp(self):
55 self.root = makeTestTempDir(TESTDIR)
56 make_repo_for_test(self.root)
58 self.run = "testrun"
59 self.butler = Butler.from_config(self.root, run=self.run)
60 self.enterContext(self.butler)
61 self.datasetType = DatasetType("test_logs", [], "ButlerLogRecords", universe=self.butler.dimensions)
63 self.butler.registry.registerDatasetType(self.datasetType)
65 def tearDown(self):
66 removeTestTempDir(self.root)
68 def testButlerLogRecordsFormatter(self):
69 """Test that we can get and put records handled by
70 ButlerLogRecordHandler.
71 """
72 handler = ButlerLogRecordHandler()
74 log = logging.getLogger(self.id())
75 log.setLevel(logging.INFO)
76 log.addHandler(handler)
78 log.info("An INFO message")
79 log.debug("A DEBUG message")
80 log.warning("A WARNING message")
82 ref = self.butler.put(handler.records, self.datasetType)
83 records = self.butler.get(ref)
85 self.assertEqual(records, handler.records)
86 self.assertEqual(len(records), 2)
88 def testButlerLogRecordsFormatterExtra(self):
89 """Test that we can get and put records handled by
90 ButlerLogRecordHandler with extra JSON data attached.
91 """
92 handler = ButlerLogRecordHandler()
94 log = logging.getLogger(self.id())
95 log.setLevel(logging.INFO)
96 log.addHandler(handler)
98 log.info("An INFO message")
99 log.debug("A DEBUG message")
100 log.warning("A WARNING message")
102 extra_data = {"extra1": 1, "extra2": 2}
103 handler.records.extra = extra_data
104 ref = self.butler.put(handler.records, self.datasetType)
105 records = self.butler.get(ref)
107 self.assertEqual(records, handler.records)
108 self.assertEqual(len(records), 2)
110 def testButlerLogRecordsFormatterEmptyExtra(self):
111 """Test that we can store extra information even with no log
112 records written.
113 """
114 handler = ButlerLogRecordHandler()
116 log = logging.getLogger(self.id())
117 log.setLevel(logging.INFO)
118 log.addHandler(handler)
120 extra_data = {"extra1": 1, "extra2": 2}
121 handler.records.extra = extra_data
122 ref = self.butler.put(handler.records, self.datasetType)
123 records = self.butler.get(ref)
125 self.assertEqual(records, handler.records)
126 self.assertEqual(len(records), 0)
128 @unittest.skipIf(
129 sys.version_info < (3, 12, 0),
130 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
131 )
132 def testButlerLogRecordsFormatterV1(self):
133 """Test that we can read log records stored via the old V1 format
134 (just a JSON list).
135 """
136 handler = ButlerLogRecordHandler()
138 log = logging.getLogger(self.id())
139 log.setLevel(logging.INFO)
140 log.addHandler(handler)
142 log.info("An INFO message")
143 log.debug("A DEBUG message")
144 log.warning("A WARNING message")
146 with tempfile.NamedTemporaryFile(
147 mode="w", suffix=".json", prefix="butler-log-", delete_on_close=False
148 ) as tmp:
149 # We can't use butler.put since that always writes the new format,
150 # so we write manually and ingest.
151 tmp.write(
152 _ButlerLogRecordsModelV1(root=handler.records._records).model_dump_json(
153 exclude_unset=True, exclude_defaults=True
154 )
155 )
156 tmp.close()
157 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
158 dataset = FileDataset(path=os.path.abspath(tmp.name), refs=ref)
159 self.butler.ingest(dataset, transfer="move")
161 records = self.butler.get(ref)
162 self.assertEqual(records, handler.records)
163 self.assertEqual(len(records), 2)
165 @unittest.skipIf(
166 sys.version_info < (3, 12, 0),
167 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
168 )
169 def testJsonLogRecordsFormatter(self):
170 """Test that externally created JSON format stream files work."""
171 log = logging.getLogger(self.id())
172 log.setLevel(logging.INFO)
174 with tempfile.NamedTemporaryFile(mode="w", suffix=".json", prefix="butler-log-") as tmp:
175 handler = FileHandler(tmp.name)
176 handler.setFormatter(JsonLogFormatter())
177 log.addHandler(handler)
179 log.info("An INFO message")
180 log.debug("A DEBUG message")
181 log.warning("A WARNING message")
183 handler.close()
185 # Now ingest the file.
186 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
187 dataset = FileDataset(path=tmp.name, refs=ref)
188 self.butler.ingest(dataset, transfer="move")
190 records = self.butler.get(ref)
191 self.assertEqual(len(records), 2)
193 @unittest.skipIf(
194 sys.version_info < (3, 12, 0),
195 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
196 )
197 def testJsonLogRecordsFormatterExtra(self):
198 """Test that externally created JSON format stream files work with
199 extra JSON data appended.
200 """
201 log = logging.getLogger(self.id())
202 log.setLevel(logging.INFO)
204 with tempfile.NamedTemporaryFile(mode="w", suffix=".json", prefix="butler-log-") as tmp:
205 handler = FileHandler(tmp.name)
206 handler.setFormatter(JsonLogFormatter())
207 log.addHandler(handler)
209 log.info("An INFO message")
210 log.debug("A DEBUG message")
211 log.warning("A WARNING message")
213 handler.close()
214 extra_data = {"extra1": 1, "extra2": 2}
215 with open(tmp.name, "a") as stream:
216 ButlerLogRecords.write_streaming_extra(stream, json.dumps(extra_data))
218 # Now ingest the file.
219 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
220 dataset = FileDataset(path=tmp.name, refs=ref)
221 self.butler.ingest(dataset, transfer="move")
223 records: ButlerLogRecords = self.butler.get(ref)
224 self.assertEqual(len(records), 2)
225 self.assertEqual(records.extra, extra_data)
227 @unittest.skipIf(
228 sys.version_info < (3, 12, 0),
229 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
230 )
231 def testJsonLogRecordsFormatterExtraEmpty(self):
232 """Test that externally created JSON format stream files work with
233 extra JSON data appended even if no log messages written.
234 """
235 log = logging.getLogger(self.id())
236 log.setLevel(logging.INFO)
238 with tempfile.NamedTemporaryFile(mode="w", suffix=".json", prefix="butler-log-") as tmp:
239 handler = FileHandler(tmp.name)
240 handler.setFormatter(JsonLogFormatter())
241 log.addHandler(handler)
243 handler.close()
244 extra_data = {"extra1": 1, "extra2": 2}
245 with open(tmp.name, "a") as stream:
246 ButlerLogRecords.write_streaming_extra(stream, json.dumps(extra_data))
248 # Now ingest the file.
249 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
250 dataset = FileDataset(path=tmp.name, refs=ref)
251 self.butler.ingest(dataset, transfer="move")
253 records: ButlerLogRecords = self.butler.get(ref)
254 self.assertEqual(len(records), 0)
255 self.assertEqual(records.extra, extra_data)
258if __name__ == "__main__":
259 unittest.main()