diff --git a/core/dbt/events/README.md b/core/dbt/events/README.md index f3f765f34d6..a49fa070c66 100644 --- a/core/dbt/events/README.md +++ b/core/dbt/events/README.md @@ -7,3 +7,16 @@ The event module provides types that represent what is happening in dbt in `even # Adding a New Event In `events.types` add a new class that represents the new event. This may be a simple class with no values, or it may be a dataclass with some values to construct downstream messaging. Only include the data necessary to construct this message within this class. You must extend all destinations (e.g. - if your log message belongs on the cli, extend `CliEventABC`) as well as the loglevel this event belongs to. + +# Adapter Maintainers +To integrate existing log messages from adapters, you likely have a line of code like this in your adapter already: +```python +from dbt.logger import GLOBAL_LOGGER as logger +``` + +Simply change it to these two lines with your adapter's database name, and all your existing call sites will now use the new system for v1.0: +```python +from dbt.events import AdapterLogger +logger = AdapterLogger("") +# e.g. AdapterLogger("Snowflake") +``` diff --git a/core/dbt/events/__init__.py b/core/dbt/events/__init__.py index e69de29bb2d..d0fc24d7bb5 100644 --- a/core/dbt/events/__init__.py +++ b/core/dbt/events/__init__.py @@ -0,0 +1 @@ +from .adapter_endpoint import AdapterLogger # noqa: F401 diff --git a/core/dbt/events/adapter_endpoint.py b/core/dbt/events/adapter_endpoint.py new file mode 100644 index 00000000000..aad71080acc --- /dev/null +++ b/core/dbt/events/adapter_endpoint.py @@ -0,0 +1,86 @@ +from dataclasses import dataclass +from dbt.events.functions import fire_event +from dbt.events.types import ( + AdapterEventDebug, AdapterEventInfo, AdapterEventWarning, AdapterEventError +) +from typing import Any + + +@dataclass +class AdapterLogger(): + name: str + + def debug( + self, + msg: str, + exc_info: Any = None, + stack_info: Any = None, + extra: Any = None + ): + event = AdapterEventDebug(name=self.name, raw_msg=msg) + + event.exc_info = exc_info + event.stack_info = stack_info + event.extra = extra + + fire_event(event) + + def info( + self, + msg: str, + exc_info: Any = None, + stack_info: Any = None, + extra: Any = None + ): + event = AdapterEventInfo(name=self.name, raw_msg=msg) + + event.exc_info = exc_info + event.stack_info = stack_info + event.extra = extra + + fire_event(event) + + def warning( + self, + msg: str, + exc_info: Any = None, + stack_info: Any = None, + extra: Any = None + ): + event = AdapterEventWarning(name=self.name, raw_msg=msg) + + event.exc_info = exc_info + event.stack_info = stack_info + event.extra = extra + + fire_event(event) + + def error( + self, + msg: str, + exc_info: Any = None, + stack_info: Any = None, + extra: Any = None + ): + event = AdapterEventError(name=self.name, raw_msg=msg) + + event.exc_info = exc_info + event.stack_info = stack_info + event.extra = extra + + fire_event(event) + + def exception( + self, + msg: str, + exc_info: Any = True, # this default is what makes this method different + stack_info: Any = None, + extra: Any = None + ): + event = AdapterEventError(name=self.name, raw_msg=msg) + + event.exc_info = exc_info + event.stack_info = stack_info + event.extra = extra + + fire_event(event) diff --git a/core/dbt/events/types.py b/core/dbt/events/types.py index c8a1c8dedc1..4822da8775d 100644 --- a/core/dbt/events/types.py +++ b/core/dbt/events/types.py @@ -60,6 +60,31 @@ def cli_msg(self) -> str: raise Exception("cli_msg not implemented for cli event") +@dataclass +class AdapterEventBase(): + name: str + raw_msg: str + + def cli_msg(self) -> str: + return f"{self.name} adapter: {self.raw_msg}" + + +class AdapterEventDebug(DebugLevel, AdapterEventBase, CliEventABC, ShowException): + pass + + +class AdapterEventInfo(InfoLevel, AdapterEventBase, CliEventABC, ShowException): + pass + + +class AdapterEventWarning(WarnLevel, AdapterEventBase, CliEventABC, ShowException): + pass + + +class AdapterEventError(ErrorLevel, AdapterEventBase, CliEventABC, ShowException): + pass + + class ParsingStart(InfoLevel, CliEventABC): def cli_msg(self) -> str: return "Start parsing." diff --git a/plugins/postgres/dbt/adapters/postgres/connections.py b/plugins/postgres/dbt/adapters/postgres/connections.py index 7eaf4c1e26c..ac9bfd8e9a5 100644 --- a/plugins/postgres/dbt/adapters/postgres/connections.py +++ b/plugins/postgres/dbt/adapters/postgres/connections.py @@ -6,13 +6,16 @@ from dbt.adapters.base import Credentials from dbt.adapters.sql import SQLConnectionManager from dbt.contracts.connection import AdapterResponse -from dbt.logger import GLOBAL_LOGGER as logger +from dbt.events import AdapterLogger from dbt.helper_types import Port from dataclasses import dataclass from typing import Optional +logger = AdapterLogger("Postgres") + + @dataclass class PostgresCredentials(Credentials): host: str diff --git a/test/unit/test_events.py b/test/unit/test_events.py new file mode 100644 index 00000000000..1e7105b893b --- /dev/null +++ b/test/unit/test_events.py @@ -0,0 +1,19 @@ +from unittest import mock, TestCase + + +class TestFlags(TestCase): + + def setUp(self): + pass + + # this interface is documented for adapter maintainers to plug into + # so we should test that it at the very least doesn't explode. + def test_adapter_logging_interface(self): + from dbt.events import AdapterLogger + logger = AdapterLogger("dbt_tests") + logger.debug("debug message") + logger.info("info message") + logger.warning("warning message") + logger.error("error message") + logger.exception("exception message") + self.assertTrue(True)