avatar

目录
python logging best practice

You:

IF i implement a package, how can I handle logger? do I do logging.xxx or logger.xxx?
I have a script to call that package, which has a main logger to local file, as context.

ChatGPT:

When implementing logging in a package that will be used by a script with its own main logger, the best practice is to create a separate logger within your package (usually via logger = logging.getLogger(__name__)). This allows the package to integrate smoothly into any application that imports it, without interfering with the application’s main logging configuration.
Here’s how you can approach it:

  1. In your package:

    • Set up a logger using logging.getLogger(__name__) to get a logger with the module’s name.

    • Don’t configure the logger (e.g., don’t set handlers or formatters). Let the script or application configure the logging.

python
1
2
3
4
5
6
7
8
# Inside your package module (e.g., my_package/my_module.py)
import logging

logger = logging.getLogger(__name__)

def my_function():
logger.info("This is an info log from the package")
logger.error("This is an error log from the package")
  1. In your main script that calls the package:
    • Set up the main logger configuration, including the file handler, formatters, and log level. The logger in your package will propagate messages to this main logger unless explicitly configured otherwise.
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# In your main script (e.g., main_script.py)
import logging
from my_package import my_module

# Configure the main logger
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger(__name__)

def main():
logger.info("This is a log from the main script")
my_module.my_function()

if __name__ == "__main__":
main()

Key Points

  • In the package: Use logger = logging.getLogger(__name__) and avoid configuring logging within the package itself.

  • In the script: Configure logging using logging.basicConfig() or a custom configuration, and the package’s logs will follow the script’s logging setup.

By using this pattern, the package’s logger integrates cleanly into the calling script or application’s logging system, avoiding conflicts or redundant log handlers.
In summary, **When implementing logging in a package that will be used by a script with its own main logger, the best practice is to create a separate logger within your package (usually via logger = logging.getLogger(__name__)). This allows the package to integrate smoothly into any application that imports it, without interfering with the application’s main logging configuration.
Here’s how you can approach it:

  1. In your package:

    • Set up a logger using logging.getLogger(__name__) to get a logger with the module’s name.

    • Don’t configure the logger (e.g., don’t set handlers or formatters). Let the script or application configure the logging.

python
1
2
3
4
5
6
7
8
# Inside your package module (e.g., my_package/my_module.py)
import logging

logger = logging.getLogger(__name__)

def my_function():
logger.info("This is an info log from the package")
logger.error("This is an error log from the package")
  1. In your main script that calls the package:
    • Set up the main logger configuration, including the file handler, formatters, and log level. The logger in your package will propagate messages to this main logger unless explicitly configured otherwise.
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# In your main script (e.g., main_script.py)
import logging
from my_package import my_module

# Configure the main logger
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger(__name__)

def main():
logger.info("This is a log from the main script")
my_module.my_function()

if __name__ == "__main__":
main()

Key Points

  • In the package: Use logger = logging.getLogger(__name__) and avoid configuring logging within the package itself.

  • In the script: Configure logging using logging.basicConfig() or a custom configuration, and the package’s logs will follow the script’s logging setup.

By using this pattern, the package’s logger integrates cleanly into the calling script or application’s logging system, avoiding conflicts or redundant log handlers.
In summary, use logger.xxx within your package (where logger is obtained using logging.getLogger(__name__))** , and handle the logging configuration in the calling script or main application.

You:

Configure the Main Logger

logging.basicConfig(
filename=’app.log’,
level=logging.DEBUG,
format=’%(asctime)s - %(name)s - %(levelname)s - %(message)s’
)

this can be done only once right?
my though is that, if there are two of this being called, there will be problem?

ChatGPT:

Yes, you are correct. The logging.basicConfig() function can only be called once in a given Python process. If it is called multiple times, it will not have any effect after the first call unless the force=True argument is passed (Python 3.8 and later).
Here’s what happens in detail:

  1. logging.basicConfig() configures the root logger. Once the root logger is configured, any subsequent calls to basicConfig() will be ignored.

  2. If you try to call basicConfig() a second time, the logging system won’t reconfigure itself because it is already set up. This can lead to a situation where only the first configuration is applied, and the second one is ignored.

  3. If multiple parts of your program (or different libraries) attempt to configure logging using basicConfig(), there can be unexpected behavior. For instance, you may expect a file handler to be added or the format to change, but this won’t happen if basicConfig() has already been called elsewhere.

How to Avoid Conflicts

  • Ensure basicConfig() is called only once: Generally, the best place to call basicConfig() is at the entry point of your application (e.g., in the main script) and ensure that no other parts of the program call it.

  • Use the force=True argument (Python 3.8+): If you do need to reconfigure logging (e.g., in a test setup), you can call basicConfig() with force=True to reapply the configuration.

python
1
2
3
4
5
6
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
force=True # Reconfigures the logging system even if already set
)
  • Manually configure loggers: If you need more flexibility (such as different logging configurations in different parts of your program), you can manually configure loggers instead of relying on basicConfig().

Here’s an example:

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# In your script or application entry point
import logging

# Configure the main logger
logger = logging.getLogger(__name__)

file_handler = logging.FileHandler('app.log')
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

logger.setLevel(logging.DEBUG)
logger.addHandler(file_handler)

logger.info("Main script logger configured")

By manually configuring loggers like this, you avoid the restriction that basicConfig() can only be called once, and you gain more control over how different parts of your application handle logging.

Best Practice

  • Call logging.basicConfig() only once in your application’s main script.

  • Use logging.getLogger() in modules or packages and let the configuration cascade from the main application’s logging setup.

  • If needed, manually configure specific loggers or use force=True with basicConfig() in special cases (Python 3.8+).

This approach avoids conflicts and ensures consistent logging behavior throughout your application.


评论