Module log_verifier.

LogVer

The LogVer class is intended to be used during testing to allow a pytest test case to verify that the code under test issued log messages as expected. This is done by providing a collection of regex patterns that the LogVer will match up to the issued log messages. A report is printed to the syslog to show any patterns or messages that failed to match, and optionally the log messages that did match.

Example1:

pytest test case logs a message and verifies

from scottbrian_utils.log_verifier import LogVer
import logging
def test_example1(caplog: pytest.LogCaptureFixture) -> None:
    t_logger = logging.getLogger("example_1")
    log_ver = LogVer(log_name="example_1")
    log_msg = "hello"
    log_ver.add_pattern(pattern=log_msg)
    t_logger.debug(log_msg)
    match_results = log_ver.get_match_results(caplog)
    log_ver.print_match_results(match_results, print_matched=True)
    log_ver.verify_match_results(match_results)

The output from LogVer.print_match_results() for test_example1:

************************************************
*             log verifier results             *
************************************************
Start: Thu Apr 11 2024 19:24:28
End: Thu Apr 11 2024 19:24:28
Elapsed time: 0:00:00.006002

************************************************
*                summary stats                 *
************************************************
    type  records  matched  unmatched
patterns        1        1          0
log_msgs        1        1          0

***********************
* unmatched patterns: *
***********************
*** no unmatched patterns found ***

***********************
* unmatched log_msgs: *
***********************
*** no unmatched log messages found ***

***********************
*  matched log_msgs:  *
***********************
log_name  level log_msg records matched unmatched
example_1    10 hello         1       1         0
Example2:

pytest test case expects two log records, only one is issued

from scottbrian_utils.log_verifier import LogVer
import logging

def test_example2(caplog: pytest.LogCaptureFixture) -> None:
    t_logger = logging.getLogger("example_2")
    log_ver = LogVer(log_name="example_2")
    log_msg1 = "hello"
    log_ver.add_pattern(pattern=log_msg1)
    log_msg2 = "goodbye"
    log_ver.add_pattern(pattern=log_msg2)
    t_logger.debug(log_msg1)
    match_results = log_ver.get_match_results(caplog)
    log_ver.print_match_results(match_results, print_matched=True)
    with pytest.raises(UnmatchedPatterns):
        log_ver.verify_match_results(match_results)

The output from LogVer.print_match_results() for test_example2:

************************************************
*             log verifier results             *
************************************************
Start: Thu Apr 11 2024 19:24:28
End: Thu Apr 11 2024 19:24:28
Elapsed time: 0:00:00.006002

************************************************
*                summary stats                 *
************************************************
    type  records  matched  unmatched
patterns        2        1          1
log_msgs        1        1          0

***********************
* unmatched patterns: *
***********************
log_name  level pattern fullmatch records matched unmatched
example_2    10 goodbye True            1       0         1

***********************
* unmatched log_msgs: *
***********************
*** no unmatched log messages found ***

***********************
*  matched log_msgs:  *
***********************
log_name  level log_msg records matched unmatched
example_2    10 hello         1       1         0
Example3:

pytest test case expects one log record, two were issued

from scottbrian_utils.log_verifier import LogVer
import logging
def test_example3(caplog: pytest.LogCaptureFixture) -> None:
    t_logger = logging.getLogger("example_3")
    log_ver = LogVer(log_name="example_3")
    log_msg1 = "hello"
    log_ver.add_pattern(pattern=log_msg1)
    log_msg2 = "goodbye"
    t_logger.debug(log_msg1)
    t_logger.debug(log_msg2)
    log_ver.print_match_results(
        match_results := log_ver.get_match_results(caplog),
        print_matched=True
    )
    with pytest.raises(UnmatchedLogMessages):
        log_ver.verify_match_results(match_results)

The output from LogVer.print_match_results() for test_example3:

************************************************
*             log verifier results             *
************************************************
Start: Thu Apr 11 2024 19:24:28
End: Thu Apr 11 2024 19:24:28
Elapsed time: 0:00:00.006002

************************************************
*                summary stats                 *
************************************************
    type  records  matched  unmatched
patterns        1        1          0
log_msgs        2        1          1

***********************
* unmatched patterns: *
***********************
*** no unmatched patterns found ***

***********************
* unmatched log_msgs: *
***********************
log_name  level log_msg records matched unmatched
example_3    10 goodbye       1       0         1

***********************
*  matched log_msgs:  *
***********************
log_name  level log_msg records matched unmatched
example_3    10 hello         1       1         0
Example4:

