Short name describing what triggered the graph break
logging.Logger method not supported for non-export cases
Values or code snippet captured at the break point
method: {self.value}.{name}, args: {args}, kwargs: {kwargs}
Explanation of why the graph break was triggered
logging.Logger methods are not supported for non-export cases.
Hints on how to resolve the graph break
torch._dynamo.config.ignore_logging_functions. Dynamo will skip the call and return None.logger.{name}(...), use torch._dynamo.config.ignore_logging_functions.add(logger.{name}). If {name} is defined on the logger class, add the class method {logger_cls_name}.{name} to ignore this method for all instances of that class.{name}) is checked against the ignore set. Ignoring a method that {name} calls internally has no effect.torch._higher_order_ops.print(...), (2) wrap the logging call in a custom op (marked as mutable), or (3) preserve the logging contents and move the logging call outside the compiled region.This graph break happens when you call a logging.Logger method (e.g.
logger.info(...), logger.debug(...)) inside a compiled region. Logging has
side effects that Dynamo cannot safely fold into the graph, so it breaks rather
than risk dropping or reordering log output.
Example code that causes the graph break is:
import logging
import torch
logger = logging.getLogger(__name__)
@torch.compile(fullgraph=True)
def fn(x):
logger.info("processing tensor of shape %s", x.shape)
return x + 1
fn(torch.randn(3))
If you do not need the log line to fire from inside the compiled region, the simplest fix is to move it outside the compiled region:
import logging
import torch
logger = logging.getLogger(__name__)
@torch.compile(fullgraph=True)
def fn(x):
return x + 1
x = torch.randn(3)
logger.info("processing tensor of shape %s", x.shape)
fn(x)
If you want to keep the logging call where it is, add the method to
torch._dynamo.config.ignore_logging_functions. Note that this makes the call a
no-op inside the compiled region: the graph break goes away, but the log message
is not emitted at all.
import logging
import torch
logger = logging.getLogger(__name__)
torch._dynamo.config.ignore_logging_functions.add(logging.Logger.info)
@torch.compile(fullgraph=True)
def fn(x):
logger.info("processing tensor of shape %s", x.shape) # silently skipped
return x + 1
fn(torch.randn(3))
reorderable_logging_functionsYou may have seen torch._dynamo.config.reorderable_logging_functions used to
handle print and similar calls (it lets Dynamo reorder the call to run after
the compiled graph, so the output is preserved). It does not apply to this
graph break: bound logging.Logger methods (logger.info, logger.debug, …)
go through a different code path that only consults ignore_logging_functions.
Adding logging.Logger.info (bound or unbound) to
reorderable_logging_functions will not prevent the break.
reorderable_logging_functions only works for free functions such as print,
warnings.warn, and the module-level logging helpers (logging.info,
logging.warning, …). So if you need the log output preserved (rather than
skipped as with ignore_logging_functions), one option is to call the
module-level function and register it:
import logging
import torch
torch._dynamo.config.reorderable_logging_functions.add(logging.info)
@torch.compile(fullgraph=True)
def fn(x):
logging.info("processing tensor %s", x) # reordered, still emitted
return x + 1
fn(torch.randn(3))
Two caveats. First, the arguments to a reordered logging call must be tensors,
constants, or string formatters; passing something else (for example
x.shape, a torch.Size object) raises a different graph break, “attempted to
reorder a debugging function that can’t actually be reordered.” Second,
logging.info logs via the root logger, which is not always equivalent to
logging through a specific named logger; moving the call out of the compiled
region (above) is the most faithful fix.