| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- # This file is part of DarkFi (https://dark.fi)
- #
- # Copyright (C) 2020-2026 Dyne.org foundation
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Affero General Public License as
- # published by the Free Software Foundation, either version 3 of the
- # License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Affero General Public License for more details.
- #
- # You should have received a copy of the GNU Affero General Public License
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- import os
- import logging
- from logging.handlers import RotatingFileHandler
- """
- Module: log.py
- This module provides functionality to setup logging for the explorer Flask application.
- """
- def setup_logger(app, env):
- """
- Sets up logging for the explorer Flask app by setting up error, application, and request logging.
- The error logger captures application errors and logs them to a dedicated error log file. The application
- logger handles general application-level logs such as debug or informational messages. Additionally, the
- request logger, derived from the Werkzeug logger, manages HTTP request logs and directs them to the
- same file as the application logger.
- The overall log level is determined by the LOG_LEVEL environment variable, defaulting to INFO if the
- variable is not set or contains an invalid value. The path where logs are stored is obtained from the
- application's TOML configuration file under the 'log_path' entry. If not specified, it defaults to the
- current directory.
- Args:
- app (Flask): The Flask application instance.
- env (str): The environment (e.g., 'localnet', 'mainnet', 'testnet', etc.).
- """
- log_path = app.config.get('log_path', '.')
- # Expand the path if home directory is specified
- log_path = os.path.expanduser(log_path)
- # Ensure the log path exists or create it if not
- if not os.path.exists(log_path):
- try:
- os.makedirs(log_path)
- print(f"created log dir: {log_path}")
- except OSError as e:
- raise RuntimeError(f"Unable to create log directory at '{log_path}': {e}")
- # Get log level from environment variable, default to INFO
- log_level_name = os.environ.get('LOG_LEVEL', 'INFO').upper()
- try:
- log_level = getattr(logging, log_level_name)
- except AttributeError:
- if log_level_name:
- app.logger.warning(f"Invalid LOG_LEVEL '{log_level_name}'. Defaulting to INFO.")
- log_level = logging.INFO
- # App logger setup
- app_logger = setup_app_logger(log_path, env, log_level)
- app.logger = app_logger
- # Request logger setup
- app_log_file = os.path.join(log_path, 'app.log')
- setup_request_logger(app_log_file, env, log_level)
- # Error logger setup
- error_logger = setup_error_logger(log_path, env)
- app.error_logger = error_logger
- def setup_error_logger(log_path, env):
- """
- Configures the error logger to capture application errors, returning an error logger instance.
- Args:
- log_path (str): Path to the directory where logs are stored.
- env (str): The application environment.
- Returns:
- logging.Logger: Configured error logger instance.
- """
- error_logger = logging.getLogger('error_logger')
- error_log_file = os.path.join(log_path, 'error.log')
- error_handler = initialize_log_handler(error_log_file, env)
- error_handler.setLevel(logging.ERROR)
- error_formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]')
- error_handler.setFormatter(error_formatter)
- error_logger.addHandler(error_handler)
- error_logger.setLevel(logging.ERROR)
- error_logger.propagate = False
- add_console_handler_if_localnet(env, error_logger, logging.ERROR)
- return error_logger
- def setup_app_logger(log_path, env, log_level=logging.INFO):
- """
- Configures the app logger for general application logging, returning an app logger instance.
- Args:
- log_path (str): Path to the directory where logs are stored.
- env (str): The application environment.
- log_level (int): The logging level (default is INFO).
- """
- app_logger = logging.getLogger('app_logger')
- app_log_file = os.path.join(log_path, 'app.log')
- app_handler = initialize_log_handler(app_log_file, env)
- app_handler.setLevel(log_level)
- app_formatter = logging.Formatter('%(asctime)s %(message)s')
- app_handler.setFormatter(app_formatter)
- app_logger.addHandler(app_handler)
- app_logger.setLevel(log_level)
- app_logger.propagate = False
- add_console_handler_if_localnet(env, app_logger, log_level)
- return app_logger
- def setup_request_logger(log_file, env, log_level=logging.INFO):
- """
- Configures the request logger to handle HTTP request logs based on the specified environment.
- If the environment is set to 'localnet', HTTP requests are logged to the console to facilitate
- local development and debugging. For all other environments, such as 'testnet' or 'mainnet',
- HTTP requests are logged to the specified log file, ensuring that logs are persisted in a location
- appropriate for testing or production use.
- Args:
- log_file (str): Path to the log file where HTTP requests should be logged.
- env (str): The application environment (e.g., 'localnet', 'testnet', 'mainnet').
- log_level (int): The logging level (default is INFO).
- """
- # Get the werkzeug logger that logs requests
- request_logger = logging.getLogger('werkzeug')
- request_logger.setLevel(log_level)
- request_logger.propagate = False
- file_handler = logging.FileHandler(log_file)
- file_handler.setLevel(log_level)
- request_logger.addHandler(file_handler)
- add_console_handler_if_localnet(env, request_logger, log_level)
- def initialize_log_handler(log_file, env):
- """
- Initializes and returns a log handler based on the environment.
- Args:
- log_file (str): Path to the log file.
- env (str): The environment (e.g., 'mainnet', 'testnet', etc.).
- """
- if env == "mainnet":
- return RotatingFileHandler(log_file, maxBytes=100_000_000, backupCount=5)
- else:
- return logging.FileHandler(log_file)
- def add_console_handler_if_localnet(env, logger, log_level=logging.INFO):
- """
- Adds a console handler to the given logger if the environment is 'localnet'.
- Args:
- env (str): The current environment (e.g., 'localnet', 'mainnet').
- logger (logging.Logger): The logger to which the console handler should be added.
- log_level (int): The logging level for the console handler.
- """
- # If localnet, also log to console
- if env == 'localnet':
- console_handler = logging.StreamHandler()
- console_handler.setLevel(log_level)
- formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
- console_handler.setFormatter(formatter)
- logger.addHandler(console_handler)
|