pytest test case expect two log records, two were issued, one different

from scottbrian_utils.log_verifier import LogVer
import logging
def test_example4(caplog: pytest.LogCaptureFixture) -> None:
    t_logger = logging.getLogger("example_4")
    log_ver = LogVer(log_name="example_4")
    log_msg1 = "hello"
    log_ver.add_pattern(pattern=log_msg1)
    log_msg2a = "goodbye"
    log_ver.add_pattern(pattern=log_msg2a)
    log_msg2b = "see you soon"
    t_logger.debug(log_msg1)
    t_logger.debug(log_msg2b)
    log_ver.print_match_results(
        match_results := log_ver.get_match_results(caplog),
        print_matched=True
    )
    with pytest.raises(UnmatchedPatterns):
        log_ver.verify_match_results(match_results)

The output from LogVer.print_match_results() for test_example4:

************************************************
*             log verifier results             *
************************************************
Start: Thu Apr 11 2024 19:24:28
End: Thu Apr 11 2024 19:24:28
Elapsed time: 0:00:00.006002

************************************************
*                summary stats                 *
************************************************
    type  records  matched  unmatched
patterns        2        1          1
log_msgs        2        1          1

***********************
* unmatched patterns: *
***********************
log_name  level pattern fullmatch records matched unmatched
example_4    10 goodbye True            1       0         1

***********************
* unmatched log_msgs: *
***********************
log_name  level log_msg      records matched unmatched
example_4    10 see you soon       1       0         1

***********************
*  matched log_msgs:  *
***********************
log_name  level log_msg records matched unmatched
example_4    10 hello         1       1         0

The log_verifier module contains:

  1. LogVer class with methods:

    1. add_call_seq

    2. get_call_seq

    3. add_pattern

    4. get_match_results

    5. print_match_results

    6. verify_match_results

class log_verifier.LogVer(log_name='root', str_col_width=None)

Log Message Verification Class.

Initialize a LogVer object.

Parameters:
  • log_name (str) – name of the logger

  • str_col_width (Optional[int]) – If specifief, limits the maximum width for string values in the display produced by method print_match_results. The string values that are limited are for columns log_name, log_msg, pattern, and fullmatch. The specified limit must be an int with a value of 9 or greater.

Example: create a logger and a LogVer instance >>> logger = logging.getLogger(‘example_logger’) >>> log_ver = LogVer(‘example_logger’)

add_call_seq(name, seq)

Add a call sequence for a given name.

Parameters:
  • name (str) – name for whom the call sequence represents

  • seq (str) – the call sequence in a format as described by get_formatted_call_sequence in diag_msg.py from the scottbrian_utils package

Return type:

None

add_msg(log_msg, log_level=10, log_name=None, fullmatch=False)

Add a message to the expected log messages.

Parameters:
  • log_msg (str) – expected message to add

  • log_level (int) – expected logging level

  • log_name (Optional[str]) – expected logger name

  • fullmatch (bool) – if True, use regex fullmatch instead of match in method get_match_results

Return type:

None

Deprecated since version 3.0.0: Use method add_pattern() instead.

add_pattern(pattern, level=10, log_name=None, fullmatch=True)

Add a pattern to be matched to a log message.

Parameters:
  • pattern (str) – pattern to use to find log_msg in the log

  • level (int) – logging level to use

  • log_name (Optional[str]) – logger name to use

  • fullmatch (bool) – if True, use regex fullmatch in method get_match_results, otherwise use regex match

Return type:

None

Added in version 3.0.0: Method add_pattern() replaces method add_msg().

Example: add two patterns, each at a different level

def test_example(caplog: pytest.LogCaptureFixture) -> None:
    t_logger = logging.getLogger("example_5")
    log_ver = LogVer("example_5")
    log_msg1 = "hello"
    log_msg2 = "goodbye"
    log_ver.add_pattern(pattern=log_msg1)
    log_ver.add_pattern(pattern=log_msg2,
                        level=logging.ERROR)
    t_logger.debug(log_msg1)
    t_logger.error(log_msg2)
    match_results = log_ver.get_match_results(caplog=caplog)
    log_ver.print_match_results(match_results,
                                print_matched=True)
    log_ver.verify_match_results(match_results)

The output from LogVer.print_match_results() for test_example:

************************************************
*             log verifier results             *
************************************************
Start: Thu Apr 11 2024 19:24:28
End: Thu Apr 11 2024 19:24:28
Elapsed time: 0:00:00.006002

