openrvdas.logger.writers.regex_logfile_writer

No module-level documentation available.
  1#!/usr/bin/env python3
  2
  3import logging
  4import re
  5
  6from logger.utils import timestamp  # noqa: E402
  7from logger.writers.writer import Writer  # noqa: E402
  8from logger.writers.file_writer import FileWriter  # noqa: E402
  9
 10
 11class RegexLogfileWriter(Writer):
 12    """Write to the specified filebase, with datestamp appended. If filebase
 13    is a <regex>:<filebase> dict, write records to every filebase whose
 14    regex appears in the record.
 15    """
 16    def __init__(self, filebase=None, flush=True,
 17                 time_format=timestamp.TIME_FORMAT,
 18                 date_format=timestamp.DATE_FORMAT,
 19                 split_char=' ', suffix='', header=None,
 20                 header_file=None, rollover_hourly=False,
 21                 **kwargs):
 22        """Write timestamped text records to a filebase. The filebase will
 23        have the current date appended, in keeping with R2R format
 24        recommendations (http://www.rvdata.us/operators/directory). When the
 25        timestamped date on records rolls over to next day, create a new file
 26        with the new date suffix.
 27
 28        If filebase is a dict of <string>:<filebase> pairs, The writer will
 29        attempt to match a <string> in the dict to each record it receives.
 30        It will write the record to the filebase corresponding to the first
 31        string it matches (Note that the order of comparison is not
 32        guaranteed!). If no strings match, the record will be written to the
 33        standalone filebase provided.
 34        ```
 35        filebase        A filebase string to write to or a dict mapping
 36                        <string>:<filebase>.
 37
 38        flush           If True (default), flush after every write() call
 39
 40        date_fomat      A strftime-compatible string, such as '%Y-%m-%d';
 41                        defaults to whatever's defined in
 42                        utils.timestamps.DATE_FORMAT.
 43
 44        split_char      Delimiter between timestamp and rest of message
 45
 46        suffix          string to apply to the end of the log filename
 47
 48        header          Add the specified header string to each file.
 49
 50        header_file     Add the content of the specified file to each file.
 51
 52        rollover_hourly Set files to truncate by hour.  By default files will
 53                        truncate by day
 54
 55        quiet           If True, don't complain if a record doesn't match
 56                        any mapped prefix
 57        ```
 58        """
 59        super().__init__(**kwargs)  # processes 'quiet' and type hints
 60
 61        self.filebase = filebase
 62        self.flush = flush
 63        self.time_format = time_format
 64        self.date_format = date_format
 65        self.split_char = split_char
 66        self.suffix = suffix
 67        self.header = header
 68        self.header_file = header_file
 69        self.rollover_hourly = rollover_hourly
 70
 71        # If our filebase is a dict, we're going to be doing our
 72        # fancy pattern->filebase mapping.
 73        self.do_filebase_mapping = isinstance(self.filebase, dict)
 74
 75        if self.do_filebase_mapping:
 76            # Do our matches faster by precompiling
 77            self.compiled_filebase_map = {
 78                pattern: re.compile(pattern) for pattern in self.filebase
 79            }
 80        self.current_filename = {}
 81        self.writer = {}
 82
 83    ############################
 84    def write(self, record: str):
 85        """Note: Assume record begins with a timestamp string."""
 86
 87        # See if it's something we can process, and if not, try digesting
 88        if not self.can_process_record(record):  # inherited from BaseModule()
 89            self.digest_record(record)  # inherited from BaseModule()
 90            return
 91
 92        # Get the timestamp we'll be using
 93        try:  # Try to extract timestamp from record
 94            time_str = record.split(self.split_char)[0]
 95            ts = timestamp.timestamp(time_str, time_format=self.time_format)
 96        except ValueError:
 97            if not self.quiet:
 98                logging.error('LogfileWriter.write() - bad timestamp: "%s"', record)
 99                return
