text: import time
  import gym
  
  
  #  This class is modified from the SB3 to monitor the env statistics
  # https://github.com/DLR-RM/stable-baselines3/blob/master/stable_baselines3/common/monitor.py
  class Monitor(gym.Wrapper):
      """
      A monitor wrapper for Gym environments, it is used to know the episode reward, length, time and other data.
  
      :param env: The environment
      :param allow_early_resets: allows the reset of the environment before it is done
      :param reset_keywords: extra keywords for the reset call,
          if extra parameters are needed at reset
      :param info_keywords: extra information to log, from the information return of env.step()
      """
  
      def __init__(
              self,
              env: gym.Env,
              allow_early_resets: bool = True,
              reset_keywords=(),
              info_keywords=(),
      ):
          super(Monitor, self).__init__(env=env)
          self.t_start = time.time()
          self.reset_keywords = reset_keywords
          self.info_keywords = info_keywords
          self.allow_early_resets = allow_early_resets
          self.rewards = None
          self.needs_reset = True
          self.episode_returns = []
          self.episode_lengths = []
          self.episode_times = []
          self.total_steps = 0
          self.current_reset_info = {}  # extra info about the current episode, that was passed in during reset()
  
      def reset(self, **kwargs):
          """
          Calls the Gym environment reset. Can only be called if the environment is over, or if allow_early_resets is True
  
          :param kwargs: Extra keywords saved for the next episode. only if defined by reset_keywords
          :return: the first observation of the environment
          """
          if not self.allow_early_resets and not self.needs_reset:
              raise RuntimeError(
                  "Tried to reset an environment before done. If you want to allow early resets, "
                  "wrap your env with Monitor(env, path, allow_early_resets=True)"
              )
          self.rewards = []
          self.needs_reset = False
          for key in self.reset_keywords:
              value = kwargs.get(key)
              if value is None:
                  raise ValueError(f"Expected you to pass keyword argument {key} into reset")
              self.current_reset_info[key] = value
          return self.env.reset(**kwargs)
  
      def step(self, action):
          """
          Step the environment with the given action
  
          :param action: the action
          :return: observation, reward, done, information
          """
          if self.needs_reset:
              raise RuntimeError("Tried to step environment that needs reset")
          observation, reward, done, info = self.env.step(action)
          self.rewards.append(reward)
          if done:
              self.needs_reset = True
              ep_rew = sum(self.rewards)
              ep_len = len(self.rewards)
              info["ep_rew"] = ep_rew
              info["ep_len"] = ep_len
              info["ep_t"] = round(time.time() - self.t_start, 6)
              info["real_done"] = True
              self.episode_returns.append(ep_rew)
              self.episode_lengths.append(ep_len)
              self.episode_times.append(time.time() - self.t_start)
  
          self.total_steps += 1
          return observation, reward, done, info
  
      def close(self) -> None:
          """
          Closes the environment
          """
          super(Monitor, self).close()
  
      def get_total_steps(self) -> int:
          """
          Returns the total number of timesteps
  
          :return:
          """
          return self.total_steps
  
      def get_episode_rewards(self):
          """
          Returns the rewards of all the episodes
  
          :return:
          """
          return self.episode_returns
  
      def get_episode_lengths(self):
          """
          Returns the number of timesteps of all the episodes
  
          :return:
          """
          return self.episode_lengths
  
      def get_episode_times(self):
          """
          Returns the runtime in seconds of all the episodes
  
          :return:
          """
          return self.episode_times
  