17

I'm using argparse to get the logging level from the command line and then passing it as input for logging.basicConfig. However, the way I'm trying to implement this is not working. Any suggestion?

Desire behavior, from command line:

python main.py -log=DEBUG

Desire output

DEBUG:__main__: Debug is working

Code

import logging
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-log", "--log", nargs='+', help="Provide logging level. Example --log debug'")

log_level = parser.parse_args().log
log_level = 'logging.'+log_level[0]
print(log_level)
logging.basicConfig(level=log_level)
logger = logging.getLogger(__name__) 
logger.debug(' Debug is working')
Igor Brejc
  • 18,157
  • 12
  • 74
  • 95
Tesla001
  • 343
  • 1
  • 3
  • 7
  • Why do you put `nargs='+'`, the user have to choose only one level, the default notation is that your parameter is optional ([python docs](https://docs.python.org/3.7/library/argparse.html#required))? – ndclt Jul 24 '19 at 23:37
  • @ndclt you are right, nargs='+' is not required and was the main reason why I was getting a list as input. – Tesla001 Jul 24 '19 at 23:45
  • I often recommend `args = parser.parse_args()` followed by `print(args)`. The gives you a clear(er) idea of what the parser has done for you. – hpaulj Jul 25 '19 at 00:01
  • Also see https://stackoverflow.com/questions/14097061 – handle Dec 06 '19 at 09:32

4 Answers4

11

Putting these combinations together, allowing the user to name the level with upper or lower case, allowing only one level to be specified, and picking the explicit level from a dictionary with a default to the WARNING level:

import argparse
import logging
parser = argparse.ArgumentParser()
parser.add_argument(
    "-log", 
    "--log", 
    default="warning",
    help=(
        "Provide logging level. "
        "Example --log debug', default='warning'"),
    ),
)

options = parser.parse_args()
levels = {
    'critical': logging.CRITICAL,
    'error': logging.ERROR,
    'warn': logging.WARNING,
    'warning': logging.WARNING,
    'info': logging.INFO,
    'debug': logging.DEBUG
}
level = levels.get(options.log.lower())
if level is None:
    raise ValueError(
        f"log level given: {options.log}"
        f" -- must be one of: {' | '.join(levels.keys())}")
logging.basicConfig(level=level)
logger = logging.getLogger(__name__)
BarryPye
  • 1,715
  • 2
  • 16
  • 19
9

The basicConfig function can take a string argument for level, and it will check its validity for you, so the code can be much simpler than BarryPye's answer.

import argparse
import logging

parser = argparse.ArgumentParser()
parser.add_argument( '-log',
                     '--loglevel',
                     default='warning',
                     help='Provide logging level. Example --loglevel debug, default=warning' )

args = parser.parse_args()

logging.basicConfig( level=args.loglevel.upper() )
logging.info( 'Logging now setup.' )
Glazed
  • 1,908
  • 17
  • 19
  • This would work better (more readable than replicating the logging levels in a local dict), but I would recommend limiting the possible inputs in this case, with `choices=logging._nameToLevel.keys()` – Nicola Feb 23 '22 at 10:30
3

The level should be a variable from logging not a string, logging.DEBUG for example. I think you have to create a dict matching the given argument and the logging variable:

level_config = {'debug': logging.DEBUG, 'info': logging.INFO} # etc.
log_level = level_config[parser.parse_args().log[0].lower()]

You can also add choices=['debug', 'info', 'warning'] in your add_argument call.

ndclt
  • 2,154
  • 2
  • 10
  • 24
1

log_level = 'logging.'+log_level[0] this just makes the string 'logging.DEBUG' which is not something that basicConfig understands. what it want's is the logging.DEBUG constant which you can get by getattr(logging, log_level[0]). Newer versions of python actually accept the textual representation as well and you can just pass in 'DEBUG' as level.

blues
  • 3,926
  • 2
  • 19
  • 36
  • Hi @blues, after trying log_level = getattr(logging, log_level[0]), logging.basicConfig(level=log_level) I got the following error raise TypeError("Level not an integer or a valid string: %r" % level) TypeError: Level not an integer or a valid string: – Tesla001 Jul 24 '19 at 23:49
  • My guess would be that you did not capitalize the argument. Make sure to supply the argument as `-log=DEBUG` and not `-log=debug`. Or call `.upper()` on it. – blues Jul 25 '19 at 07:08