100
101        # Now parse ts into hour and date strings
102        hr_str = self.rollover_hourly and \
103            timestamp.date_str(ts, date_format='_%H00') or ""
104        date_str = timestamp.date_str(ts, date_format=self.date_format)
105        time_str = date_str + hr_str + self.suffix
106        logging.debug('LogfileWriter time_str: %s', time_str)
107
108        # Figure out where we're going to write
109        if self.do_filebase_mapping:
110            matched_patterns = [self.write_if_match(record, pattern, time_str)
111                                for pattern in self.filebase]
112            if True not in matched_patterns:
113                if not self.quiet:
114                    logging.warning(f'No patterns matched in PatternLogfileWriter '
115                                    f'for record "{record}"')
116        else:
117            pattern = 'fixed'  # just an arbitrary fixed pattern
118            filename = self.filebase + '-' + time_str
119            self.write_filename(record, pattern, filename)
120
121    ############################
122    def write_filename(self, record, pattern, filename):
123        """Write record to filename. If it's the first time we're writing to
124        this filename, create the appropriate FileWriter and insert it into
125        the map for the relevant pattern."""
126
127        # Are we currently writing to this file? If not, open/create it.
128        if not filename == self.current_filename.get(pattern):
129            logging.info('LogfileWriter opening new file: %s', filename)
130            self.current_filename[pattern] = filename
131            self.writer[pattern] = FileWriter(filename=filename,
132                                              header=self.header,
133                                              header_file=self.header_file,
134                                              flush=self.flush)
135        # Now, if our logic is correct, should *always* have a matching_writer
136        matching_writer = self.writer.get(pattern)
137        matching_writer.write(record)
138
139    ############################
140    def write_if_match(self, record, pattern, time_str):
141        """If the record matches the pattern, write to the matching filebase."""
142        # Find the compiled regex matching the pattern
143        regex = self.compiled_filebase_map.get(pattern)
144        if not regex:
145            logging.error(f'System error: found no regex pattern matching "{pattern}"!')
146            return None
147
148        # If the pattern isn't in this record, go home quietly
149        if regex.search(record) is None:
150            return None
151
152        # Otherwise, we write.
153        filebase = self.filebase.get(pattern)
154        if filebase is None:
155            logging.error(f'System error: found no filebase matching pattern "{pattern}"!')
156            return None
157
158        filename = filebase + '-' + time_str
159        self.write_filename(record, pattern, filename)
160        return True
class RegexLogfileWriter(logger.writers.writer.Writer):
 12class RegexLogfileWriter(Writer):
 13    """Write to the specified filebase, with datestamp appended. If filebase
 14    is a <regex>:<filebase> dict, write records to every filebase whose
 15    regex appears in the record.
 16    """
 17    def __init__(self, filebase=None, flush=True,
 18                 time_format=timestamp.TIME_FORMAT,
 19                 date_format=timestamp.DATE_FORMAT,
 20                 split_char=' ', suffix='', header=None,
 21                 header_file=None, rollover_hourly=False,
 22                 **kwargs):
 23        """Write timestamped text records to a filebase. The filebase will
 24        have the current date appended, in keeping with R2R format
 25        recommendations (http://www.rvdata.us/operators/directory). When the
 26        timestamped date on records rolls over to next day, create a new file
 27        with the new date suffix.
 28
 29        If filebase is a dict of <string>:<filebase> pairs, The writer will
 30        attempt to match a <string> in the dict to each record it receives.
 31        It will write the record to the filebase corresponding to the first
 32        string it matches (Note that the order of comparison is not
 33        guaranteed!). If no strings match, the record will be written to the
 34        standalone filebase provided.
 35        ```
 36        filebase        A filebase string to write to or a dict mapping
 37                        <string>:<filebase>.
 38
 39        flush           If True (default), flush after every write() call
 40
 41        date_fomat      A strftime-compatible string, such as '%Y-%m-%d';
 42                        defaults to whatever's defined in
 43                        utils.timestamps.DATE_FORMAT.
 44
 45        split_char      Delimiter between timestamp and rest of message
 46
 47        suffix          string to apply to the end of the log filename
 48
 49        header          Add the specified header string to each file.
 50
 51        header_file     Add the content of the specified file to each file.
 52
 53        rollover_hourly Set files to truncate by hour.  By default files will
 54                        truncate by day
 55
 56        quiet           If True, don't complain if a record doesn't match
 57                        any mapped prefix
 58        ```
 59        """
 60        super().__init__(**kwargs)  # processes 'quiet' and type hints
 61
 62        self.filebase = filebase
 63        self.flush = flush
 64        self.time_format = time_format
 65        self.date_format = date_format
 66        self.split_char = split_char
 67        self.suffix = suffix
 68        self.header = header
 69        self.header_file = header_file
 70        self.rollover_hourly = rollover_hourly
 71
 72        # If our filebase is a dict, we're going to be doing our
 73        # fancy pattern->filebase mapping.
 74        self.do_filebase_mapping = isinstance(self.filebase, dict)
 75
 76        if self.do_filebase_mapping:
 77            # Do our matches faster by precompiling
 78            self.compiled_filebase_map = {
 79                pattern: re.compile(pattern) for pattern in self.filebase
 80            }
 81        self.current_filename = {}
 82        self.writer = {}
 83
 84    ############################
 85    def write(self, record: str):
 86        """Note: Assume record begins with a timestamp string."""
 87
 88        # See if it's something we can process, and if not, try digesting
 89        if not self.can_process_record(record):  # inherited from BaseModule()
 90            self.digest_record(record)  # inherited from BaseModule()
 91            return
 92
 93        # Get the timestamp we'll be using
 94        try:  # Try to extract timestamp from record
 95            time_str = record.split(self.split_char)[0]
 96            ts = timestamp.timestamp(time_str, time_format=self.time_format)
 97        except ValueError:
 98            if not self.quiet:
 99                logging.error('LogfileWriter.write() - bad timestamp: "%s"', record)
