ewjoachim: The failing test in travis: it's because there's no .vault.yml. Avoid the falilure by simply defining explicit parameters in the dump-config call (and you won't need DEFAULT anymore, that's even better)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24f56b7..8eea173 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
 CHANGELOG
 =========
 
+0.4.0 (unreleased)
+------------------
+
+- Added vault dump-config
+- Added vault bootstrap-env
+
 0.3.9
 -----
 
diff --git a/README.md b/README.md
index cca31b1..ac7eefd 100644
--- a/README.md
+++ b/README.md
@@ -43,19 +43,20 @@ Options:
                                a "password" key.
   -b, --base-path TEXT         Base path for requests
   --backend TEXT               Name of the backend to use (requests, hvac)
+  -v, --verbose                Use multiple times to increase verbosity
   --config-file PATH           Config file to use. Use 'no' to disable config
                                file. Default value: first of ./.vault.yml,
                                ~/.vault.yml, /etc/vault.yml
   -h, --help                   Show this message and exit.
 
 Commands:
-  env      Launches the given command with all secrets from --path...
-  delete   Deletes a single secret.
-  get      Return a single secret value.
-  get-all  Return multiple secrets.
-  list     List all the secrets at the given path.
-  set      Set a single secret to the given value(s).
-
+  delete       Deletes a single secret.
+  dump-config  Displays all the current settings in the format of a config...
+  env          Launches the given command with all secrets from --path...
+  get          Return a single secret value.
+  get-all      Return multiple secrets.
+  list         List all the secrets at the given path.
+  set          Set a single secret to the given value(s).
 ```
 
 ## Authentication
@@ -121,6 +122,9 @@ $ vault bootstrap-env --path blob_secret -- env
 ...
 code=supercode
 ...
+
+# Recreate a configuration file based on the current settings
+$ vault --url https://something --token mytoken dump-config > .vault.yaml
 ```
 
 ## Configuration
diff --git a/tox.ini b/tox.ini
index 61e86d3..d015feb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,6 +14,7 @@ commands =
 
 [testenv:check-lint]
 extras =
+    test
     lint
     hvac
 ignore_errors=true
@@ -28,6 +29,7 @@ commands =
 extras =
     dev
     hvac
+    test
 commands =
     isort -y
     black .
diff --git a/vault_cli/cli.py b/vault_cli/cli.py
index 5d5a944..915aea8 100644
--- a/vault_cli/cli.py
+++ b/vault_cli/cli.py
@@ -16,6 +16,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 """
+import logging
 import os
 from typing import Any, Dict, Mapping, NoReturn, Sequence
 
@@ -24,6 +25,8 @@ import yaml
 
 from vault_cli import client, settings, types
 
+logger = logging.getLogger(__name__)
+
 CONTEXT_SETTINGS = {
     "help_option_names": ["-h", "--help"],
     "auto_envvar_prefix": settings.ENV_PREFIX,
@@ -44,6 +47,13 @@ def load_config(ctx: click.Context, param: click.Parameter, value: str) -> None:
     ctx.default_map = config
 
 
+def set_verbosity(ctx: click.Context, param: click.Parameter, value: int) -> int:
+    level = settings.get_log_level(verbosity=value)
+    logging.basicConfig(level=level)
+    logger.info(f"Log level set to {logging.getLevelName(level)}")
+    return value
+
+
 @click.group(context_settings=CONTEXT_SETTINGS)
 @click.pass_context
 @click.option(
@@ -88,6 +98,14 @@ def load_config(ctx: click.Context, param: click.Parameter, value: str) -> None:
     default=settings.DEFAULTS["backend"],
     help="Name of the backend to use (requests, hvac)",
 )
+@click.option(
+    "-v",
+    "--verbose",
+    is_eager=True,
+    callback=set_verbosity,
+    count=True,
+    help="Use multiple times to increase verbosity",
+)
 @click.option(
     "--config-file",
     is_eager=True,
@@ -105,14 +123,18 @@ def cli(ctx: click.Context, **kwargs) -> None:
 
     """
     kwargs.pop("config_file")
+    verbose = kwargs.pop("verbose")
     backend: str = kwargs.pop("backend")
 
     kwargs.update(extract_special_args(ctx.default_map, os.environ))
 
     # There might still be files to read, so let's do it now
     kwargs = settings.read_all_files(kwargs)
+    saved_settings = kwargs.copy()
+    saved_settings.update({"backend": backend, "verbose": verbose})
     try:
         ctx.obj = client.get_client_from_kwargs(backend=backend, **kwargs)
