+ # First try to find a logging level by scope (--lmodule)
+ if len(self.level_by_scope) > 0:
+ min_level = None
+ for scope in (
+ record.module,
+ f'{record.module}:{record.funcName}',
+ f':{record.funcName}'
+ ):
+ level = self.level_by_scope.get(scope, None)
+ if level is not None:
+ if min_level is None or level < min_level:
+ min_level = level
+
+ # If we found one, use it instead of the global default level.
+ if min_level is not None:
+ return record.levelno >= min_level
+
+ # Otherwise, use the global logging level (--logging_level)
+ return record.levelno >= self.default_logging_level
+
+
+# A map from function_identifier -> probability of logging (0.0%..100.0%)
+probabilistic_logging_levels: Mapping[str, float] = {}
+
+
+def logging_is_probabilistic(probability_of_logging: float) -> Callable:
+ """
+ A decorator that indicates that all logging statements within the
+ scope of a particular (marked) function are not deterministic
+ (i.e. they do not always unconditionally log) but rather are
+ probabilistic (i.e. they log N% of the time randomly).
+
+ Note that this functionality can be disabled (forcing all logged
+ messages to produce output) via the --no_logging_probabilistically
+ cmdline argument.
+
+ This affects *ALL* logging statements within the marked function.
+
+ """
+ def probabilistic_logging_wrapper(f: Callable):
+ identifier = function_identifier(f)
+ probabilistic_logging_levels[identifier] = probability_of_logging
+ return f
+ return probabilistic_logging_wrapper
+
+
+class ProbabilisticFilter(logging.Filter):
+ """
+ A filter that logs messages probabilistically (i.e. randomly at some
+ percent chance).
+
+ This filter only affects logging messages from functions that have
+ been tagged with the @logging_utils.probabilistic_logging decorator.
+
+ """
+ @overrides
+ def filter(self, record: logging.LogRecord) -> bool:
+ id1 = f'{record.module}:{record.funcName}'
+ if id1 not in probabilistic_logging_levels:
+ return True
+ threshold = probabilistic_logging_levels[id1]
+ return (random.random() * 100.0) <= threshold
+
+
+class OnlyInfoFilter(logging.Filter):
+ """
+ A filter that only logs messages produced at the INFO logging
+ level. This is used by the logging_info_is_print commandline
+ option to select a subset of the logging stream to send to a
+ stdout handler.
+
+ """
+ @overrides
+ def filter(self, record: logging.LogRecord):
+ return record.levelno == logging.INFO