code: def log_metrics(self,
                      train_metrics: dict,
                      val_metrics: dict = None,
                      epoch: int = None,
                      log_to_console: bool = False) -> None:
          
          metric_names = set(train_metrics.keys())
          if val_metrics is not None:
              metric_names.update(val_metrics.keys())
          val_metrics = val_metrics or {}
  
          # For logging to the console
          if log_to_console:
              dual_message_template = "%s |  %8.3f  |  %8.3f"
              no_val_message_template = "%s |  %8.3f  |  %8s"
              no_train_message_template = "%s |  %8s  |  %8.3f"
              header_template = "%s |  %-10s"
              name_length = max([len(x) for x in metric_names])
              logger.info(header_template, "Training".rjust(name_length + 13), "Validation")
  
          for name in metric_names:
              # Log to tensorboard
              train_metric = train_metrics.get(name)
              if train_metric is not None:
                  self.add_train_scalar(name, train_metric, timestep=epoch)
              val_metric = val_metrics.get(name)
              if val_metric is not None:
                  self.add_validation_scalar(name, val_metric, timestep=epoch)
  
              # And maybe log to console
              if log_to_console and val_metric is not None and train_metric is not None:
                  logger.info(dual_message_template, name.ljust(name_length), train_metric, val_metric)
              elif log_to_console and val_metric is not None:
                  logger.info(no_train_message_template, name.ljust(name_length), "N/A", val_metric)
              elif log_to_console and train_metric is not None:
                  logger.info(no_val_message_template, name.ljust(name_length), train_metric, "N/A")
docs: Sends all of the train metrics (and validation metrics, if provided) to tensorboard.