code: def test_logs_events():
      class CustomDummyKPICalculator(KpiCalculator):
          """
          Dummy KPIs for dummy environment.
          """
  
          def calculate_kpis(
              self,
              episode_event_log: EpisodeEventLog,
              last_maze_state: MazeStateType,
          ) -> Dict[str, float]:
              """
              Returns a dummy KPI.
              """
              return {"dummy_kpi": random.random()}
  
      class CustomDummyRewardAggregator(RewardAggregator):
          """
          Customized dummy reward aggregator subscribed to BaseEnvEvents.
          """
  
          def get_interfaces(self) -> List[Type[ABC]]:
              """
              Return events class is subscribed to.
              """
              additional_interfaces: List[Type[ABC]] = [BaseEnvEvents]
              parent_interfaces = super().get_interfaces()
              return additional_interfaces + parent_interfaces
  
      class CustomDummyCoreEnv(DummyCoreEnvironment):
          """
          Customized dummy core env with serializable components that regenerates state only in step.
          """
  
          def __init__(self, observation_space):
              super().__init__(observation_space)
              self.reward_aggregator = CustomDummyRewardAggregator()
              self.pubsub: Pubsub = Pubsub(self.context.event_service)
              self.pubsub.register_subscriber(self.reward_aggregator)
              self.base_event_publisher = (
                  self.pubsub.create_event_topic(BaseEnvEvents)
              )
              self.kpi_calculator = CustomDummyKPICalculator()
  
          def get_kpi_calculator(self) -> CustomDummyKPICalculator:
              """KPIs are supported."""
              return self.kpi_calculator
  
      class TestWriter(LogEventsWriter):
          """Mock writer checking the logged events"""
  
          def __init__(self):
              self.episode_count = 0
              self.step_count = 0
  
          def write(self, episode_record: EpisodeEventLog):
              """Count steps and episodes & check that some events were logged in each step"""
              self.episode_count += 1
              self.step_count += len(episode_record.step_event_logs)
  
              for step_id, step_event_log in enumerate(
                  episode_record.step_event_logs
              ):
                  assert step_id == step_event_log.env_time
                  assert len(step_event_log.events) > 0
  
              last_step_event_log = episode_record.step_event_logs[-1]
              kpi_count_in_last_step = len(
                  list(
                      last_step_event_log.events.query_events(
                          BaseEnvEvents.kpi
                      )
                  )
              )
              assert kpi_count_in_last_step != 0
              # KPIs should be a part of the last step record only
              assert kpi_count_in_last_step == len(
                  list(episode_record.query_events(BaseEnvEvents.kpi))
              )
  
      observation_conversion = ObservationConversion()
      writer = TestWriter()
      _run_rollout_loop(
          env=DummyEnvironment(
              core_env=CustomDummyCoreEnv(
                  observation_conversion.space()
              ),
              action_conversion=[DictActionConversion()],
              observation_conversion=[observation_conversion],
          ),
          n_episodes=5,
          n_steps_per_episode=10,
          writer=writer,
      )
  
      assert writer.episode_count == 5
      assert writer.step_count == 5 * 10
  
imports: import random
  from abc import ABC
  from typing import Tuple, Optional
  from typing import Union, List, Type, Dict
  import gym
  import numpy
  from maze.core.env.base_env import BaseEnv
  from maze.core.env.base_env_events import BaseEnvEvents
  from maze.core.env.maze_state import MazeStateType
  from maze.core.env.time_env_mixin import TimeEnvMixin
  from maze.core.events.pubsub import Pubsub
  from maze.core.log_events.episode_event_log import EpisodeEventLog
  from maze.core.log_events.kpi_calculator import KpiCalculator
  from maze.core.log_events.log_events_writer import LogEventsWriter
  from maze.core.log_events.log_events_writer_registry import LogEventsWriterRegistry
  from maze.core.log_stats.event_decorators import define_episode_stats, define_step_stats, define_epoch_stats
  from maze.core.wrappers.log_stats_wrapper import LogStatsWrapper
  from maze.core.wrappers.maze_gym_env_wrapper import GymMazeEnv
  from maze.test.shared_test_utils.dummy_env.dummy_core_env import DummyCoreEnvironment
  from maze.test.shared_test_utils.dummy_env.dummy_maze_env import DummyEnvironment
  from maze.test.shared_test_utils.dummy_env.dummy_struct_env import DummyStructuredEnvironment
  from maze.test.shared_test_utils.dummy_env.reward.base import RewardAggregator
  from maze.test.shared_test_utils.dummy_env.space_interfaces.action_conversion.dict import DictActionConversion
  from maze.test.shared_test_utils.dummy_env.space_interfaces.action_conversion.dict import DictActionConversion
  from maze.test.shared_test_utils.dummy_env.space_interfaces.observation_conversion.dict import ObservationConversion
  from maze.test.shared_test_utils.dummy_env.space_interfaces.observation_conversion.dict import ObservationConversion
label_desc: Use image node_id 2 for calling a global function with example usage: test_logs_events() and returns: dict, unknown, self