class_docstr: PipelineFeatureGenerator is an implementation of BulkFeatureGenerator with various smart defaults and edge case handling functionality to enable
  robust data handling.
  It is recommended that users base any custom feature generators meant for end-to-end data transformation from PipelineFeatureGenerator.
      Reference AutoMLPipelineFeatureGenerator for an example of extending PipelineFeatureGenerator.
  It is not recommended that PipelineFeatureGenerator be used as a generator within any other generator's pre or post generators.
code: def print_feature_metadata_info(self, log_level=20):
      if self._useless_features_in:
          self._log(
              log_level,
              f"\tUseless Original Features (Count: {len(self._useless_features_in)}): {list(self._useless_features_in)}",
          )
          # TODO: What about features with 1 unique value but also np.nan?
          self._log(
              log_level,
              "\t\tThese features carry no predictive signal and should be manually investigated.",
          )
          self._log(
              log_level,
              "\t\tThis is typically a feature which has the same value for all rows.",
          )
          self._log(
              log_level,
              "\t\tThese features do not need to be present at inference time.",
          )
      if self._feature_metadata_in_unused.get_features():
          # TODO: Consider highlighting why a feature was unused
          #  (complex to implement, can check if was valid input to any generator in a generator group through feature chaining)
          self._log(
              log_level,
              f"\tUnused Original Features (Count: {len(self._feature_metadata_in_unused.get_features())}): "
              f"{self._feature_metadata_in_unused.get_features()}",
          )
          self._log(
              log_level,
              "\t\tThese features were not used to generate any of the output features. "
              "Add a feature generator compatible with these features to utilize them.",
          )
          self._log(
              log_level,
              "\t\tFeatures can also be unused if they carry very little information, "
              "such as being categorical but having almost entirely unique values or being duplicates of other features.",
          )
          self._log(
              log_level,
              "\t\tThese features do not need to be present at inference time.",
          )
          self._feature_metadata_in_unused.print_feature_metadata_full(
              self.log_prefix + "\t\t", log_level=log_level
          )
      self._log(
          log_level - 5,
          "\tTypes of features in original data (exact raw dtype, raw dtype):",
      )
      self._feature_metadata_in_real.print_feature_metadata_full(
          self.log_prefix + "\t\t",
          print_only_one_special=True,
          log_level=log_level - 5,
      )
      super().print_feature_metadata_info(log_level=log_level)
  
imports: import copy
  import logging
  from pandas import DataFrame
  from autogluon.common.features.feature_metadata import FeatureMetadata
  from autogluon.common.features.infer_types import get_type_map_real
  from autogluon.common.utils.pandas_utils import get_approximate_df_mem_usage
  from autogluon.common.utils.resource_utils import ResourceManager
  from .bulk import BulkFeatureGenerator
  from .drop_unique import DropUniqueFeatureGenerator
  from .dummy import DummyFeatureGenerator
  from .fillna import FillNaFeatureGenerator
label_desc: Use image node_id 10 for calling the PipelineFeatureGenerator obj's underlying member method code with example usage: obj.print_feature_metadata_info(log_level) without return types