100                return
101
102        # Now parse ts into hour and date strings
103        hr_str = self.rollover_hourly and \
104            timestamp.date_str(ts, date_format='_%H00') or ""
105        date_str = timestamp.date_str(ts, date_format=self.date_format)
106        time_str = date_str + hr_str + self.suffix
107        logging.debug('LogfileWriter time_str: %s', time_str)
108
109        # Figure out where we're going to write
110        if self.do_filebase_mapping:
111            matched_patterns = [self.write_if_match(record, pattern, time_str)
112                                for pattern in self.filebase]
113            if True not in matched_patterns:
114                if not self.quiet:
115                    logging.warning(f'No patterns matched in PatternLogfileWriter '
116                                    f'for record "{record}"')
117        else:
118            pattern = 'fixed'  # just an arbitrary fixed pattern
119            filename = self.filebase + '-' + time_str
120            self.write_filename(record, pattern, filename)
121
122    ############################
123    def write_filename(self, record, pattern, filename):
124        """Write record to filename. If it's the first time we're writing to
125        this filename, create the appropriate FileWriter and insert it into
126        the map for the relevant pattern."""
127
128        # Are we currently writing to this file? If not, open/create it.
129        if not filename == self.current_filename.get(pattern):
130            logging.info('LogfileWriter opening new file: %s', filename)
131            self.current_filename[pattern] = filename
132            self.writer[pattern] = FileWriter(filename=filename,
133                                              header=self.header,
134                                              header_file=self.header_file,
135                                              flush=self.flush)
136        # Now, if our logic is correct, should *always* have a matching_writer
137        matching_writer = self.writer.get(pattern)
138        matching_writer.write(record)
139
140    ############################
141    def write_if_match(self, record, pattern, time_str):
142        """If the record matches the pattern, write to the matching filebase."""
143        # Find the compiled regex matching the pattern
144        regex = self.compiled_filebase_map.get(pattern)
145        if not regex:
146            logging.error(f'System error: found no regex pattern matching "{pattern}"!')
147            return None
148
149        # If the pattern isn't in this record, go home quietly
150        if regex.search(record) is None:
151            return None
152
153        # Otherwise, we write.
154        filebase = self.filebase.get(pattern)
155        if filebase is None:
156            logging.error(f'System error: found no filebase matching pattern "{pattern}"!')
157            return None
158
159        filename = filebase + '-' + time_str
160        self.write_filename(record, pattern, filename)
161        return True

