22# Copyright(c) 2010-2014 Intel Corporation
33# Copyright(c) 2022-2023 PANTHEON.tech s.r.o.
44# Copyright(c) 2022-2023 University of New Hampshire
5+ # Copyright(c) 2025 Arm Limited
56
67"""DTS logger module.
78
89The module provides several additional features:
910
1011 * The storage of DTS execution stages,
11- * Logging to console, a human-readable log file and a machine-readable log file ,
12- * Optional log files for specific stages.
12+ * Logging to console, a human-readable log artifact and a machine-readable log artifact ,
13+ * Optional log artifacts for specific stages.
1314"""
1415
1516import logging
16- from logging import FileHandler , StreamHandler
17- from pathlib import Path
18- from typing import ClassVar
17+ from logging import StreamHandler
18+ from typing import TYPE_CHECKING , ClassVar , NamedTuple
19+
20+ if TYPE_CHECKING :
21+ from framework .testbed_model .artifact import Artifact
1922
2023date_fmt = "%Y/%m/%d %H:%M:%S"
2124stream_fmt = "%(asctime)s - %(stage)s - %(name)s - %(levelname)s - %(message)s"
2225dts_root_logger_name = "dts"
2326
2427
28+ class ArtifactHandler (NamedTuple ):
29+ """A logger handler with an associated artifact."""
30+
31+ artifact : "Artifact"
32+ handler : StreamHandler
33+
34+
2535class DTSLogger (logging .Logger ):
2636 """The DTS logger class.
2737
2838 The class extends the :class:`~logging.Logger` class to add the DTS execution stage information
2939 to log records. The stage is common to all loggers, so it's stored in a class variable.
3040
31- Any time we switch to a new stage, we have the ability to log to an additional log file along
32- with a supplementary log file with machine-readable format. These two log files are used until
33- a new stage switch occurs. This is useful mainly for logging per test suite.
41+ Any time we switch to a new stage, we have the ability to log to an additional log artifact
42+ along with a supplementary log artifact with machine-readable format. These two log artifacts
43+ are used until a new stage switch occurs. This is useful mainly for logging per test suite.
3444 """
3545
3646 _stage : ClassVar [str ] = "pre_run"
37- _extra_file_handlers : list [FileHandler ] = []
47+ _root_artifact_handlers : list [ArtifactHandler ] = []
48+ _extra_artifact_handlers : list [ArtifactHandler ] = []
3849
3950 def __init__ (self , * args , ** kwargs ):
40- """Extend the constructor with extra file handlers."""
41- self ._extra_file_handlers = []
51+ """Extend the constructor with extra artifact handlers."""
52+ self ._extra_artifact_handlers = []
4253 super ().__init__ (* args , ** kwargs )
4354
4455 def makeRecord (self , * args , ** kwargs ) -> logging .LogRecord :
@@ -56,7 +67,7 @@ def makeRecord(self, *args, **kwargs) -> logging.LogRecord:
5667 record .stage = DTSLogger ._stage
5768 return record
5869
59- def add_dts_root_logger_handlers (self , verbose : bool , output_dir : str ) -> None :
70+ def add_dts_root_logger_handlers (self , verbose : bool ) -> None :
6071 """Add logger handlers to the DTS root logger.
6172
6273 This method should be called only on the DTS root logger.
@@ -65,18 +76,16 @@ def add_dts_root_logger_handlers(self, verbose: bool, output_dir: str) -> None:
6576 Three handlers are added:
6677
6778 * A console handler,
68- * A file handler,
69- * A supplementary file handler with machine-readable logs
79+ * An artifact handler,
80+ * A supplementary artifact handler with machine-readable logs
7081 containing more debug information.
7182
72- All log messages will be logged to files . The log level of the console handler
83+ All log messages will be logged to artifacts . The log level of the console handler
7384 is configurable with `verbose`.
7485
7586 Args:
7687 verbose: If :data:`True`, log all messages to the console.
7788 If :data:`False`, log to console with the :data:`logging.INFO` level.
78- output_dir: The directory where the log files will be located.
79- The names of the log files correspond to the name of the logger instance.
8089 """
8190 self .setLevel (1 )
8291
@@ -86,70 +95,81 @@ def add_dts_root_logger_handlers(self, verbose: bool, output_dir: str) -> None:
8695 sh .setLevel (logging .INFO )
8796 self .addHandler (sh )
8897
89- self ._add_file_handlers ( Path ( output_dir , self .name ) )
98+ self ._root_artifact_handlers = self ._add_artifact_handlers ( self . name )
9099
91- def set_stage (self , stage : str , log_file_path : Path | None = None ) -> None :
92- """Set the DTS execution stage and optionally log to files.
93-
94- Set the DTS execution stage of the DTSLog class and optionally add
95- file handlers to the instance if the log file name is provided.
96-
97- The file handlers log all messages. One is a regular human-readable log file and
98- the other one is a machine-readable log file with extra debug information.
100+ def set_stage (self , stage : str ) -> None :
101+ """Set the DTS execution stage.
99102
100103 Args:
101104 stage: The DTS stage to set.
102- log_file_path: An optional path of the log file to use. This should be a full path
103- (either relative or absolute) without suffix (which will be appended).
104105 """
105- self ._remove_extra_file_handlers ()
106-
107106 if DTSLogger ._stage != stage :
108107 self .info (f"Moving from stage '{ DTSLogger ._stage } ' to stage '{ stage } '." )
109108 DTSLogger ._stage = stage
110109
111- if log_file_path :
112- self . _extra_file_handlers . extend ( self . _add_file_handlers ( log_file_path ))
110+ def set_custom_log_file ( self , log_file_name : str | None ) -> None :
111+ """Set a custom log file.
113112
114- def _add_file_handlers ( self , log_file_path : Path ) -> list [ FileHandler ]:
115- """Add file handlers to the DTS root logger .
113+ Add artifact handlers to the instance if the log artifact file name is provided. Otherwise,
114+ stop logging to any custom log file .
116115
117- Add two type of file handlers:
116+ The artifact handlers log all messages. One is a regular human-readable log artifact and
117+ the other one is a machine-readable log artifact with extra debug information.
118118
119- * A regular file handler with suffix ".log",
120- * A machine-readable file handler with suffix ".verbose.log".
119+ Args:
120+ log_file_name: An optional name of the log artifact file to use. This should be without
121+ suffix (which will be appended).
122+ """
123+ self ._remove_extra_artifact_handlers ()
124+
125+ if log_file_name :
126+ self ._extra_artifact_handlers .extend (self ._add_artifact_handlers (log_file_name ))
127+
128+ def _add_artifact_handlers (self , log_file_name : str ) -> list [ArtifactHandler ]:
129+ """Add artifact handlers to the DTS root logger.
130+
131+ Add two type of artifact handlers:
132+
133+ * A regular artifact handler with suffix ".log",
134+ * A machine-readable artifact handler with suffix ".verbose.log".
121135 This format provides extensive information for debugging and detailed analysis.
122136
123137 Args:
124- log_file_path : The full path to the log file without suffix.
138+ log_file_name : The name of the artifact log file without suffix.
125139
126140 Returns:
127- The newly created file handlers.
128-
141+ The newly created artifact handlers.
129142 """
130- fh = FileHandler (f"{ log_file_path } .log" )
131- fh .setFormatter (logging .Formatter (stream_fmt , date_fmt ))
132- self .addHandler (fh )
143+ from framework .testbed_model .artifact import Artifact
144+
145+ log_artifact = Artifact ("local" , f"{ log_file_name } .log" )
146+ handler = StreamHandler (log_artifact .open ("w" ))
147+ handler .setFormatter (logging .Formatter (stream_fmt , date_fmt ))
148+ self .addHandler (handler )
133149
134- verbose_fh = FileHandler (f"{ log_file_path } .verbose.log" )
135- verbose_fh .setFormatter (
150+ verbose_log_artifact = Artifact ("local" , f"{ log_file_name } .verbose.log" )
151+ verbose_handler = StreamHandler (verbose_log_artifact .open ("w" ))
152+ verbose_handler .setFormatter (
136153 logging .Formatter (
137154 "%(asctime)s|%(stage)s|%(name)s|%(levelname)s|%(pathname)s|%(lineno)d|"
138155 "%(funcName)s|%(process)d|%(thread)d|%(threadName)s|%(message)s" ,
139156 datefmt = date_fmt ,
140157 )
141158 )
142- self .addHandler (verbose_fh )
159+ self .addHandler (verbose_handler )
143160
144- return [fh , verbose_fh ]
161+ return [
162+ ArtifactHandler (log_artifact , handler ),
163+ ArtifactHandler (verbose_log_artifact , verbose_handler ),
164+ ]
145165
146- def _remove_extra_file_handlers (self ) -> None :
147- """Remove any extra file handlers that have been added to the logger."""
148- if self ._extra_file_handlers :
149- for extra_file_handler in self ._extra_file_handlers :
150- self .removeHandler (extra_file_handler )
166+ def _remove_extra_artifact_handlers (self ) -> None :
167+ """Remove any extra artifact handlers that have been added to the logger."""
168+ if self ._extra_artifact_handlers :
169+ for extra_artifact_handler in self ._extra_artifact_handlers :
170+ self .removeHandler (extra_artifact_handler . handler )
151171
152- self ._extra_file_handlers = []
172+ self ._extra_artifact_handlers = []
153173
154174
155175def get_dts_logger (name : str | None = None ) -> DTSLogger :
0 commit comments