+        ctx.obj.saved_settings = saved_settings
     except ValueError as exc:
         raise click.UsageError(str(exc))
 
@@ -279,6 +301,21 @@ def exec_command(command: Sequence[str], environ: Dict[str, str]) -> NoReturn:
     os.execvpe(command[0], tuple(command), environ)
 
 
+@cli.command("dump-config")
+@click.pass_obj
+def dump_config(client_obj: client.VaultClientBase,) -> None:
+    """
+    Displays all the current settings in the format of a config file.
+    """
+    assert client_obj.saved_settings
+    click.echo(
+        yaml.safe_dump(
+            client_obj.saved_settings, default_flow_style=False, explicit_start=True
+        ),
+        nl=False,
+    )
+
+
 def main():
     # https://click.palletsprojects.com/en/7.x/python3/
     os.environ.setdefault("LC_ALL", "C.UTF-8")
diff --git a/vault_cli/client.py b/vault_cli/client.py
index d1f43d4..d526c91 100644
--- a/vault_cli/client.py
+++ b/vault_cli/client.py
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 """
 import json
-from typing import Iterable, Type, Union
+from typing import Iterable, Optional, Type, Union
 
 from vault_cli import settings, types
 
@@ -111,6 +111,9 @@ class VaultAPIException(Exception):
 
 
 class VaultClientBase:
+
+    saved_settings: Optional[types.SettingsDict] = None
+
     def __init__(
         self,
         url: str,
diff --git a/vault_cli/settings.py b/vault_cli/settings.py
index 8f465aa..a459a4f 100644
--- a/vault_cli/settings.py
+++ b/vault_cli/settings.py
@@ -17,6 +17,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 """
 
+import logging
 import os
 import sys
 from functools import lru_cache
@@ -26,6 +27,8 @@ import yaml
 
 from vault_cli import types
 
+logger = logging.getLogger(__name__)
+
 ENV_PREFIX = "VAULT_CLI"
 
 # Ordered by increasing priority