Write to the specified filebase, with datestamp appended. If filebase is a : dict, write records to every filebase whose regex appears in the record.

RegexLogfileWriter( filebase=None, flush=True, time_format='%Y-%m-%dT%H:%M:%S.%fZ', date_format='%Y-%m-%d', split_char=' ', suffix='', header=None, header_file=None, rollover_hourly=False, **kwargs)
17    def __init__(self, filebase=None, flush=True,
18                 time_format=timestamp.TIME_FORMAT,
19                 date_format=timestamp.DATE_FORMAT,
20                 split_char=' ', suffix='', header=None,
21                 header_file=None, rollover_hourly=False,
22                 **kwargs):
23        """Write timestamped text records to a filebase. The filebase will
24        have the current date appended, in keeping with R2R format
25        recommendations (http://www.rvdata.us/operators/directory). When the
26        timestamped date on records rolls over to next day, create a new file
27        with the new date suffix.
28
29        If filebase is a dict of <string>:<filebase> pairs, The writer will
30        attempt to match a <string> in the dict to each record it receives.
31        It will write the record to the filebase corresponding to the first
32        string it matches (Note that the order of comparison is not
33        guaranteed!). If no strings match, the record will be written to the
34        standalone filebase provided.
35        ```
36        filebase        A filebase string to write to or a dict mapping
37                        <string>:<filebase>.
38
39        flush           If True (default), flush after every write() call
40
41        date_fomat      A strftime-compatible string, such as '%Y-%m-%d';
42                        defaults to whatever's defined in
43                        utils.timestamps.DATE_FORMAT.
44
45        split_char      Delimiter between timestamp and rest of message
46
47        suffix          string to apply to the end of the log filename
48
49        header          Add the specified header string to each file.
50
51        header_file     Add the content of the specified file to each file.
52
53        rollover_hourly Set files to truncate by hour.  By default files will
54                        truncate by day
55
56        quiet           If True, don't complain if a record doesn't match
57                        any mapped prefix
58        ```
59        """
60        super().__init__(**kwargs)  # processes 'quiet' and type hints
61
62        self.filebase = filebase
63        self.flush = flush
64        self.time_format = time_format
65        self.date_format = date_format
66        self.split_char = split_char
67        self.suffix = suffix
68        self.header = header
69        self.header_file = header_file
70        self.rollover_hourly = rollover_hourly
71
72        # If our filebase is a dict, we're going to be doing our
73        # fancy pattern->filebase mapping.
74        self.do_filebase_mapping = isinstance(self.filebase, dict)
75
76        if self.do_filebase_mapping:
77            # Do our matches faster by precompiling
78            self.compiled_filebase_map = {
79                pattern: re.compile(pattern) for pattern in self.filebase
80            }
81        self.current_filename = {}
82        self.writer = {}

