From 0e3cdad5a6aa58714789b920012a0ee0cd61964d Mon Sep 17 00:00:00 2001 From: Soufiane Fariss Date: Tue, 24 Sep 2024 15:49:11 +0200 Subject: [PATCH] logging: set up logging in `capa.main` this commit sets up logging in `capa.main` and uses a shared `log_console` in `capa.helpers` for logging purposes --- capa/__init__.py | 26 ----------------------- capa/capabilities/dynamic.py | 4 +++- capa/capabilities/static.py | 4 +++- capa/helpers.py | 5 +++++ capa/main.py | 40 ++++++++++++++++++++++++++++-------- scripts/profile-time.py | 2 +- 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/capa/__init__.py b/capa/__init__.py index 0b919767c..e69de29bb 100644 --- a/capa/__init__.py +++ b/capa/__init__.py @@ -1,26 +0,0 @@ -# Copyright (C) 2024 Mandiant, Inc. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: [package root]/LICENSE.txt -# Unless required by applicable law or agreed to in writing, software distributed under the License -# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and limitations under the License. -import logging -from logging import Formatter - -from rich.logging import RichHandler - -# use [/] after the logger name to reset any styling, -# and prevent the color from carrying over to the message -LOGFORMAT = "[grey54]%(name)s[/]: %(message)s" - -logging.basicConfig(level=logging.NOTSET) -logger = logging.getLogger() - -if logger.hasHandlers(): - logger.handlers.clear() - -# markup=True, to allow the use of Rich's markup syntax in log messages -rich_handler = RichHandler(level=logging.NOTSET, markup=True, show_time=False) -rich_handler.setFormatter(Formatter(LOGFORMAT)) -logger.addHandler(rich_handler) diff --git a/capa/capabilities/dynamic.py b/capa/capabilities/dynamic.py index be79cf959..2a433be4e 100644 --- a/capa/capabilities/dynamic.py +++ b/capa/capabilities/dynamic.py @@ -138,7 +138,9 @@ def find_dynamic_capabilities( processes: List[ProcessHandle] = list(extractor.get_processes()) n_processes: int = len(processes) - with capa.helpers.CapaProgressBar(transient=True, disable=disable_progress) as pbar: + with capa.helpers.CapaProgressBar( + console=capa.helpers.log_console, transient=True, disable=disable_progress + ) as pbar: task = pbar.add_task("matching", total=n_processes, unit="processes") for p in processes: process_matches, thread_matches, call_matches, feature_count = find_process_capabilities( diff --git a/capa/capabilities/static.py b/capa/capabilities/static.py index 04d2e1262..aeb710ae3 100644 --- a/capa/capabilities/static.py +++ b/capa/capabilities/static.py @@ -145,7 +145,9 @@ def find_static_capabilities( n_libs: int = 0 percentage: float = 0 - with capa.helpers.CapaProgressBar(transient=True, disable=disable_progress) as pbar: + with capa.helpers.CapaProgressBar( + console=capa.helpers.log_console, transient=True, disable=disable_progress + ) as pbar: task = pbar.add_task( "matching", total=n_funcs, unit="functions", postfix=f"skipped {n_libs} library functions, {percentage}%" ) diff --git a/capa/helpers.py b/capa/helpers.py index 0085b65f4..ad1e4bd91 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -16,6 +16,7 @@ from datetime import datetime import msgspec.json +from rich.console import Console from rich.progress import ( Task, Text, @@ -58,6 +59,10 @@ logger = logging.getLogger("capa") +# shared console used to redirect logging to stderr +log_console: Console = Console(stderr=True) + + def hex(n: int) -> str: """render the given number using upper case hex, like: 0x123ABC""" if n < 0: diff --git a/capa/main.py b/capa/main.py index 6216ce188..a9c959ba3 100644 --- a/capa/main.py +++ b/capa/main.py @@ -124,8 +124,24 @@ E_INVALID_INPUT_FORMAT = 25 E_INVALID_FEATURE_EXTRACTOR = 26 +root_logger = logging.getLogger() +logging.basicConfig(level=logging.NOTSET) + +# use [/] after the logger name to reset any styling, +# and prevent the color from carrying over to the message +LOGFORMAT = "[dim]%(name)s[/]: %(message)s" + +if root_logger.hasHandlers(): + root_logger.handlers.clear() + +# markup=True, to allow the use of Rich's markup syntax in log messages +rich_handler = RichHandler( + level=logging.NOTSET, markup=True, show_time=False, show_path=True, console=capa.helpers.log_console +) +rich_handler.setFormatter(logging.Formatter(LOGFORMAT)) +root_logger.addHandler(rich_handler) + logger = logging.getLogger("capa") -logger.propagate = False class FilterConfig(TypedDict, total=False): @@ -404,14 +420,6 @@ def handle_common_args(args): raises: ShouldExitError: if the program is invoked incorrectly and should exit. """ - # use [/] after the logger name to reset any styling, - # and prevent the color from carrying over to the message - LOGFORMAT = "[dim]%(name)s[/]: %(message)s" - - # markup=True, to allow the use of Rich's markup syntax in log messages - rich_handler = RichHandler(markup=True, show_time=False) - rich_handler.setFormatter(logging.Formatter(LOGFORMAT)) - logger.addHandler(rich_handler) if args.quiet: logging.basicConfig(level=logging.WARNING) @@ -426,6 +434,20 @@ def handle_common_args(args): # disable vivisect-related logging, it's verbose and not relevant for capa users set_vivisect_log_level(logging.CRITICAL) + # logger = logging.getLogger() + + # # use [/] after the logger name to reset any styling, + # # and prevent the color from carrying over to the message + # LOGFORMAT = "[dim]%(name)s[/]: %(message)s" + + # if logger.hasHandlers(): + # logger.handlers.clear() + + # # markup=True, to allow the use of Rich's markup syntax in log messages + # rich_handler = RichHandler(level=logging.NOTSET, markup=True, show_time=False, show_path=False, console=Console(stderr=True)) + # rich_handler.setFormatter(logging.Formatter(LOGFORMAT)) + # logger.addHandler(rich_handler) + if isinstance(sys.stdout, io.TextIOWrapper) or hasattr(sys.stdout, "reconfigure"): # from sys.stdout type hint: # diff --git a/scripts/profile-time.py b/scripts/profile-time.py index e89f2e9cb..84d48c6c4 100644 --- a/scripts/profile-time.py +++ b/scripts/profile-time.py @@ -93,7 +93,7 @@ def main(argv=None): except capa.main.ShouldExitError as e: return e.status_code - with capa.helpers.CapaProgressBar() as progress: + with capa.helpers.CapaProgressBar(console=capa.helpers.log_console) as progress: total_iterations = args.number * args.repeat task = progress.add_task("profiling", total=total_iterations)