Logging#
Tango makes heavy use of the logging
module from the standard library to convey information to users.
When youâre writing your own Step
implementations we encourage you to also use standard
Python logging as opposed to print()
or other functions that write directly to stdout
or stderr
.
This is easy enough since each Step
class already comes with its own logger:
Step.logger
.
When using the Tango CLI you can set the log level in several different ways:
Through a Tango global settings file.
With the environment variable
TANGO_LOG_LEVEL
.Or with the
--log-level
command-line option.
In some cases (like when running on Beaker) you may also want to enable âfile friendly loggingâ.
Configuring logging in your own CLI#
If youâre writing your own CLI that uses tango, you can utilize the initialize_logging()
function to easily configure logging properly.
For example,
from tango.common.logging import initialize_logging, teardown_logging
initialize_logging(log_level="info")
logger = logging.getLogger()
logger.info("Running script!")
teardown_logging()
[...] INFO Running script! ...
If you want to have logs written to a file, you can use the file_handler()
context manager.
Logging from worker processes or threads#
If you have steps or other functions that spawn workers, and you want to enable logging within
those workers, you can call the initialize_worker_logging()
function to configure
logging within each worker. This assumes that youâve called initialize_logging()
from the
main process (the tango CLI does this for you).
For example,
import logging
import multiprocessing as mp
from tango import Step
from tango.common.logging import initialize_worker_logging
@Step.register("multiprocessing_step_example")
class MultiprocessingStep(Step):
def run(self, num_proc: int = 2) -> bool: # type: ignore
workers = []
for i in range(num_proc):
worker = mp.Process(target=_worker_function, args=(i,))
workers.append(worker)
worker.start()
for worker in workers:
worker.join()
return True
def _worker_function(worker_id: int):
initialize_worker_logging(worker_rank=worker_id)
logger = logging.getLogger(MultiprocessingStep.__name__)
logger.info("Hello from worker %d!", worker_id)
Reference#
- tango.common.logging.TANGO_LOG_LEVEL: Optional[str] = None#
The log level to use globally. The value can be set from the corresponding environment variable (
TANGO_LOG_LEVEL
) or field in aTangoGlobalSettings
file (log_level
), or from the command line with the--log-level
option. Possible values are âdebugâ, âinfoâ, âwarningâ, or âerrorâ (not case sensitive). For example,$ tango --log-level info run ...
Note
This does not affect the
cli_logger
or logs fromTqdm
progress bars.
- tango.common.logging.FILE_FRIENDLY_LOGGING: bool = False#
If this flag is set to
True
, we remove special styling characters from log messages, add newlines toTqdm
output even on an interactive terminal, and we slow downTqdm
âs output to only once every 10 seconds.Attention
Unfortunately this wonât affect
tqdm
output from other libraries that donât use TangoâsTqdm
wrapper.By default, it is set to
False
. It can be changed by setting the corresponding environment variable (FILE_FRIENDLY_LOGGING
) or field in aTangoGlobalSettings
file (file_friendly_logging
) to âtrueâ or âfalseâ, or from the command line with the--file-friendly-logging
flag. For example,$ tango --file-friendly-logging run ...
- tango.common.logging.cli_logger = <Logger tango.__main__ (WARNING)>#
A logger that emits messages directly to stdout/stderr using richâs
Console
class.This provides a convenient way for command-line apps to log pretty, styled messages uses the markup style provided by rich.
- tango.common.logging.initialize_logging(*, log_level=None, enable_cli_logs=None, file_friendly_logging=None)[source]#
Initialize logging, which includes setting the global log level, format, and configuring handlers.
Tip
This should be called as early on in your script as possible.
Tip
You should also call
teardown_logging()
as the end of your script.Tip
For worker threads/processes, use
initialize_worker_logging()
instead.- Parameters:
log_level (
Optional
[str
], default:None
) â Can be one of âdebugâ, âinfoâ, âwarningâ, âerrorâ. Defaults to the value ofTANGO_LOG_LEVEL
, if set, or âerrorâ.enable_cli_logs (
Optional
[bool
], default:None
) â Set toTrue
to enable messages from thecli_logger
.file_friendly_logging (
Optional
[bool
], default:None
) â Enable or disable file friendly logging. Defaults to the value ofFILE_FRIENDLY_LOGGING
.
- tango.common.logging.initialize_worker_logging(worker_rank=None)[source]#
Initialize logging in a worker thread/process.
- tango.common.logging.initialize_prefix_logging(*, log_level=None, prefix=None, main_process=False)[source]#
Initialize logging with a prefix.
- Parameters:
log_level (
Optional
[str
], default:None
) â Can be one of âdebugâ, âinfoâ, âwarningâ, âerrorâ. Defaults to the value ofTANGO_LOG_LEVEL
, if set, or âerrorâ.prefix (
Optional
[str
], default:None
) â The string prefix to add to the log message.main_process (
bool
, default:False
) â Whether it is for the main/worker process.
- tango.common.logging.teardown_logging()[source]#
Cleanup any logging fixtures created from
initialize_logging()
. Should be called at the end of your script.
- tango.common.logging.file_handler(filepath)[source]#
A context manager that can be used to route logs to a file by adding a
logging.FileHandler
to the root loggerâs handlers.For example,
from tango.common.logging import initialize_logging, file_handler, teardown_logging initialize_logging(log_level="info") logger = logging.getLogger() logger.info("Hi!") with file_handler("log.out"): logger.info("This message should also go into 'log.out'") teardown_logging()
- Return type:
AbstractContextManager
[None
]