Coverage for tests / test_logFormatter.py: 19%
133 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 08:49 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 08:49 +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.utils import makeTestTempDir, removeTestTempDir
47TESTDIR = os.path.abspath(os.path.dirname(__file__))
50class ButlerLogRecordsFormatterTestCase(unittest.TestCase):
51 """Test for ButlerLogRecords put/get."""
53 def setUp(self):
54 self.root = makeTestTempDir(TESTDIR)
55 Butler.makeRepo(self.root)
57 self.run = "testrun"
58 self.butler = Butler.from_config(self.root, run=self.run)
59 self.enterContext(self.butler)
60 self.datasetType = DatasetType("test_logs", [], "ButlerLogRecords", universe=self.butler.dimensions)
62 self.butler.registry.registerDatasetType(self.datasetType)
64 def tearDown(self):
65 removeTestTempDir(self.root)
67 def testButlerLogRecordsFormatter(self):
68 """Test that we can get and put records handled by
69 ButlerLogRecordHandler.
70 """
71 handler = ButlerLogRecordHandler()
73 log = logging.getLogger(self.id())
74 log.setLevel(logging.INFO)
75 log.addHandler(handler)
77 log.info("An INFO message")
78 log.debug("A DEBUG message")
79 log.warning("A WARNING message")
81 ref = self.butler.put(handler.records, self.datasetType)
82 records = self.butler.get(ref)
84 self.assertEqual(records, handler.records)
85 self.assertEqual(len(records), 2)
87 def testButlerLogRecordsFormatterExtra(self):
88 """Test that we can get and put records handled by
89 ButlerLogRecordHandler with extra JSON data attached.
90 """
91 handler = ButlerLogRecordHandler()
93 log = logging.getLogger(self.id())
94 log.setLevel(logging.INFO)
95 log.addHandler(handler)
97 log.info("An INFO message")
98 log.debug("A DEBUG message")
99 log.warning("A WARNING message")
101 extra_data = {"extra1": 1, "extra2": 2}
102 handler.records.extra = extra_data
103 ref = self.butler.put(handler.records, self.datasetType)
104 records = self.butler.get(ref)
106 self.assertEqual(records, handler.records)
107 self.assertEqual(len(records), 2)
109 def testButlerLogRecordsFormatterEmptyExtra(self):
110 """Test that we can store extra information even with no log
111 records written.
112 """
113 handler = ButlerLogRecordHandler()
115 log = logging.getLogger(self.id())
116 log.setLevel(logging.INFO)
117 log.addHandler(handler)
119 extra_data = {"extra1": 1, "extra2": 2}
120 handler.records.extra = extra_data
121 ref = self.butler.put(handler.records, self.datasetType)
122 records = self.butler.get(ref)
124 self.assertEqual(records, handler.records)
125 self.assertEqual(len(records), 0)
127 @unittest.skipIf(
128 sys.version_info < (3, 12, 0),
129 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
130 )
131 def testButlerLogRecordsFormatterV1(self):
132 """Test that we can read log records stored via the old V1 format
133 (just a JSON list).
134 """
135 handler = ButlerLogRecordHandler()
137 log = logging.getLogger(self.id())
138 log.setLevel(logging.INFO)
139 log.addHandler(handler)
141 log.info("An INFO message")
142 log.debug("A DEBUG message")
143 log.warning("A WARNING message")
145 with tempfile.NamedTemporaryFile(
146 mode="w", suffix=".json", prefix="butler-log-", delete_on_close=False
147 ) as tmp:
148 # We can't use butler.put since that always writes the new format,
149 # so we write manually and ingest.
150 tmp.write(
151 _ButlerLogRecordsModelV1(root=handler.records._records).model_dump_json(
152 exclude_unset=True, exclude_defaults=True
153 )
154 )
155 tmp.close()
156 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
157 dataset = FileDataset(path=os.path.abspath(tmp.name), refs=ref)
158 self.butler.ingest(dataset, transfer="move")
160 records = self.butler.get(ref)
161 self.assertEqual(records, handler.records)
162 self.assertEqual(len(records), 2)
164 @unittest.skipIf(
165 sys.version_info < (3, 12, 0),
166 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
167 )
168 def testJsonLogRecordsFormatter(self):
169 """Test that externally created JSON format stream files work."""
170 log = logging.getLogger(self.id())
171 log.setLevel(logging.INFO)
173 with tempfile.NamedTemporaryFile(mode="w", suffix=".json", prefix="butler-log-") as tmp:
174 handler = FileHandler(tmp.name)
175 handler.setFormatter(JsonLogFormatter())
176 log.addHandler(handler)
178 log.info("An INFO message")
179 log.debug("A DEBUG message")
180 log.warning("A WARNING message")
182 handler.close()
184 # Now ingest the file.
185 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
186 dataset = FileDataset(path=tmp.name, refs=ref)
187 self.butler.ingest(dataset, transfer="move")
189 records = self.butler.get(ref)
190 self.assertEqual(len(records), 2)
192 @unittest.skipIf(
193 sys.version_info < (3, 12, 0),
194 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
195 )
196 def testJsonLogRecordsFormatterExtra(self):
197 """Test that externally created JSON format stream files work with
198 extra JSON data appended.
199 """
200 log = logging.getLogger(self.id())
201 log.setLevel(logging.INFO)
203 with tempfile.NamedTemporaryFile(mode="w", suffix=".json", prefix="butler-log-") as tmp:
204 handler = FileHandler(tmp.name)
205 handler.setFormatter(JsonLogFormatter())
206 log.addHandler(handler)
208 log.info("An INFO message")
209 log.debug("A DEBUG message")
210 log.warning("A WARNING message")
212 handler.close()
213 extra_data = {"extra1": 1, "extra2": 2}
214 with open(tmp.name, "a") as stream:
215 ButlerLogRecords.write_streaming_extra(stream, json.dumps(extra_data))
217 # Now ingest the file.
218 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
219 dataset = FileDataset(path=tmp.name, refs=ref)
220 self.butler.ingest(dataset, transfer="move")
222 records: ButlerLogRecords = self.butler.get(ref)
223 self.assertEqual(len(records), 2)
224 self.assertEqual(records.extra, extra_data)
226 @unittest.skipIf(
227 sys.version_info < (3, 12, 0),
228 "This test requires NamedTemporaryFile behavior not available in older Python versions.",
229 )
230 def testJsonLogRecordsFormatterExtraEmpty(self):
231 """Test that externally created JSON format stream files work with
232 extra JSON data appended even if no log messages written.
233 """
234 log = logging.getLogger(self.id())
235 log.setLevel(logging.INFO)
237 with tempfile.NamedTemporaryFile(mode="w", suffix=".json", prefix="butler-log-") as tmp:
238 handler = FileHandler(tmp.name)
239 handler.setFormatter(JsonLogFormatter())
240 log.addHandler(handler)
242 handler.close()
243 extra_data = {"extra1": 1, "extra2": 2}
244 with open(tmp.name, "a") as stream:
245 ButlerLogRecords.write_streaming_extra(stream, json.dumps(extra_data))
247 # Now ingest the file.
248 ref = DatasetRef(self.datasetType, dataId={}, run=self.run)
249 dataset = FileDataset(path=tmp.name, refs=ref)
250 self.butler.ingest(dataset, transfer="move")
252 records: ButlerLogRecords = self.butler.get(ref)
253 self.assertEqual(len(records), 0)
254 self.assertEqual(records.extra, extra_data)
257if __name__ == "__main__":
258 unittest.main()