Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapter logging interface #4161

Closed
13 changes: 13 additions & 0 deletions core/dbt/events/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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("<database name>")
# e.g. AdapterLogger("Snowflake")
Comment on lines +12 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

```
1 change: 1 addition & 0 deletions core/dbt/events/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .adapter_endpoint import AdapterLogger # noqa: F401
86 changes: 86 additions & 0 deletions core/dbt/events/adapter_endpoint.py
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this call out.

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)
25 changes: 25 additions & 0 deletions core/dbt/events/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
5 changes: 4 additions & 1 deletion plugins/postgres/dbt/adapters/postgres/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions test/unit/test_events.py
Original file line number Diff line number Diff line change
@@ -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)