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

Allow adapters to include python package logging in dbt logs #8643

Merged
merged 15 commits into from
Sep 20, 2023
Merged
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20230915-123733.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: 'Allow adapters to include package logs in dbt standard logging '
time: 2023-09-15T12:37:33.862862-07:00
custom:
Author: colin-rogers-dbt
Issue: "7859"
10 changes: 9 additions & 1 deletion core/dbt/events/adapter_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import traceback
from dataclasses import dataclass
from dbt.events.functions import fire_event
from dbt.events.functions import fire_event, EVENT_MANAGER
from dbt.events.contextvars import get_node_info
from dbt.events.event_handler import set_package_logging
from dbt.events.types import (
AdapterEventDebug,
AdapterEventInfo,
Expand Down Expand Up @@ -56,3 +57,10 @@
name=self.name, base_msg=str(msg), args=list(args), node_info=get_node_info()
)
fire_event(event)

@staticmethod
def set_adapter_dependency_log_level(package_name, level):
mikealfare marked this conversation as resolved.
Show resolved Hide resolved
"""By default dbt suppresses non-dbt package logs. This method allows
you to set the log level for a specific package.
"""
set_package_logging(package_name, level, EVENT_MANAGER)

Check warning on line 66 in core/dbt/events/adapter_endpoint.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/events/adapter_endpoint.py#L66

Added line #L66 was not covered by tests
46 changes: 46 additions & 0 deletions core/dbt/events/event_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import logging
from typing import Union

from dbt.events.base_types import EventLevel
from dbt.events.types import Note

from dbt.events.eventmgr import IEventManager


def log_level_to_event_level(log_level: int) -> EventLevel:
colin-rogers-dbt marked this conversation as resolved.
Show resolved Hide resolved
if log_level == logging.DEBUG:
return EventLevel.DEBUG
elif log_level == logging.INFO:
return EventLevel.INFO
elif log_level == logging.WARNING:
return EventLevel.WARN
elif log_level == logging.ERROR:
return EventLevel.ERROR
elif log_level == logging.CRITICAL:
return EventLevel.ERROR
else:
return EventLevel.DEBUG

Check warning on line 22 in core/dbt/events/event_handler.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/events/event_handler.py#L22

Added line #L22 was not covered by tests


class DbtEventLoggingHandler(logging.Handler):
Copy link
Member

Choose a reason for hiding this comment

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

I refactored some of this code into core/dbt/events/logger.py, is it possible to move the contents of this file there for consistency?

Copy link
Contributor Author

@colin-rogers-dbt colin-rogers-dbt Sep 18, 2023

Choose a reason for hiding this comment

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

I think that would create a circular dependency since this depends on eventmgr which depends on logger.py

"""A logging handler that wraps the EventManager
This allows non-dbt packages to log to the dbt event stream.
All logs are generated as "Note" events.
"""

def __init__(self, event_manager: IEventManager, level=logging.NOTSET):
super().__init__(level)
self.event_manager = event_manager

def emit(self, record: logging.LogRecord):
note = Note(msg=record.getMessage())
level = log_level_to_event_level(record.levelno)
self.event_manager.fire_event(e=note, level=level)


def set_package_logging(package_name: str, log_level: Union[str, int], event_mgr: IEventManager):
"""Attach dbt's custom logging handler to the package's logger."""
log = logging.getLogger(package_name)
log.setLevel(log_level)
event_handler = DbtEventLoggingHandler(event_manager=event_mgr, level=log_level)
log.addHandler(event_handler)

Check warning on line 46 in core/dbt/events/event_handler.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/events/event_handler.py#L43-L46

Added lines #L43 - L46 were not covered by tests
2 changes: 1 addition & 1 deletion core/dbt/events/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ def _get_stdout_config(
level: EventLevel,
log_cache_events: bool,
) -> LoggerConfig:

return LoggerConfig(
name="stdout_log",
level=level,
Expand Down Expand Up @@ -127,6 +126,7 @@ def _get_logfile_config(
level: EventLevel,
log_file_max_bytes: int,
) -> LoggerConfig:

return LoggerConfig(
name="file_log",
line_format=line_format,
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/test_event_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import logging

from dbt.events.base_types import EventLevel
from dbt.events.event_handler import DbtEventLoggingHandler
from dbt.events.eventmgr import TestEventManager


def test_event_logging_handler_emits_records_correctly():
event_manager = TestEventManager()
handler = DbtEventLoggingHandler(event_manager=event_manager)
mikealfare marked this conversation as resolved.
Show resolved Hide resolved
log = logging.getLogger("test")
log.setLevel(logging.DEBUG)
log.addHandler(handler)

log.debug("test")
log.info("test")
log.warning("test")
log.error("test")
log.exception("test")
log.critical("test")
assert len(event_manager.event_history) == 6
assert event_manager.event_history[0][1] == EventLevel.DEBUG
assert event_manager.event_history[1][1] == EventLevel.INFO
assert event_manager.event_history[2][1] == EventLevel.WARN
assert event_manager.event_history[3][1] == EventLevel.ERROR
assert event_manager.event_history[4][1] == EventLevel.ERROR
assert event_manager.event_history[5][1] == EventLevel.ERROR