Repository: dbt-labs/dbt-core

Issue:
[CT-1917] [Regression] Non-error log is not silenced by `-q` flag.
### Is this a regression in a recent version of dbt-core?

- [X] I believe this is a regression in dbt-core functionality
- [X] I have searched the existing issues, and I could not find an existing issue for this regression

### Current Behavior

According to `-q`:
> Suppress all non-error logging to stdout. Does not affect {{ print() }} macro calls.

A new log is printed even though it's not an error.

```bash
(elementary) ☁  dbt_project [main] dbt run-operation hello
14:22:47  target not specified in profile 'elementary', using 'default'
14:22:47  Running with dbt=1.4.0
hello
(elementary) ☁  dbt_project [main] dbt -q run-operation hello
14:22:56  target not specified in profile 'elementary', using 'default'
hello
```


### Expected/Previous Behavior

Previously, the log was omitted when passing the `-q` flag.

```bash
(elementary) ☁  dbt_project [main] dbt run-operation hello
14:22:15  target not specified in profile 'elementary', using 'default'
14:22:16  Running with dbt=1.3.0
hello
(elementary) ☁  dbt_project [main] dbt -q run-operation hello
hello
```

### Steps To Reproduce

1. Name a `default` target without having a `target` defined.
2. Run a dbt command with the `-q` flag.

### Relevant log output

_No response_

### Environment

```markdown
- OS: macOS
- Python: 3.9.6
- dbt (working version): 1.3.0
- dbt (regression version): 1.4.0
```


### Which database adapter are you using with dbt?

_No response_

### Additional Context

I'd love to contribute.


Discussion:
My suspicion here is:
- We set up a basic event logger before we have the full set of information we need to initialize it for real (all flags & configs resolved, including `--quiet` mode). This ensures that any real initialization errors don't get swallowed. Relevant: #6434, #6447.
- In order to resolve global configs, we have to look at `UserConfig`, which today is defined in `profiles.yml`, so we have to render `profiles.yml`. (Should it be? Almost certainly not: #6207)
- The event you're seeing, `MissingProfileTarget`, is raised during `render_profile`, which is happening during initialization and before we call `setup_event_logger` here:
https://github.com/dbt-labs/dbt-core/blob/c65ba11ae6dca5203228320a7f6388ce1796f104/core/dbt/main.py#L225-L232

Confirmed:
```python
ipdb> task = parsed.cls.from_args(args=parsed)
15:01:43  target not specified in profile 'my-profile', using 'default'
```

Options:
1. We document that `--quiet` doesn't work for warnings/errors during initialization
2. We turn the initial stdout logger (before we've resolved configs) into `ERROR`-level only:
https://github.com/dbt-labs/dbt-core/blob/c65ba11ae6dca5203228320a7f6388ce1796f104/core/dbt/events/functions.py#L112-L118
Hi @jtcohen6 , thanks a lot for replying.
I personally tend to favor (2) because I think that if a user is using `--quiet` they'd generally like to keep a program quiet at its entirety except for logs made by them (`print()`) and it doesn't really matter at what phase the execution is.
What do you think?

If that resonates, care to share any guidelines on how can I make this contribution?
I think it would be as simple as modifying line 117 to pass a level override into `_get_stdout_config`:
```python
 EVENT_MANAGER.add_logger( 
     _get_logbook_log_config() if flags.ENABLE_LEGACY_LOGGER else _get_stdout_config(EventLevel.ERROR) 
 ) 
```

@peterallenwebb How does this approach sound to you? The downside is, I think we'd _never_ actually log `MissingProfileTarget` (or any other initialization-time logging below that isn't error-level) — whether or not the user is actually passing the `--quiet` flag, because we haven't resolved flags before this default logger is set up.
The approach would work, and it would indeed have the consequences you described.

We could, however, add some log configuration code right after [command line arguments are parsed](https://github.com/dbt-labs/dbt-core/blob/main/core/dbt/main.py#L176) so that we can turn on quiet mode as soon as possible, if it is specified. I think I actually like that approach better?
@peterallenwebb Let's give it a shot! While it might not work perfectly for "quiet mode," if set via env var or user config, I generally expect users to pass it in as a CLI flag, as @elongl is showing above.

Resolving patch:
diff --git a/core/dbt/events/functions.py b/core/dbt/events/functions.py
--- a/core/dbt/events/functions.py
+++ b/core/dbt/events/functions.py
@@ -18,6 +18,14 @@
 metadata_vars: Optional[Dict[str, str]] = None
 
 
+# The "fallback" logger is used as a stop-gap so that console logging works before the logging
+# configuration is fully loaded.
+def setup_fallback_logger(use_legacy: bool, level: EventLevel) -> None:
+    cleanup_event_logger()
+    config = _get_logbook_log_config(level) if use_legacy else _get_stdout_config(level)
+    EVENT_MANAGER.add_logger(config)
+
+
 def setup_event_logger(log_path: str, level_override: Optional[EventLevel] = None):
     cleanup_event_logger()
     make_log_dir_if_missing(log_path)
@@ -113,9 +121,7 @@ def cleanup_event_logger():
 # currently fire before logs can be configured by setup_event_logger(), we
 # create a default configuration with default settings and no file output.
 EVENT_MANAGER: EventManager = EventManager()
-EVENT_MANAGER.add_logger(
-    _get_logbook_log_config() if flags.ENABLE_LEGACY_LOGGER else _get_stdout_config()
-)
+setup_fallback_logger(bool(flags.ENABLE_LEGACY_LOGGER), EventLevel.INFO)
 
 
 # This global, and the following two functions for capturing stdout logs are
diff --git a/core/dbt/main.py b/core/dbt/main.py
--- a/core/dbt/main.py
+++ b/core/dbt/main.py
@@ -11,8 +11,9 @@
 from pathlib import Path
 
 import dbt.version
-from dbt.events.functions import fire_event, setup_event_logger, LOG_VERSION
+from dbt.events.functions import fire_event, setup_event_logger, setup_fallback_logger, LOG_VERSION
 from dbt.events.types import (
+    EventLevel,
     MainEncounteredError,
     MainKeyboardInterrupt,
     MainReportVersion,
@@ -178,6 +179,13 @@ def handle_and_check(args):
         # Set flags from args, user config, and env vars
         user_config = read_user_config(flags.PROFILES_DIR)  # This is read again later
         flags.set_from_args(parsed, user_config)
+
+        # If the user has asked to supress non-error logging on the cli, we want to respect that as soon as possible,
+        # so that any non-error logging done before full log config is loaded and ready is filtered accordingly.
+        setup_fallback_logger(
+            bool(flags.ENABLE_LEGACY_LOGGER), EventLevel.ERROR if flags.QUIET else EventLevel.INFO
+        )
+
         dbt.tracking.initialize_from_flags()
         # Set log_format from flags
         parsed.cls.set_log_format()


Test patch: