We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[CT-2139] Don't exclude `Formatting` events from JSON-formatted logs
We have some events that are only for formatting / aesthetic display in the terminal. (Previously, this was called `EmptyLine`; in v1.5 we're renaming it to `Formatting`.) Historically, we've intentionally _excluded_ these events from JSON-formatted structured logs. This makes sense in one way, but it's actively unhelpful for someone who wants _both_ the full structure of JSON logs _and_ the ability to produce their own text-formatted logs at parity with `dbt-core`'s.

_copying from slack_
> @jtcohen6
> While it's great to be opinionated about what & how we display `text`, it does feel weird to me that we're depriving the json log consumer of some detail/flexibility.
> @peterallenwebb:
> On balance, I think we should include the EmptyLine/Formatting events. I infer that the original logic behind excluding them was that they would be confusing/unwanted by consumers, but the existence of this use case suggests otherwise.

### Proposed change

Remove the line from `_stdout_filter` that filters out `Formatting` if we're emitting JSON-formatted logs:
https://github.com/dbt-labs/dbt-core/blob/c952d44ec5c2506995fbad75320acbae49125d3d/core/dbt/events/functions.py#L89

[`TextOnly`](https://github.com/dbt-labs/dbt-core/search?q=TextOnly) achieves the same purpose, for legacy logging _only_. We could remove it now, or remove it when we deprecate legacy logging in ~v1.6~ v1.7.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `core/dbt/events/functions.py`
Content:
```
1 from dbt.constants import METADATA_ENV_PREFIX
2 from dbt.events.base_types import BaseEvent, EventLevel, EventMsg
3 from dbt.events.eventmgr import EventManager, LoggerConfig, LineFormat, NoFilter, IEventManager
4 from dbt.events.helpers import env_secrets, scrub_secrets
5 from dbt.events.types import Formatting, Note
6 from dbt.flags import get_flags, ENABLE_LEGACY_LOGGER
7 from dbt.logger import GLOBAL_LOGGER, make_log_dir_if_missing
8 from functools import partial
9 import json
10 import os
11 import sys
12 from typing import Callable, Dict, List, Optional, TextIO
13 import uuid
14 from google.protobuf.json_format import MessageToDict
15 
16 import dbt.utils
17 
18 LOG_VERSION = 3
19 metadata_vars: Optional[Dict[str, str]] = None
20 
21 # These are the logging events issued by the "clean" command,
22 # where we can't count on having a log directory. We've removed
23 # the "class" flags on the events in types.py. If necessary we
24 # could still use class or method flags, but we'd have to get
25 # the type class from the msg and then get the information from the class.
26 nofile_codes = ["Z012", "Z013", "Z014", "Z015"]
27 
28 
29 def setup_event_logger(flags, callbacks: List[Callable[[EventMsg], None]] = []) -> None:
30     cleanup_event_logger()
31     make_log_dir_if_missing(flags.LOG_PATH)
32     EVENT_MANAGER.callbacks = callbacks.copy()
33 
34     if ENABLE_LEGACY_LOGGER:
35         EVENT_MANAGER.add_logger(
36             _get_logbook_log_config(
37                 flags.DEBUG, flags.USE_COLORS, flags.LOG_CACHE_EVENTS, flags.QUIET
38             )
39         )
40     else:
41         if flags.LOG_LEVEL != "none":
42             line_format = _line_format_from_str(flags.LOG_FORMAT, LineFormat.PlainText)
43             log_level = (
44                 EventLevel.ERROR
45                 if flags.QUIET
46                 else EventLevel.DEBUG
47                 if flags.DEBUG
48                 else EventLevel(flags.LOG_LEVEL)
49             )
50             console_config = _get_stdout_config(
51                 line_format,
52                 flags.USE_COLORS,
53                 log_level,
54                 flags.LOG_CACHE_EVENTS,
55             )
56             EVENT_MANAGER.add_logger(console_config)
57 
58             if _CAPTURE_STREAM:
59                 # Create second stdout logger to support test which want to know what's
60                 # being sent to stdout.
61                 console_config.output_stream = _CAPTURE_STREAM
62                 EVENT_MANAGER.add_logger(console_config)
63 
64         if flags.LOG_LEVEL_FILE != "none":
65             # create and add the file logger to the event manager
66             log_file = os.path.join(flags.LOG_PATH, "dbt.log")
67             log_file_format = _line_format_from_str(flags.LOG_FORMAT_FILE, LineFormat.DebugText)
68             log_level_file = EventLevel.DEBUG if flags.DEBUG else EventLevel(flags.LOG_LEVEL_FILE)
69             EVENT_MANAGER.add_logger(
70                 _get_logfile_config(
71                     log_file,
72                     flags.USE_COLORS_FILE,
73                     log_file_format,
74                     log_level_file,
75                     flags.LOG_FILE_MAX_BYTES,
76                 )
77             )
78 
79 
80 def _line_format_from_str(format_str: str, default: LineFormat) -> LineFormat:
81     if format_str == "text":
82         return LineFormat.PlainText
83     elif format_str == "debug":
84         return LineFormat.DebugText
85     elif format_str == "json":
86         return LineFormat.Json
87 
88     return default
89 
90 
91 def _get_stdout_config(
92     line_format: LineFormat,
93     use_colors: bool,
94     level: EventLevel,
95     log_cache_events: bool,
96 ) -> LoggerConfig:
97 
98     return LoggerConfig(
99         name="stdout_log",
100         level=level,
101         use_colors=use_colors,
102         line_format=line_format,
103         scrubber=env_scrubber,
104         filter=partial(
105             _stdout_filter,
106             log_cache_events,
107             line_format,
108         ),
109         output_stream=sys.stdout,
110     )
111 
112 
113 def _stdout_filter(
114     log_cache_events: bool,
115     line_format: LineFormat,
116     msg: EventMsg,
117 ) -> bool:
118     return (msg.info.name not in ["CacheAction", "CacheDumpGraph"] or log_cache_events) and not (
119         line_format == LineFormat.Json and type(msg.data) == Formatting
120     )
121 
122 
123 def _get_logfile_config(
124     log_path: str,
125     use_colors: bool,
126     line_format: LineFormat,
127     level: EventLevel,
128     log_file_max_bytes: int,
129 ) -> LoggerConfig:
130     return LoggerConfig(
131         name="file_log",
132         line_format=line_format,
133         use_colors=use_colors,
134         level=level,  # File log is *always* debug level
135         scrubber=env_scrubber,
136         filter=partial(_logfile_filter, bool(get_flags().LOG_CACHE_EVENTS), line_format),
137         output_file_name=log_path,
138         output_file_max_bytes=log_file_max_bytes,
139     )
140 
141 
142 def _logfile_filter(log_cache_events: bool, line_format: LineFormat, msg: EventMsg) -> bool:
143     return (
144         msg.info.code not in nofile_codes
145         and not (msg.info.name in ["CacheAction", "CacheDumpGraph"] and not log_cache_events)
146         and not (line_format == LineFormat.Json and type(msg.data) == Formatting)
147     )
148 
149 
150 def _get_logbook_log_config(
151     debug: bool, use_colors: bool, log_cache_events: bool, quiet: bool
152 ) -> LoggerConfig:
153     config = _get_stdout_config(
154         LineFormat.PlainText,
155         use_colors,
156         EventLevel.ERROR if quiet else EventLevel.DEBUG if debug else EventLevel.INFO,
157         log_cache_events,
158     )
159     config.name = "logbook_log"
160     config.filter = (
161         NoFilter
162         if log_cache_events
163         else lambda e: e.info.name not in ["CacheAction", "CacheDumpGraph"]
164     )
165     config.logger = GLOBAL_LOGGER
166     config.output_stream = None
167     return config
168 
169 
170 def env_scrubber(msg: str) -> str:
171     return scrub_secrets(msg, env_secrets())
172 
173 
174 def cleanup_event_logger():
175     # Reset to a no-op manager to release streams associated with logs. This is
176     # especially important for tests, since pytest replaces the stdout stream
177     # during test runs, and closes the stream after the test is over.
178     EVENT_MANAGER.loggers.clear()
179     EVENT_MANAGER.callbacks.clear()
180 
181 
182 # Since dbt-rpc does not do its own log setup, and since some events can
183 # currently fire before logs can be configured by setup_event_logger(), we
184 # create a default configuration with default settings and no file output.
185 EVENT_MANAGER: IEventManager = EventManager()
186 EVENT_MANAGER.add_logger(
187     _get_logbook_log_config(False, True, False, False)  # type: ignore
188     if ENABLE_LEGACY_LOGGER
189     else _get_stdout_config(LineFormat.PlainText, True, EventLevel.INFO, False)
190 )
191 
192 # This global, and the following two functions for capturing stdout logs are
193 # an unpleasant hack we intend to remove as part of API-ification. The GitHub
194 # issue #6350 was opened for that work.
195 _CAPTURE_STREAM: Optional[TextIO] = None
196 
197 
198 # used for integration tests
199 def capture_stdout_logs(stream: TextIO):
200     global _CAPTURE_STREAM
201     _CAPTURE_STREAM = stream
202 
203 
204 def stop_capture_stdout_logs():
205     global _CAPTURE_STREAM
206     _CAPTURE_STREAM = None
207 
208 
209 # returns a dictionary representation of the event fields.
210 # the message may contain secrets which must be scrubbed at the usage site.
211 def msg_to_json(msg: EventMsg) -> str:
212     msg_dict = msg_to_dict(msg)
213     raw_log_line = json.dumps(msg_dict, sort_keys=True, cls=dbt.utils.ForgivingJSONEncoder)
214     return raw_log_line
215 
216 
217 def msg_to_dict(msg: EventMsg) -> dict:
218     msg_dict = dict()
219     try:
220         msg_dict = MessageToDict(
221             msg, preserving_proto_field_name=True, including_default_value_fields=True  # type: ignore
222         )
223     except Exception as exc:
224         event_type = type(msg).__name__
225         fire_event(
226             Note(msg=f"type {event_type} is not serializable. {str(exc)}"), level=EventLevel.WARN
227         )
228     # We don't want an empty NodeInfo in output
229     if (
230         "data" in msg_dict
231         and "node_info" in msg_dict["data"]
232         and msg_dict["data"]["node_info"]["node_name"] == ""
233     ):
234         del msg_dict["data"]["node_info"]
235     return msg_dict
236 
237 
238 def warn_or_error(event, node=None):
239     flags = get_flags()
240     if flags.WARN_ERROR or flags.WARN_ERROR_OPTIONS.includes(type(event).__name__):
241 
242         # TODO: resolve this circular import when at top
243         from dbt.exceptions import EventCompilationError
244 
245         raise EventCompilationError(event.message(), node)
246     else:
247         fire_event(event)
248 
249 
250 # an alternative to fire_event which only creates and logs the event value
251 # if the condition is met. Does nothing otherwise.
252 def fire_event_if(
253     conditional: bool, lazy_e: Callable[[], BaseEvent], level: Optional[EventLevel] = None
254 ) -> None:
255     if conditional:
256         fire_event(lazy_e(), level=level)
257 
258 
259 # a special case of fire_event_if, to only fire events in our unit/functional tests
260 def fire_event_if_test(
261     lazy_e: Callable[[], BaseEvent], level: Optional[EventLevel] = None
262 ) -> None:
263     fire_event_if(conditional=("pytest" in sys.modules), lazy_e=lazy_e, level=level)
264 
265 
266 # top-level method for accessing the new eventing system
267 # this is where all the side effects happen branched by event type
268 # (i.e. - mutating the event history, printing to stdout, logging
269 # to files, etc.)
270 def fire_event(e: BaseEvent, level: Optional[EventLevel] = None) -> None:
271     EVENT_MANAGER.fire_event(e, level=level)
272 
273 
274 def get_metadata_vars() -> Dict[str, str]:
275     global metadata_vars
276     if metadata_vars is None:
277         metadata_vars = {
278             k[len(METADATA_ENV_PREFIX) :]: v
279             for k, v in os.environ.items()
280             if k.startswith(METADATA_ENV_PREFIX)
281         }
282     return metadata_vars
283 
284 
285 def reset_metadata_vars() -> None:
286     global metadata_vars
287     metadata_vars = None
288 
289 
290 def get_invocation_id() -> str:
291     return EVENT_MANAGER.invocation_id
292 
293 
294 def set_invocation_id() -> None:
295     # This is primarily for setting the invocation_id for separate
296     # commands in the dbt servers. It shouldn't be necessary for the CLI.
297     EVENT_MANAGER.invocation_id = str(uuid.uuid4())
298 
299 
300 def ctx_set_event_manager(event_manager: IEventManager):
301     global EVENT_MANAGER
302     EVENT_MANAGER = event_manager
303
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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
@@ -2,7 +2,7 @@
 from dbt.events.base_types import BaseEvent, EventLevel, EventMsg
 from dbt.events.eventmgr import EventManager, LoggerConfig, LineFormat, NoFilter, IEventManager
 from dbt.events.helpers import env_secrets, scrub_secrets
-from dbt.events.types import Formatting, Note
+from dbt.events.types import Note
 from dbt.flags import get_flags, ENABLE_LEGACY_LOGGER
 from dbt.logger import GLOBAL_LOGGER, make_log_dir_if_missing
 from functools import partial
@@ -115,9 +115,7 @@
     line_format: LineFormat,
     msg: EventMsg,
 ) -> bool:
-    return (msg.info.name not in ["CacheAction", "CacheDumpGraph"] or log_cache_events) and not (
-        line_format == LineFormat.Json and type(msg.data) == Formatting
-    )
+    return msg.info.name not in ["CacheAction", "CacheDumpGraph"] or log_cache_events
 
 
 def _get_logfile_config(
@@ -140,10 +138,8 @@
 
 
 def _logfile_filter(log_cache_events: bool, line_format: LineFormat, msg: EventMsg) -> bool:
-    return (
-        msg.info.code not in nofile_codes
-        and not (msg.info.name in ["CacheAction", "CacheDumpGraph"] and not log_cache_events)
-        and not (line_format == LineFormat.Json and type(msg.data) == Formatting)
+    return msg.info.code not in nofile_codes and not (
+        msg.info.name in ["CacheAction", "CacheDumpGraph"] and not log_cache_events
     )

{"golden_diff": "diff --git a/core/dbt/events/functions.py b/core/dbt/events/functions.py\n--- a/core/dbt/events/functions.py\n+++ b/core/dbt/events/functions.py\n@@ -2,7 +2,7 @@\n from dbt.events.base_types import BaseEvent, EventLevel, EventMsg\n from dbt.events.eventmgr import EventManager, LoggerConfig, LineFormat, NoFilter, IEventManager\n from dbt.events.helpers import env_secrets, scrub_secrets\n-from dbt.events.types import Formatting, Note\n+from dbt.events.types import Note\n from dbt.flags import get_flags, ENABLE_LEGACY_LOGGER\n from dbt.logger import GLOBAL_LOGGER, make_log_dir_if_missing\n from functools import partial\n@@ -115,9 +115,7 @@\n     line_format: LineFormat,\n     msg: EventMsg,\n ) -> bool:\n-    return (msg.info.name not in [\"CacheAction\", \"CacheDumpGraph\"] or log_cache_events) and not (\n-        line_format == LineFormat.Json and type(msg.data) == Formatting\n-    )\n+    return msg.info.name not in [\"CacheAction\", \"CacheDumpGraph\"] or log_cache_events\n \n \n def _get_logfile_config(\n@@ -140,10 +138,8 @@\n \n \n def _logfile_filter(log_cache_events: bool, line_format: LineFormat, msg: EventMsg) -> bool:\n-    return (\n-        msg.info.code not in nofile_codes\n-        and not (msg.info.name in [\"CacheAction\", \"CacheDumpGraph\"] and not log_cache_events)\n-        and not (line_format == LineFormat.Json and type(msg.data) == Formatting)\n+    return msg.info.code not in nofile_codes and not (\n+        msg.info.name in [\"CacheAction\", \"CacheDumpGraph\"] and not log_cache_events\n     )\n", "issue": "[CT-2139] Don't exclude `Formatting` events from JSON-formatted logs\nWe have some events that are only for formatting / aesthetic display in the terminal. (Previously, this was called `EmptyLine`; in v1.5 we're renaming it to `Formatting`.) Historically, we've intentionally _excluded_ these events from JSON-formatted structured logs. This makes sense in one way, but it's actively unhelpful for someone who wants _both_ the full structure of JSON logs _and_ the ability to produce their own text-formatted logs at parity with `dbt-core`'s.\r\n\r\n_copying from slack_\r\n> @jtcohen6\r\n> While it's great to be opinionated about what & how we display `text`, it does feel weird to me that we're depriving the json log consumer of some detail/flexibility.\r\n> @peterallenwebb:\r\n> On balance, I think we should include the EmptyLine/Formatting events. I infer that the original logic behind excluding them was that they would be confusing/unwanted by consumers, but the existence of this use case suggests otherwise.\r\n\r\n### Proposed change\r\n\r\nRemove the line from `_stdout_filter` that filters out `Formatting` if we're emitting JSON-formatted logs:\r\nhttps://github.com/dbt-labs/dbt-core/blob/c952d44ec5c2506995fbad75320acbae49125d3d/core/dbt/events/functions.py#L89\r\n\r\n[`TextOnly`](https://github.com/dbt-labs/dbt-core/search?q=TextOnly) achieves the same purpose, for legacy logging _only_. We could remove it now, or remove it when we deprecate legacy logging in ~v1.6~ v1.7.\n", "before_files": [{"content": "from dbt.constants import METADATA_ENV_PREFIX\nfrom dbt.events.base_types import BaseEvent, EventLevel, EventMsg\nfrom dbt.events.eventmgr import EventManager, LoggerConfig, LineFormat, NoFilter, IEventManager\nfrom dbt.events.helpers import env_secrets, scrub_secrets\nfrom dbt.events.types import Formatting, Note\nfrom dbt.flags import get_flags, ENABLE_LEGACY_LOGGER\nfrom dbt.logger import GLOBAL_LOGGER, make_log_dir_if_missing\nfrom functools import partial\nimport json\nimport os\nimport sys\nfrom typing import Callable, Dict, List, Optional, TextIO\nimport uuid\nfrom google.protobuf.json_format import MessageToDict\n\nimport dbt.utils\n\nLOG_VERSION = 3\nmetadata_vars: Optional[Dict[str, str]] = None\n\n# These are the logging events issued by the \"clean\" command,\n# where we can't count on having a log directory. We've removed\n# the \"class\" flags on the events in types.py. If necessary we\n# could still use class or method flags, but we'd have to get\n# the type class from the msg and then get the information from the class.\nnofile_codes = [\"Z012\", \"Z013\", \"Z014\", \"Z015\"]\n\n\ndef setup_event_logger(flags, callbacks: List[Callable[[EventMsg], None]] = []) -> None:\n    cleanup_event_logger()\n    make_log_dir_if_missing(flags.LOG_PATH)\n    EVENT_MANAGER.callbacks = callbacks.copy()\n\n    if ENABLE_LEGACY_LOGGER:\n        EVENT_MANAGER.add_logger(\n            _get_logbook_log_config(\n                flags.DEBUG, flags.USE_COLORS, flags.LOG_CACHE_EVENTS, flags.QUIET\n            )\n        )\n    else:\n        if flags.LOG_LEVEL != \"none\":\n            line_format = _line_format_from_str(flags.LOG_FORMAT, LineFormat.PlainText)\n            log_level = (\n                EventLevel.ERROR\n                if flags.QUIET\n                else EventLevel.DEBUG\n                if flags.DEBUG\n                else EventLevel(flags.LOG_LEVEL)\n            )\n            console_config = _get_stdout_config(\n                line_format,\n                flags.USE_COLORS,\n                log_level,\n                flags.LOG_CACHE_EVENTS,\n            )\n            EVENT_MANAGER.add_logger(console_config)\n\n            if _CAPTURE_STREAM:\n                # Create second stdout logger to support test which want to know what's\n                # being sent to stdout.\n                console_config.output_stream = _CAPTURE_STREAM\n                EVENT_MANAGER.add_logger(console_config)\n\n        if flags.LOG_LEVEL_FILE != \"none\":\n            # create and add the file logger to the event manager\n            log_file = os.path.join(flags.LOG_PATH, \"dbt.log\")\n            log_file_format = _line_format_from_str(flags.LOG_FORMAT_FILE, LineFormat.DebugText)\n            log_level_file = EventLevel.DEBUG if flags.DEBUG else EventLevel(flags.LOG_LEVEL_FILE)\n            EVENT_MANAGER.add_logger(\n                _get_logfile_config(\n                    log_file,\n                    flags.USE_COLORS_FILE,\n                    log_file_format,\n                    log_level_file,\n                    flags.LOG_FILE_MAX_BYTES,\n                )\n            )\n\n\ndef _line_format_from_str(format_str: str, default: LineFormat) -> LineFormat:\n    if format_str == \"text\":\n        return LineFormat.PlainText\n    elif format_str == \"debug\":\n        return LineFormat.DebugText\n    elif format_str == \"json\":\n        return LineFormat.Json\n\n    return default\n\n\ndef _get_stdout_config(\n    line_format: LineFormat,\n    use_colors: bool,\n    level: EventLevel,\n    log_cache_events: bool,\n) -> LoggerConfig:\n\n    return LoggerConfig(\n        name=\"stdout_log\",\n        level=level,\n        use_colors=use_colors,\n        line_format=line_format,\n        scrubber=env_scrubber,\n        filter=partial(\n            _stdout_filter,\n            log_cache_events,\n            line_format,\n        ),\n        output_stream=sys.stdout,\n    )\n\n\ndef _stdout_filter(\n    log_cache_events: bool,\n    line_format: LineFormat,\n    msg: EventMsg,\n) -> bool:\n    return (msg.info.name not in [\"CacheAction\", \"CacheDumpGraph\"] or log_cache_events) and not (\n        line_format == LineFormat.Json and type(msg.data) == Formatting\n    )\n\n\ndef _get_logfile_config(\n    log_path: str,\n    use_colors: bool,\n    line_format: LineFormat,\n    level: EventLevel,\n    log_file_max_bytes: int,\n) -> LoggerConfig:\n    return LoggerConfig(\n        name=\"file_log\",\n        line_format=line_format,\n        use_colors=use_colors,\n        level=level,  # File log is *always* debug level\n        scrubber=env_scrubber,\n        filter=partial(_logfile_filter, bool(get_flags().LOG_CACHE_EVENTS), line_format),\n        output_file_name=log_path,\n        output_file_max_bytes=log_file_max_bytes,\n    )\n\n\ndef _logfile_filter(log_cache_events: bool, line_format: LineFormat, msg: EventMsg) -> bool:\n    return (\n        msg.info.code not in nofile_codes\n        and not (msg.info.name in [\"CacheAction\", \"CacheDumpGraph\"] and not log_cache_events)\n        and not (line_format == LineFormat.Json and type(msg.data) == Formatting)\n    )\n\n\ndef _get_logbook_log_config(\n    debug: bool, use_colors: bool, log_cache_events: bool, quiet: bool\n) -> LoggerConfig:\n    config = _get_stdout_config(\n        LineFormat.PlainText,\n        use_colors,\n        EventLevel.ERROR if quiet else EventLevel.DEBUG if debug else EventLevel.INFO,\n        log_cache_events,\n    )\n    config.name = \"logbook_log\"\n    config.filter = (\n        NoFilter\n        if log_cache_events\n        else lambda e: e.info.name not in [\"CacheAction\", \"CacheDumpGraph\"]\n    )\n    config.logger = GLOBAL_LOGGER\n    config.output_stream = None\n    return config\n\n\ndef env_scrubber(msg: str) -> str:\n    return scrub_secrets(msg, env_secrets())\n\n\ndef cleanup_event_logger():\n    # Reset to a no-op manager to release streams associated with logs. This is\n    # especially important for tests, since pytest replaces the stdout stream\n    # during test runs, and closes the stream after the test is over.\n    EVENT_MANAGER.loggers.clear()\n    EVENT_MANAGER.callbacks.clear()\n\n\n# Since dbt-rpc does not do its own log setup, and since some events can\n# currently fire before logs can be configured by setup_event_logger(), we\n# create a default configuration with default settings and no file output.\nEVENT_MANAGER: IEventManager = EventManager()\nEVENT_MANAGER.add_logger(\n    _get_logbook_log_config(False, True, False, False)  # type: ignore\n    if ENABLE_LEGACY_LOGGER\n    else _get_stdout_config(LineFormat.PlainText, True, EventLevel.INFO, False)\n)\n\n# This global, and the following two functions for capturing stdout logs are\n# an unpleasant hack we intend to remove as part of API-ification. The GitHub\n# issue #6350 was opened for that work.\n_CAPTURE_STREAM: Optional[TextIO] = None\n\n\n# used for integration tests\ndef capture_stdout_logs(stream: TextIO):\n    global _CAPTURE_STREAM\n    _CAPTURE_STREAM = stream\n\n\ndef stop_capture_stdout_logs():\n    global _CAPTURE_STREAM\n    _CAPTURE_STREAM = None\n\n\n# returns a dictionary representation of the event fields.\n# the message may contain secrets which must be scrubbed at the usage site.\ndef msg_to_json(msg: EventMsg) -> str:\n    msg_dict = msg_to_dict(msg)\n    raw_log_line = json.dumps(msg_dict, sort_keys=True, cls=dbt.utils.ForgivingJSONEncoder)\n    return raw_log_line\n\n\ndef msg_to_dict(msg: EventMsg) -> dict:\n    msg_dict = dict()\n    try:\n        msg_dict = MessageToDict(\n            msg, preserving_proto_field_name=True, including_default_value_fields=True  # type: ignore\n        )\n    except Exception as exc:\n        event_type = type(msg).__name__\n        fire_event(\n            Note(msg=f\"type {event_type} is not serializable. {str(exc)}\"), level=EventLevel.WARN\n        )\n    # We don't want an empty NodeInfo in output\n    if (\n        \"data\" in msg_dict\n        and \"node_info\" in msg_dict[\"data\"]\n        and msg_dict[\"data\"][\"node_info\"][\"node_name\"] == \"\"\n    ):\n        del msg_dict[\"data\"][\"node_info\"]\n    return msg_dict\n\n\ndef warn_or_error(event, node=None):\n    flags = get_flags()\n    if flags.WARN_ERROR or flags.WARN_ERROR_OPTIONS.includes(type(event).__name__):\n\n        # TODO: resolve this circular import when at top\n        from dbt.exceptions import EventCompilationError\n\n        raise EventCompilationError(event.message(), node)\n    else:\n        fire_event(event)\n\n\n# an alternative to fire_event which only creates and logs the event value\n# if the condition is met. Does nothing otherwise.\ndef fire_event_if(\n    conditional: bool, lazy_e: Callable[[], BaseEvent], level: Optional[EventLevel] = None\n) -> None:\n    if conditional:\n        fire_event(lazy_e(), level=level)\n\n\n# a special case of fire_event_if, to only fire events in our unit/functional tests\ndef fire_event_if_test(\n    lazy_e: Callable[[], BaseEvent], level: Optional[EventLevel] = None\n) -> None:\n    fire_event_if(conditional=(\"pytest\" in sys.modules), lazy_e=lazy_e, level=level)\n\n\n# top-level method for accessing the new eventing system\n# this is where all the side effects happen branched by event type\n# (i.e. - mutating the event history, printing to stdout, logging\n# to files, etc.)\ndef fire_event(e: BaseEvent, level: Optional[EventLevel] = None) -> None:\n    EVENT_MANAGER.fire_event(e, level=level)\n\n\ndef get_metadata_vars() -> Dict[str, str]:\n    global metadata_vars\n    if metadata_vars is None:\n        metadata_vars = {\n            k[len(METADATA_ENV_PREFIX) :]: v\n            for k, v in os.environ.items()\n            if k.startswith(METADATA_ENV_PREFIX)\n        }\n    return metadata_vars\n\n\ndef reset_metadata_vars() -> None:\n    global metadata_vars\n    metadata_vars = None\n\n\ndef get_invocation_id() -> str:\n    return EVENT_MANAGER.invocation_id\n\n\ndef set_invocation_id() -> None:\n    # This is primarily for setting the invocation_id for separate\n    # commands in the dbt servers. It shouldn't be necessary for the CLI.\n    EVENT_MANAGER.invocation_id = str(uuid.uuid4())\n\n\ndef ctx_set_event_manager(event_manager: IEventManager):\n    global EVENT_MANAGER\n    EVENT_MANAGER = event_manager\n", "path": "core/dbt/events/functions.py"}], "after_files": [{"content": "from dbt.constants import METADATA_ENV_PREFIX\nfrom dbt.events.base_types import BaseEvent, EventLevel, EventMsg\nfrom dbt.events.eventmgr import EventManager, LoggerConfig, LineFormat, NoFilter, IEventManager\nfrom dbt.events.helpers import env_secrets, scrub_secrets\nfrom dbt.events.types import Note\nfrom dbt.flags import get_flags, ENABLE_LEGACY_LOGGER\nfrom dbt.logger import GLOBAL_LOGGER, make_log_dir_if_missing\nfrom functools import partial\nimport json\nimport os\nimport sys\nfrom typing import Callable, Dict, List, Optional, TextIO\nimport uuid\nfrom google.protobuf.json_format import MessageToDict\n\nimport dbt.utils\n\nLOG_VERSION = 3\nmetadata_vars: Optional[Dict[str, str]] = None\n\n# These are the logging events issued by the \"clean\" command,\n# where we can't count on having a log directory. We've removed\n# the \"class\" flags on the events in types.py. If necessary we\n# could still use class or method flags, but we'd have to get\n# the type class from the msg and then get the information from the class.\nnofile_codes = [\"Z012\", \"Z013\", \"Z014\", \"Z015\"]\n\n\ndef setup_event_logger(flags, callbacks: List[Callable[[EventMsg], None]] = []) -> None:\n    cleanup_event_logger()\n    make_log_dir_if_missing(flags.LOG_PATH)\n    EVENT_MANAGER.callbacks = callbacks.copy()\n\n    if ENABLE_LEGACY_LOGGER:\n        EVENT_MANAGER.add_logger(\n            _get_logbook_log_config(\n                flags.DEBUG, flags.USE_COLORS, flags.LOG_CACHE_EVENTS, flags.QUIET\n            )\n        )\n    else:\n        if flags.LOG_LEVEL != \"none\":\n            line_format = _line_format_from_str(flags.LOG_FORMAT, LineFormat.PlainText)\n            log_level = (\n                EventLevel.ERROR\n                if flags.QUIET\n                else EventLevel.DEBUG\n                if flags.DEBUG\n                else EventLevel(flags.LOG_LEVEL)\n            )\n            console_config = _get_stdout_config(\n                line_format,\n                flags.USE_COLORS,\n                log_level,\n                flags.LOG_CACHE_EVENTS,\n            )\n            EVENT_MANAGER.add_logger(console_config)\n\n            if _CAPTURE_STREAM:\n                # Create second stdout logger to support test which want to know what's\n                # being sent to stdout.\n                console_config.output_stream = _CAPTURE_STREAM\n                EVENT_MANAGER.add_logger(console_config)\n\n        if flags.LOG_LEVEL_FILE != \"none\":\n            # create and add the file logger to the event manager\n            log_file = os.path.join(flags.LOG_PATH, \"dbt.log\")\n            log_file_format = _line_format_from_str(flags.LOG_FORMAT_FILE, LineFormat.DebugText)\n            log_level_file = EventLevel.DEBUG if flags.DEBUG else EventLevel(flags.LOG_LEVEL_FILE)\n            EVENT_MANAGER.add_logger(\n                _get_logfile_config(\n                    log_file,\n                    flags.USE_COLORS_FILE,\n                    log_file_format,\n                    log_level_file,\n                    flags.LOG_FILE_MAX_BYTES,\n                )\n            )\n\n\ndef _line_format_from_str(format_str: str, default: LineFormat) -> LineFormat:\n    if format_str == \"text\":\n        return LineFormat.PlainText\n    elif format_str == \"debug\":\n        return LineFormat.DebugText\n    elif format_str == \"json\":\n        return LineFormat.Json\n\n    return default\n\n\ndef _get_stdout_config(\n    line_format: LineFormat,\n    use_colors: bool,\n    level: EventLevel,\n    log_cache_events: bool,\n) -> LoggerConfig:\n\n    return LoggerConfig(\n        name=\"stdout_log\",\n        level=level,\n        use_colors=use_colors,\n        line_format=line_format,\n        scrubber=env_scrubber,\n        filter=partial(\n            _stdout_filter,\n            log_cache_events,\n            line_format,\n        ),\n        output_stream=sys.stdout,\n    )\n\n\ndef _stdout_filter(\n    log_cache_events: bool,\n    line_format: LineFormat,\n    msg: EventMsg,\n) -> bool:\n    return msg.info.name not in [\"CacheAction\", \"CacheDumpGraph\"] or log_cache_events\n\n\ndef _get_logfile_config(\n    log_path: str,\n    use_colors: bool,\n    line_format: LineFormat,\n    level: EventLevel,\n    log_file_max_bytes: int,\n) -> LoggerConfig:\n    return LoggerConfig(\n        name=\"file_log\",\n        line_format=line_format,\n        use_colors=use_colors,\n        level=level,  # File log is *always* debug level\n        scrubber=env_scrubber,\n        filter=partial(_logfile_filter, bool(get_flags().LOG_CACHE_EVENTS), line_format),\n        output_file_name=log_path,\n        output_file_max_bytes=log_file_max_bytes,\n    )\n\n\ndef _logfile_filter(log_cache_events: bool, line_format: LineFormat, msg: EventMsg) -> bool:\n    return msg.info.code not in nofile_codes and not (\n        msg.info.name in [\"CacheAction\", \"CacheDumpGraph\"] and not log_cache_events\n    )\n\n\ndef _get_logbook_log_config(\n    debug: bool, use_colors: bool, log_cache_events: bool, quiet: bool\n) -> LoggerConfig:\n    config = _get_stdout_config(\n        LineFormat.PlainText,\n        use_colors,\n        EventLevel.ERROR if quiet else EventLevel.DEBUG if debug else EventLevel.INFO,\n        log_cache_events,\n    )\n    config.name = \"logbook_log\"\n    config.filter = (\n        NoFilter\n        if log_cache_events\n        else lambda e: e.info.name not in [\"CacheAction\", \"CacheDumpGraph\"]\n    )\n    config.logger = GLOBAL_LOGGER\n    config.output_stream = None\n    return config\n\n\ndef env_scrubber(msg: str) -> str:\n    return scrub_secrets(msg, env_secrets())\n\n\ndef cleanup_event_logger():\n    # Reset to a no-op manager to release streams associated with logs. This is\n    # especially important for tests, since pytest replaces the stdout stream\n    # during test runs, and closes the stream after the test is over.\n    EVENT_MANAGER.loggers.clear()\n    EVENT_MANAGER.callbacks.clear()\n\n\n# Since dbt-rpc does not do its own log setup, and since some events can\n# currently fire before logs can be configured by setup_event_logger(), we\n# create a default configuration with default settings and no file output.\nEVENT_MANAGER: IEventManager = EventManager()\nEVENT_MANAGER.add_logger(\n    _get_logbook_log_config(False, True, False, False)  # type: ignore\n    if ENABLE_LEGACY_LOGGER\n    else _get_stdout_config(LineFormat.PlainText, True, EventLevel.INFO, False)\n)\n\n# This global, and the following two functions for capturing stdout logs are\n# an unpleasant hack we intend to remove as part of API-ification. The GitHub\n# issue #6350 was opened for that work.\n_CAPTURE_STREAM: Optional[TextIO] = None\n\n\n# used for integration tests\ndef capture_stdout_logs(stream: TextIO):\n    global _CAPTURE_STREAM\n    _CAPTURE_STREAM = stream\n\n\ndef stop_capture_stdout_logs():\n    global _CAPTURE_STREAM\n    _CAPTURE_STREAM = None\n\n\n# returns a dictionary representation of the event fields.\n# the message may contain secrets which must be scrubbed at the usage site.\ndef msg_to_json(msg: EventMsg) -> str:\n    msg_dict = msg_to_dict(msg)\n    raw_log_line = json.dumps(msg_dict, sort_keys=True, cls=dbt.utils.ForgivingJSONEncoder)\n    return raw_log_line\n\n\ndef msg_to_dict(msg: EventMsg) -> dict:\n    msg_dict = dict()\n    try:\n        msg_dict = MessageToDict(\n            msg, preserving_proto_field_name=True, including_default_value_fields=True  # type: ignore\n        )\n    except Exception as exc:\n        event_type = type(msg).__name__\n        fire_event(\n            Note(msg=f\"type {event_type} is not serializable. {str(exc)}\"), level=EventLevel.WARN\n        )\n    # We don't want an empty NodeInfo in output\n    if (\n        \"data\" in msg_dict\n        and \"node_info\" in msg_dict[\"data\"]\n        and msg_dict[\"data\"][\"node_info\"][\"node_name\"] == \"\"\n    ):\n        del msg_dict[\"data\"][\"node_info\"]\n    return msg_dict\n\n\ndef warn_or_error(event, node=None):\n    flags = get_flags()\n    if flags.WARN_ERROR or flags.WARN_ERROR_OPTIONS.includes(type(event).__name__):\n\n        # TODO: resolve this circular import when at top\n        from dbt.exceptions import EventCompilationError\n\n        raise EventCompilationError(event.message(), node)\n    else:\n        fire_event(event)\n\n\n# an alternative to fire_event which only creates and logs the event value\n# if the condition is met. Does nothing otherwise.\ndef fire_event_if(\n    conditional: bool, lazy_e: Callable[[], BaseEvent], level: Optional[EventLevel] = None\n) -> None:\n    if conditional:\n        fire_event(lazy_e(), level=level)\n\n\n# a special case of fire_event_if, to only fire events in our unit/functional tests\ndef fire_event_if_test(\n    lazy_e: Callable[[], BaseEvent], level: Optional[EventLevel] = None\n) -> None:\n    fire_event_if(conditional=(\"pytest\" in sys.modules), lazy_e=lazy_e, level=level)\n\n\n# top-level method for accessing the new eventing system\n# this is where all the side effects happen branched by event type\n# (i.e. - mutating the event history, printing to stdout, logging\n# to files, etc.)\ndef fire_event(e: BaseEvent, level: Optional[EventLevel] = None) -> None:\n    EVENT_MANAGER.fire_event(e, level=level)\n\n\ndef get_metadata_vars() -> Dict[str, str]:\n    global metadata_vars\n    if metadata_vars is None:\n        metadata_vars = {\n            k[len(METADATA_ENV_PREFIX) :]: v\n            for k, v in os.environ.items()\n            if k.startswith(METADATA_ENV_PREFIX)\n        }\n    return metadata_vars\n\n\ndef reset_metadata_vars() -> None:\n    global metadata_vars\n    metadata_vars = None\n\n\ndef get_invocation_id() -> str:\n    return EVENT_MANAGER.invocation_id\n\n\ndef set_invocation_id() -> None:\n    # This is primarily for setting the invocation_id for separate\n    # commands in the dbt servers. It shouldn't be necessary for the CLI.\n    EVENT_MANAGER.invocation_id = str(uuid.uuid4())\n\n\ndef ctx_set_event_manager(event_manager: IEventManager):\n    global EVENT_MANAGER\n    EVENT_MANAGER = event_manager\n", "path": "core/dbt/events/functions.py"}]}