@@ -47,9 +50,21 @@ DEFAULTS = {
 def read_config_file(file_path: str) -> Optional[types.SettingsDict]:
     try:
         with open(os.path.expanduser(file_path), "r") as f:
-            return yaml.safe_load(f)
+            config = yaml.safe_load(f)
+            logger.info(
+                f"Reading yaml config file at {file_path}, "
+                f"contains keys: {', '.join(config)}"
+            )
+            return config
+    except FileNotFoundError:
+        logger.debug(f"No config file at {file_path} (skipping)")
     except IOError:
-        return None
+        logger.warning(
+            f"Config file exists at {file_path}, but cannot be read. "
+            "Have you checked permissions? (skipping)"
+        )
+
+    return None
 
 
 def dash_to_underscores(config: types.SettingsDict) -> types.SettingsDict:
@@ -98,36 +113,36 @@ def build_config_from_env(environ: Dict[str, str]) -> types.SettingsDict:
 
 def read_all_files(config: types.SettingsDict) -> types.SettingsDict:
     config = config.copy()
-    # Files override direct values when both are defined
-    certificate_file = config.pop("certificate_file", None)
-    if certificate_file:
-        assert isinstance(certificate_file, str)
-        config["certificate"] = read_file(certificate_file)
-
-    password_file = config.pop("password_file", None)
-    if password_file:
-        assert isinstance(password_file, str)
-        config["password"] = read_file(password_file)
 
-    token_file = config.pop("token_file", None)
-    if token_file:
-        assert isinstance(token_file, str)
-        config["token"] = read_file(token_file)
+    replace_path_with_content(config=config, setting_name="certificate")
+    replace_path_with_content(config=config, setting_name="password")
+    replace_path_with_content(config=config, setting_name="token")
 
     return config
 
 
+def replace_path_with_content(config: types.SettingsDict, setting_name: str) -> None:
+    """
+    Modifies config dict in place: reads the file at <setting_name>_file
+    and put its contents at <setting_name> instead.
+    """
+    # Files override direct values when both are defined
+    filename = config.pop(f"{setting_name}_file", None)
+    if filename:
+        assert isinstance(filename, str)
+        logger.info(f"Reading value of '{setting_name}' from file {filename}")
+        config[setting_name] = read_file(filename)
+
+
 def read_file(path: str) -> Optional[str]:
     """
     Returns the content of the pointed file
     """
-    if not path:
-        return None
-
     if path == "-":
         return sys.stdin.read().strip()
 
     with open(os.path.expanduser(path)) as file_handler:
+
         return file_handler.read().strip()
 
 
@@ -152,3 +167,13 @@ def get_vault_options(**kwargs: types.Settings):
     values.update(kwargs)
 
     return values
+
+
+def get_log_level(verbosity: int) -> int:
+    """
+    Given the number of repetitions of the flag -v,
+    returns the desired log level
+    """
+    return {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}.get(
+        min((2, verbosity)), 0
+    )

Add details on where the settings are coming from 
write in stderr the location of the settings file, if used.

(ping @tlehoux )
diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py
index 0da3aea..026d67a 100644
--- a/tests/integration/test_integration.py
+++ b/tests/integration/test_integration.py
@@ -2,6 +2,7 @@ import os
 import subprocess
 
 import pytest
+
 import vault_cli
 from vault_cli import cli
 
diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py
index adcac72..cc2c965 100644
--- a/tests/unit/test_cli.py
+++ b/tests/unit/test_cli.py
@@ -1,7 +1,9 @@
-import yaml
+import logging
 
 import pytest
-from vault_cli import cli, client
+import yaml
+
+from vault_cli import cli, client, settings
 
 
 class FakeClient(client.VaultClientBase):
@@ -277,3 +279,28 @@ def test_extract_special_args(config, environ, expected):
     result = {key: value for key, value in result.items() if value is not None}
 
     assert result == expected
+
+
+def test_set_verbosity(mocker):
+    basic_config = mocker.patch("logging.basicConfig")
+
+    cli.set_verbosity(None, None, 1)
+
+    basic_config.assert_called_with(level=logging.INFO)
+
+
+def test_dump_config(cli_runner, backend):
+    result = cli_runner.invoke(
+        cli.cli,
+        ["--base-path", "mybase/", "--token-file", "-", "dump-config"],
+        input="some-token",
+    )
+
+    expected_settings = settings.DEFAULTS.copy()
+    expected_settings.update(
+        {"base_path": "mybase/", "token": "some-token", "verbose": 0}
+    )
+
+    output = yaml.safe_load(result.output)
+
+    assert output == expected_settings
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
index 22c27ee..2901b70 100644
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -1,4 +1,5 @@
 import pytest
+
 from vault_cli import client
 
 
diff --git a/tests/unit/test_clients.py b/tests/unit/test_clients.py
index 6393aa3..d954503 100644
--- a/tests/unit/test_clients.py
+++ b/tests/unit/test_clients.py
@@ -1,4 +1,5 @@
 import pytest
+
 from vault_cli import client
 
 
diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py
index 512da21..0f6c6a5 100644
--- a/tests/unit/test_settings.py
+++ b/tests/unit/test_settings.py
@@ -1,8 +1,10 @@
 from __future__ import unicode_literals
 
 import io
+import logging
 
 import pytest
+
 from vault_cli import settings
 
 
@@ -10,6 +12,10 @@ def test_read_config_file_not_existing():
     assert settings.read_config_file("/non-existant-file") is None
 
 
+def test_read_config_file_other_error():
+    assert settings.read_config_file("/") is None
+
+
 def test_read_config_file(tmpdir):
     path = str(tmpdir.join("test.yml"))
     open(path, "w").write('{"yay": 1}')
@@ -45,10 +51,6 @@ def test_read_all_files(tmpdir):
     assert settings.read_all_files(d) == expected
 
 
-def test_read_file_no_path():
-    assert settings.read_file(None) is None
-
-
 def test_read_file_stdin(mocker):
     mocker.patch("sys.stdin", io.StringIO("yay"))
     assert settings.read_file("-") == "yay"
@@ -136,3 +138,11 @@ def test_load_bool_wrong():
 )
 def test_build_config_from_env(value, expected):
     assert settings.build_config_from_env(value) == expected
