docs: def output_parameters(general_options, spec):
      # type: (blobxfer.models.options.General, object) -> None
      """Output parameters
      :param blobxfer.models.options.General general_options: general options
      :param object spec: upload or download spec
      """
      if general_options.quiet:
          return
      sep = '============================================'
      log = []
      log.append(sep)
      log.append('         Azure blobxfer parameters')
      log.append(sep)
      log.append('         blobxfer version: {}'.format(
          blobxfer.version.__version__))
      log.append('                 platform: {}'.format(platform.platform()))
      log.append(
          ('               components: {}={}-{} azstor.blob={} azstor.file={} '
           'crypt={} req={}').format(
              platform.python_implementation(),
              platform.python_version(),
               '64bit' if sys.maxsize > 2**32 else '32bit',
              azure.storage.blob._constants.__version__,
              azure.storage.file._constants.__version__,
              cryptography.__version__,
              requests.__version__,))
      # specific preamble
      if isinstance(spec, blobxfer.models.download.Specification):
          log.append('       transfer direction: {}'.format('Azure -> local'))
          log.append(
              ('                  workers: disk={} xfer={} (msoc={}) md5={} '
               'crypto={}').format(
                   general_options.concurrency.disk_threads,
                   general_options.concurrency.transfer_threads,
                   spec.options.max_single_object_concurrency,
                   general_options.concurrency.md5_processes
                   if spec.options.check_file_md5 else 0,
                   general_options.concurrency.crypto_processes))
      elif isinstance(spec, blobxfer.models.upload.Specification):
          log.append('       transfer direction: {}'.format('local -> Azure'))
          log.append(
              ('                  workers: disk={} xfer={} md5={} '
               'crypto={}').format(
                   general_options.concurrency.disk_threads,
                   general_options.concurrency.transfer_threads,
                   general_options.concurrency.md5_processes
                   if spec.skip_on.md5_match or
                   spec.options.store_file_properties.md5 else 0,
                   0))
      elif isinstance(spec, blobxfer.models.synccopy.Specification):
          log.append('       transfer direction: {}'.format('Azure -> Azure'))
          log.append(
              ('                  workers: disk={} xfer={} md5={} '
               'crypto={}').format(
                   general_options.concurrency.disk_threads,
                   general_options.concurrency.transfer_threads,
                   general_options.concurrency.md5_processes,
                   general_options.concurrency.crypto_processes))
      # common block
      log.append('                 log file: {}'.format(
          general_options.log_file))
      log.append('                  dry run: {}'.format(
          general_options.dry_run))
      log.append('              resume file: {}'.format(
          general_options.resume_file))
      log.append(
          '                  timeout: connect={} read={} max_retries={}'.format(
              general_options.timeout.connect, general_options.timeout.read,
              general_options.timeout.max_retries))
      if isinstance(spec, blobxfer.models.synccopy.Specification):
          log.append('              source mode: {}'.format(
              spec.options.mode))
          log.append('                dest mode: {}'.format(
              spec.options.dest_mode))
      else:
          log.append('                     mode: {}'.format(
              spec.options.mode))
      log.append(
          '                  skip on: fs_match={} lmt_ge={} md5={}'.format(
              spec.skip_on.filesize_match,
              spec.skip_on.lmt_ge,
              spec.skip_on.md5_match))
      log.append('        delete extraneous: {}'.format(
          spec.options.delete_extraneous_destination))
      log.append('                overwrite: {}'.format(
          spec.options.overwrite))
      log.append('                recursive: {}'.format(
          spec.options.recursive))
      log.append('            rename single: {}'.format(
          spec.options.rename))
      # specific epilog
      if isinstance(spec, blobxfer.models.download.Specification):
          log.append('         chunk size bytes: {}'.format(
              spec.options.chunk_size_bytes))
          log.append('         strip components: {}'.format(
              spec.options.strip_components))
          log.append('         compute file md5: {}'.format(
              spec.options.check_file_md5))
          log.append('       restore properties: attr={} lmt={}'.format(
              spec.options.restore_file_properties.attributes,
              spec.options.restore_file_properties.lmt))
          log.append('          rsa private key: {}'.format(
              'Loaded' if spec.options.rsa_private_key else 'None'))
          log.append('        local destination: {}'.format(
              spec.destination.path))
      elif isinstance(spec, blobxfer.models.upload.Specification):
          log.append('              access tier: {}'.format(
              spec.options.access_tier))
          log.append('         chunk size bytes: {}'.format(
              spec.options.chunk_size_bytes))
          log.append('           one shot bytes: {}'.format(
              spec.options.one_shot_bytes))
          log.append('         strip components: {}'.format(
              spec.options.strip_components))
          log.append(
              '         store properties: attr={} cc=\'{}\' md5={}'.format(
                  spec.options.store_file_properties.attributes,
                  spec.options.store_file_properties.cache_control or '',
                  spec.options.store_file_properties.md5))
          log.append('           rsa public key: {}'.format(
              'Loaded' if spec.options.rsa_public_key else 'None'))
          log.append('       local source paths: {}'.format(
              ' '.join([str(src) for src in spec.sources.paths])))
      elif isinstance(spec, blobxfer.models.synccopy.Specification):
          log.append('              access tier: {}'.format(
              spec.options.access_tier))
      log.append(sep)
      log = '\n'.join(log)
      if blobxfer.util.is_not_empty(general_options.log_file):
          print(log)
      logger.info('\n{}'.format(log))