Write timestamped text records to a filebase. The filebase will have the current date appended, in keeping with R2R format recommendations (http://www.rvdata.us/operators/directory). When the timestamped date on records rolls over to next day, create a new file with the new date suffix.

If filebase is a dict of : pairs, The writer will attempt to match a in the dict to each record it receives. It will write the record to the filebase corresponding to the first string it matches (Note that the order of comparison is not guaranteed!). If no strings match, the record will be written to the standalone filebase provided.

filebase        A filebase string to write to or a dict mapping
                <string>:<filebase>.

flush           If True (default), flush after every write() call

date_fomat      A strftime-compatible string, such as '%Y-%m-%d';
                defaults to whatever's defined in
                utils.timestamps.DATE_FORMAT.

split_char      Delimiter between timestamp and rest of message

suffix          string to apply to the end of the log filename

header          Add the specified header string to each file.

header_file     Add the content of the specified file to each file.

rollover_hourly Set files to truncate by hour.  By default files will
                truncate by day

quiet           If True, don't complain if a record doesn't match
                any mapped prefix
filebase
flush
time_format
date_format
split_char
suffix
header
header_file
rollover_hourly
do_filebase_mapping
current_filename
writer
def write(self, record: str):
 85    def write(self, record: str):
 86        """Note: Assume record begins with a timestamp string."""
 87
 88        # See if it's something we can process, and if not, try digesting
 89        if not self.can_process_record(record):  # inherited from BaseModule()
 90            self.digest_record(record)  # inherited from BaseModule()
 91            return
 92
 93        # Get the timestamp we'll be using
 94        try:  # Try to extract timestamp from record
 95            time_str = record.split(self.split_char)[0]
 96            ts = timestamp.timestamp(time_str, time_format=self.time_format)
 97        except ValueError:
 98            if not self.quiet:
 99                logging.error('LogfileWriter.write() - bad timestamp: "%s"', record)
100                return
101
102        # Now parse ts into hour and date strings
103        hr_str = self.rollover_hourly and \
104            timestamp.date_str(ts, date_format='_%H00') or ""
105        date_str = timestamp.date_str(ts, date_format=self.date_format)
106        time_str = date_str + hr_str + self.suffix
107        logging.debug('LogfileWriter time_str: %s', time_str)
108
109        # Figure out where we're going to write
110        if self.do_filebase_mapping:
111            matched_patterns = [self.write_if_match(record, pattern, time_str)
112                                for pattern in self.filebase]
113            if True not in matched_patterns:
114                if not self.quiet:
115                    logging.warning(f'No patterns matched in PatternLogfileWriter '
116                                    f'for record "{record}"')
117        else:
118            pattern = 'fixed'  # just an arbitrary fixed pattern
119            filename = self.filebase + '-' + time_str
120            self.write_filename(record, pattern, filename)

Note: Assume record begins with a timestamp string.

def write_filename(self, record, pattern, filename):
123    def write_filename(self, record, pattern, filename):
124        """Write record to filename. If it's the first time we're writing to
125        this filename, create the appropriate FileWriter and insert it into
126        the map for the relevant pattern."""
127
128        # Are we currently writing to this file? If not, open/create it.
129        if not filename == self.current_filename.get(pattern):
130            logging.info('LogfileWriter opening new file: %s', filename)
131            self.current_filename[pattern] = filename
132            self.writer[pattern] = FileWriter(filename=filename,
133                                              header=self.header,
134                                              header_file=self.header_file,
135                                              flush=self.flush)
136        # Now, if our logic is correct, should *always* have a matching_writer
137        matching_writer = self.writer.get(pattern)
138        matching_writer.write(record)

Write record to filename. If it's the first time we're writing to this filename, create the appropriate FileWriter and insert it into the map for the relevant pattern.

def write_if_match(self, record, pattern, time_str):
141    def write_if_match(self, record, pattern, time_str):
142        """If the record matches the pattern, write to the matching filebase."""
143        # Find the compiled regex matching the pattern
144        regex = self.compiled_filebase_map.get(pattern)
145        if not regex:
146            logging.error(f'System error: found no regex pattern matching "{pattern}"!')
147            return None
148
149        # If the pattern isn't in this record, go home quietly
150        if regex.search(record) is None:
151            return None
152
153        # Otherwise, we write.
154        filebase = self.filebase.get(pattern)
155        if filebase is None:
156            logging.error(f'System error: found no filebase matching pattern "{pattern}"!')
157            return None
158
159        filename = filebase + '-' + time_str
160        self.write_filename(record, pattern, filename)
161        return True

If the record matches the pattern, write to the matching filebase.