+
+
+@pytest.mark.parametrize(
+    "verbosity, log_level",
+    [(0, "WARNING"), (1, "INFO"), (2, "DEBUG"), (3, "DEBUG"), (None, "WARNING")],
+)
+def get_log_level(verbosity, log_level):
+    assert settings.get_log_level(verbosity=verbosity) == getattr(logging, log_level)

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio pytest
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
black==23.3.0
bleach==6.0.0
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
charset-normalizer==3.4.1
click==8.1.8
coverage==7.2.7
cryptography==44.0.2
docutils==0.20.1
exceptiongroup==1.2.2
execnet==2.0.2
idna==3.10
importlib-metadata==6.7.0
importlib-resources==5.12.0
iniconfig==2.0.0
isort==5.11.5
jaraco.classes==3.2.3
jeepney==0.9.0
keyring==24.1.1
markdown-it-py==2.2.0
mdurl==0.1.2
more-itertools==9.1.0
mypy-extensions==1.0.0
packaging==24.0
pathspec==0.11.2
pkginfo==1.10.0
platformdirs==4.0.0
pluggy==1.2.0
pycparser==2.21
Pygments==2.17.2
pytest==7.4.4
pytest-asyncio==0.21.2
pytest-cov==4.1.0
pytest-mock==3.11.1
pytest-xdist==3.5.0
PyYAML==6.0.1
readme-renderer==37.3
requests==2.31.0
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==13.8.1
SecretStorage==3.3.3
six==1.17.0
tomli==2.0.1
twine==4.0.2
typed-ast==1.5.5
typing_extensions==4.7.1
urllib3==2.0.7
-e git+https://github.com/peopledoc/vault-cli.git@a7d15a7e0dfd713710ca6d472a37dd49a7e8c575#egg=vault_cli
webencodings==0.5.1
zipp==3.15.0

name: vault-cli
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - black==23.3.0
      - bleach==6.0.0
      - cffi==1.15.1
      - charset-normalizer==3.4.1
      - click==8.1.8
      - coverage==7.2.7
      - cryptography==44.0.2
      - docutils==0.20.1
      - exceptiongroup==1.2.2
      - execnet==2.0.2
      - idna==3.10
      - importlib-metadata==6.7.0
      - importlib-resources==5.12.0
      - iniconfig==2.0.0
      - isort==5.11.5
      - jaraco-classes==3.2.3
      - jeepney==0.9.0
      - keyring==24.1.1
      - markdown-it-py==2.2.0
      - mdurl==0.1.2
      - more-itertools==9.1.0
      - mypy-extensions==1.0.0
      - packaging==24.0
      - pathspec==0.11.2
      - pkginfo==1.10.0
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - pycparser==2.21
      - pygments==2.17.2
      - pytest==7.4.4
      - pytest-asyncio==0.21.2
      - pytest-cov==4.1.0
      - pytest-mock==3.11.1
      - pytest-xdist==3.5.0
      - pyyaml==6.0.1
      - readme-renderer==37.3
      - requests==2.31.0
      - requests-toolbelt==1.0.0
      - rfc3986==2.0.0
      - rich==13.8.1
      - secretstorage==3.3.3
      - six==1.17.0
      - tomli==2.0.1
      - twine==4.0.2
      - typed-ast==1.5.5
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - webencodings==0.5.1
      - zipp==3.15.0
prefix: /opt/conda/envs/vault-cli