************************************************
*                summary stats                 *
************************************************
    type  records  matched  unmatched
patterns        2        2          0
log_msgs        2        2          0

***********************
* unmatched patterns: *
***********************
*** no unmatched patterns found ***

***********************
* unmatched log_msgs: *
***********************
*** no unmatched log messages found ***

***********************
*  matched log_msgs:  *
***********************
log_name  level log_msg records matched unmatched
example_5    10 hello         1       1         0
example_5    40 goodbye       1       1         0
get_call_seq(name)

Retrieve a call sequence by name.

Parameters:

name (str) – name for whom the call sequence represents

Return type:

str

Returns:

the call sequence in a format as described by

get_formatted_call_sequence in diag_msg.py with the regex string “:[0-9]*” appended to represent the source line number to match

get_match_results(caplog)

Match the patterns to log records.

Parameters:

caplog (LogCaptureFixture) – pytest fixture that captures log messages

Return type:

MatchResults

Returns:

MatchResults object, which contain the results of the matching operation. This will include a summary for the patterns and messages, and the data frames containing the patterns and messaged used to print and verify the results.

print_df(df_to_print, col_names, left_justify_col_names)

Prin the data set to screen.

Parameters:
  • df_to_print (DataFrame) – the dataframe to print

  • col_names (list[str]) – list of column names to be printed

  • left_justify_col_names (list[str]) – list of column names to be printed left justified

Return type:

None

print_match_results(match_results, print_matched=False)

Print the match results.

Parameters:
  • match_results (MatchResults) – contains the results to be printed

  • print_matched (bool) – if True, print the matched records, otherwise skip printing the matched records

Return type:

None

Changed in version 3.0.0: print_matched keyword default changed to False

static search_df(avail_df, search_arg_df, search_targ_df, targ_work_grp, min_potential_matches)

Search the data frames for matches.

Parameters:
  • avail_df (DataFrame) – data frame of available entries

  • search_arg_df (DataFrame) – data frame that has the search arg

  • search_targ_df (DataFrame) – data frame that has the search target

  • targ_work_grp (DataFrame) – work group dataframe that has the target avail count

  • min_potential_matches (int) – the currently known minimum number of non-zero potential matches that need to be processed

Return type:

None

test_msg(log_msg, level=10)

Issue a log msg and add its pattern.

Parameters:
  • log_msg (str) – log message to issue

  • level (int) – logging level to use

Return type:

None

Notes

  1. This method makes it easier to issue a log message in a test case by also adding the pattern.

Added in version 3.0.0.

Example: issue a test msg

def test_example(caplog: pytest.LogCaptureFixture) -> None:
    log_ver = LogVer("example_6")
    log_ver.test_msg("my test message")

    match_results = log_ver.get_match_results(caplog=caplog)
    log_ver.print_match_results(match_results,
                                print_matched=True)
    log_ver.verify_match_results(match_results)

The output from LogVer.print_match_results() for test_example:

************************************************
*             log verifier results             *
************************************************
Start: Thu Apr 11 2024 19:24:28
End: Thu Apr 11 2024 19:24:28
Elapsed time: 0:00:00.006002

************************************************
*                summary stats                 *
************************************************
    type  records  matched  unmatched
patterns        1        1          0
log_msgs        1        1          0

***********************
* unmatched patterns: *
***********************
*** no unmatched patterns found ***

***********************
* unmatched log_msgs: *
***********************
*** no unmatched log messages found ***

***********************
*  matched log_msgs:  *
***********************
log_name  level log_msg     records matched unmatched
example_6    10 my test msg       1       1         0
static verify_log_results(match_results)

Verify that each log message issued is as expected.

Parameters:

match_results (MatchResults) – contains the results to be verified

Return type:

None

Deprecated since version 3.0.0: Use method verify_match_results() instead.

Raises:
  • UnmatchedExpectedMessages – There are expected log messages that failed to match actual log messages.

  • UnmatchedActualMessages – There are actual log messages that failed to match expected log messages.

static verify_match_results(match_results)

Verify that each log message issued is as expected.

Parameters:

match_results (MatchResults) – contains the results to be verified

Raises:
  • UnmatchedPatterns – One or more patterns failed to match their intended log messages. The patterns and/or the log messages may have been incorrectly specified.

  • UnmatchedLogMessages – One or more log messages failed to be matched by corresponding patterns. The patterns and/or the log messages may have been incorrectly specified.

Return type:

None