docstr: Log metrics of learner and actors
          
docstr_tok: ["Log","metrics","of","learner","and","actors"]
code: def log_metrics(self):
      """Log metrics of learner and actors"""
      if self.start_time is None:
          return
  
      metrics = []
      # get the total metrics data
      future_object_ids = [
          remote_actor.get_metrics()
          for remote_actor in self.remote_actors
      ]
      metrics = [
          future_object.get() for future_object in future_object_ids
      ]
  
      # if the metric of all the metrics are empty, return nothing.
      total_length = sum(len(metric) for metric in metrics)
      if not total_length:
          return
  
      episode_rewards, episode_steps = [], []
      for x in metrics:
          episode_rewards.extend(x["episode_rewards"])
          episode_steps.extend(x["episode_steps"])
      (
          max_episode_rewards,
          mean_episode_rewards,
          min_episode_rewards,
          max_episode_steps,
          mean_episode_steps,
          min_episode_steps,
      ) = (None, None, None, None, None, None)
      if episode_rewards:
          mean_episode_rewards = np.mean(
              np.array(episode_rewards).flatten()
          )
          max_episode_rewards = np.max(
              np.array(episode_rewards).flatten()
          )
          min_episode_rewards = np.min(
              np.array(episode_rewards).flatten()
          )
  
          mean_episode_steps = np.mean(
              np.array(episode_steps).flatten()
          )
          max_episode_steps = np.max(np.array(episode_steps).flatten())
          min_episode_steps = np.min(np.array(episode_steps).flatten())
  
      metric = {
          "Sample steps": self.sample_total_steps,
          "max_episode_rewards": max_episode_rewards,
          "mean_episode_rewards": mean_episode_rewards,
          "min_episode_rewards": min_episode_rewards,
          "max_episode_steps": max_episode_steps,
          "mean_episode_steps": mean_episode_steps,
          "min_episode_steps": min_episode_steps,
          "total_loss": self.total_loss_stat.mean,
          "pi_loss": self.pi_loss_stat.mean,
          "vf_loss": self.vf_loss_stat.mean,
          "entropy": self.entropy_stat.mean,
          "learn_time_s": self.learn_time_stat.mean,
          "elapsed_time_s": int(time.time() - self.start_time),
          "lr": self.lr,
          "entropy_coeff": self.entropy_coeff,
      }
  
      for key, value in metric.items():
          if value is not None:
              summary.add_scalar(key, value, self.sample_total_steps)
  
      logger.info(metric)
  
imports: import torch
  import gym
  import parl
  import time
  import numpy
  from collections import defaultdict
  from parl.env.atari_wrappers import wrap_deepmind
  from parl.utils.window_stat import WindowStat
  from parl.utils.time_stat import TimeStat
  from parl.utils import logger, summary
  from parl.algorithms import A2C
  from atari_model import ActorCritic
  from atari_agent import Agent
  from actor import Actor
label_desc: Use image node_id 4 for calling the Learner obj's underlying member method code with example usage: obj.log_metrics() without return types