tests/unit/test_cli.py::test_set_verbosity
tests/integration/test_integration.py::test_integration_lib tests/integration/test_integration.py::test_env_var_config tests/unit/test_client.py::test_get_client_from_kwargs[hvac-vault_cli.hvac.HVACVaultClient]
tests/unit/test_cli.py::test_read_yaml tests/unit/test_cli.py::test_write_yaml tests/unit/test_cli.py::test_exec_command tests/unit/test_cli.py::test_main tests/unit/test_cli.py::test_load_config_no_config tests/unit/test_cli.py::test_load_config[bla-expected0] tests/unit/test_cli.py::test_load_config[None-expected1] tests/unit/test_cli.py::test_extract_special_args[config0-environ0-expected0] tests/unit/test_cli.py::test_extract_special_args[config1-environ1-expected1] tests/unit/test_cli.py::test_extract_special_args[config2-environ2-expected2] tests/unit/test_cli.py::test_extract_special_args[config3-environ3-expected3] tests/unit/test_cli.py::test_extract_special_args[config4-environ4-expected4] tests/unit/test_cli.py::test_extract_special_args[config5-environ5-expected5] tests/unit/test_cli.py::test_extract_special_args[config6-environ6-expected6] tests/unit/test_cli.py::test_extract_special_args[config7-environ7-expected7] tests/unit/test_cli.py::test_extract_special_args[config8-environ8-expected8] tests/unit/test_cli.py::test_extract_special_args[config9-environ9-expected9] tests/unit/test_cli.py::test_extract_special_args[config10-environ10-expected10] tests/unit/test_cli.py::test_extract_special_args[config11-environ11-expected11] tests/unit/test_client.py::test_get_client_from_kwargs[requests-vault_cli.requests.RequestsVaultClient] tests/unit/test_client.py::test_get_client_from_kwargs_custom tests/unit/test_client.py::test_get_client_from_kwargs_bad tests/unit/test_client.py::test_get_client tests/unit/test_client.py::test_vault_api_exception[oh tests/unit/test_client.py::test_vault_api_exception[{"errors": tests/unit/test_client.py::test_vault_client_base_not_implemented[_init_session-url tests/unit/test_client.py::test_vault_client_base_not_implemented[_authenticate_token-token] tests/unit/test_client.py::test_vault_client_base_not_implemented[_authenticate_certificate-certificate] tests/unit/test_client.py::test_vault_client_base_not_implemented[_authenticate_userpass-username tests/unit/test_client.py::test_vault_client_base_not_implemented[list_secrets-path] tests/unit/test_client.py::test_vault_client_base_not_implemented[get_secret-path] tests/unit/test_client.py::test_vault_client_base_not_implemented[delete_secret-path] tests/unit/test_client.py::test_vault_client_base_not_implemented[set_secret-path tests/unit/test_client.py::test_nested_keys[test-foo-expected0] tests/unit/test_client.py::test_nested_keys[test/bla-foo-expected1] tests/unit/test_client.py::test_vault_client_base_call_init_session tests/unit/test_client.py::test_vault_client_base_authenticate[test_kwargs0-expected0] tests/unit/test_client.py::test_vault_client_base_authenticate[test_kwargs1-expected1] tests/unit/test_client.py::test_vault_client_base_authenticate[test_kwargs2-expected2] tests/unit/test_client.py::test_vault_client_base_username_without_password tests/unit/test_client.py::test_vault_client_base_no_auth tests/unit/test_client.py::test_vault_client_set_ca_bundle tests/unit/test_client.py::test_vault_client_set_ca_bundle_no_bundle tests/unit/test_client.py::test_vault_client_set_ca_bundle_no_verify tests/unit/test_client.py::test_vault_client_base_get_recursive_secrets tests/unit/test_client.py::test_vault_client_base_get_recursive_secrets_single_secret tests/unit/test_client.py::test_vault_client_base__merge_secrets tests/unit/test_client.py::test_vault_client_base_get_all tests/unit/test_settings.py::test_read_config_file_not_existing tests/unit/test_settings.py::test_read_config_file_other_error tests/unit/test_settings.py::test_read_config_file tests/unit/test_settings.py::test_dash_to_underscores tests/unit/test_settings.py::test_read_all_files_no_file tests/unit/test_settings.py::test_read_all_files tests/unit/test_settings.py::test_read_file_stdin tests/unit/test_settings.py::test_build_config_from_files tests/unit/test_settings.py::test_build_config_from_files_no_files tests/unit/test_settings.py::test_get_vault_options tests/unit/test_settings.py::test_load_bool[true-True] tests/unit/test_settings.py::test_load_bool[True-True0] tests/unit/test_settings.py::test_load_bool[True-True1] tests/unit/test_settings.py::test_load_bool[t-True] tests/unit/test_settings.py::test_load_bool[T-True] tests/unit/test_settings.py::test_load_bool[1-True] tests/unit/test_settings.py::test_load_bool[yes-True] tests/unit/test_settings.py::test_load_bool[YES-True] tests/unit/test_settings.py::test_load_bool[y-True] tests/unit/test_settings.py::test_load_bool[false-False] tests/unit/test_settings.py::test_load_bool[False-False] tests/unit/test_settings.py::test_load_bool[FALSE-False] tests/unit/test_settings.py::test_load_bool[f-False] tests/unit/test_settings.py::test_load_bool[F-False] tests/unit/test_settings.py::test_load_bool[0-False] tests/unit/test_settings.py::test_load_bool[no-False] tests/unit/test_settings.py::test_load_bool[NO-False] tests/unit/test_settings.py::test_load_bool[n-False] tests/unit/test_settings.py::test_load_bool[N-False] tests/unit/test_settings.py::test_load_bool_wrong tests/unit/test_settings.py::test_build_config_from_env[value0-expected0] tests/unit/test_settings.py::test_build_config_from_env[value1-expected1] tests/unit/test_settings.py::test_build_config_from_env[value2-expected2] tests/unit/test_settings.py::test_build_config_from_env[value3-expected3] tests/unit/test_settings.py::test_build_config_from_env[value4-expected4] tests/unit/test_settings.py::test_build_config_from_env[value5-expected5]
vault_cli/client.py vault_cli/cli.py CHANGELOG.md vault_cli/settings.py tox.ini README.md
vault_cli/client.py vault_cli/cli.py CHANGELOG.md vault_cli/settings.py tox.ini README.md