Skip to content

Commit d08ece1

Browse files
Chore!: configure logging before loading config to log errors properly (#4509)
Co-authored-by: Iaroslav Zeigerman <zeigerman.ia@gmail.com>
1 parent 00a47a0 commit d08ece1

6 files changed

Lines changed: 52 additions & 19 deletions

File tree

sqlmesh/__init__.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,24 @@ def format(self, record: logging.LogRecord) -> str:
166166
return formatter.format(record)
167167

168168

169+
def remove_excess_logs(
170+
log_file_dir: t.Optional[t.Union[str, Path]] = None,
171+
log_limit: int = c.DEFAULT_LOG_LIMIT,
172+
) -> None:
173+
if log_limit <= 0:
174+
return
175+
176+
log_file_dir = log_file_dir or c.DEFAULT_LOG_FILE_DIR
177+
log_path_prefix = Path(log_file_dir) / LOG_FILENAME_PREFIX
178+
179+
for path in list(sorted(glob.glob(f"{log_path_prefix}*.log"), reverse=True))[log_limit:]:
180+
os.remove(path)
181+
182+
169183
def configure_logging(
170184
force_debug: bool = False,
171185
write_to_stdout: bool = False,
172186
write_to_file: bool = True,
173-
log_limit: int = c.DEFAULT_LOG_LIMIT,
174187
log_file_dir: t.Optional[t.Union[str, Path]] = None,
175188
ignore_warnings: bool = False,
176189
) -> None:
@@ -192,20 +205,18 @@ def configure_logging(
192205

193206
log_file_dir = log_file_dir or c.DEFAULT_LOG_FILE_DIR
194207
log_path_prefix = Path(log_file_dir) / LOG_FILENAME_PREFIX
208+
195209
if write_to_file:
196210
os.makedirs(str(log_file_dir), exist_ok=True)
197211
filename = f"{log_path_prefix}{datetime.now().strftime('%Y_%m_%d_%H_%M_%S')}.log"
198212
file_handler = logging.FileHandler(filename, mode="w", encoding="utf-8")
213+
199214
# the log files should always log at least info so that users will always have
200215
# minimal info for debugging even if they specify "ignore_warnings"
201216
file_handler.setLevel(level)
202217
file_handler.setFormatter(logging.Formatter(LOG_FORMAT))
203218
logger.addHandler(file_handler)
204219

205-
if log_limit > 0:
206-
for path in list(sorted(glob.glob(f"{log_path_prefix}*.log"), reverse=True))[log_limit:]:
207-
os.remove(path)
208-
209220
if debug:
210221
import faulthandler
211222

sqlmesh/cli/main.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import click
99

10-
from sqlmesh import configure_logging
10+
from sqlmesh import configure_logging, remove_excess_logs
1111
from sqlmesh.cli import error_handler
1212
from sqlmesh.cli import options as opt
1313
from sqlmesh.cli.example_project import ProjectTemplate, init_example_project
@@ -100,17 +100,19 @@ def cli(
100100
if ctx.invoked_subcommand in SKIP_LOAD_COMMANDS:
101101
load = False
102102

103-
configs = load_configs(config, Context.CONFIG_TYPE, paths)
104-
log_limit = list(configs.values())[0].log_limit
105103
configure_logging(
106104
debug,
107105
log_to_stdout,
108-
log_limit=log_limit,
109106
log_file_dir=log_file_dir,
110107
ignore_warnings=ignore_warnings,
111108
)
112109
configure_console(ignore_warnings=ignore_warnings)
113110

111+
configs = load_configs(config, Context.CONFIG_TYPE, paths)
112+
log_limit = list(configs.values())[0].log_limit
113+
114+
remove_excess_logs(log_file_dir, log_limit)
115+
114116
try:
115117
context = Context(
116118
paths=paths,

sqlmesh/core/config/connection.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
)
2727
from sqlmesh.core.engine_adapter.shared import CatalogSupport
2828
from sqlmesh.core.engine_adapter import EngineAdapter
29-
from sqlmesh.utils import str_to_bool
29+
from sqlmesh.utils import debug_mode_enabled, str_to_bool
3030
from sqlmesh.utils.errors import ConfigError
3131
from sqlmesh.utils.pydantic import (
3232
ValidationInfo,
@@ -67,8 +67,16 @@ def validate(cls: t.Any, data: t.Any) -> t.Any:
6767
try:
6868
importlib.import_module(import_name)
6969
except ImportError:
70+
if debug_mode_enabled():
71+
raise
72+
73+
logger.exception("Failed to import the engine library")
74+
7075
raise ConfigError(
71-
f"Failed to import the '{engine_type}' engine library. Please run `pip install \"sqlmesh[{extra_name}]\"`."
76+
f"Failed to import the '{engine_type}' engine library. This may be due to a missing "
77+
"or incompatible installation. Please ensure the required dependency is installed by "
78+
f'running: `pip install "sqlmesh[{extra_name}]"`. For more details, check the logs '
79+
"in the 'logs/' folder, or rerun the command with the '--debug' flag."
7280
)
7381

7482
return data

sqlmesh/core/context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535

3636
import abc
3737
import collections
38-
from itertools import chain
3938
import logging
4039
import sys
4140
import time
@@ -44,6 +43,7 @@
4443
import unittest.result
4544
from functools import cached_property
4645
from io import StringIO
46+
from itertools import chain
4747
from pathlib import Path
4848
from shutil import rmtree
4949
from types import MappingProxyType

sqlmesh/magics.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,25 +129,31 @@ def _shell(self) -> t.Any:
129129
@line_magic
130130
def context(self, line: str) -> None:
131131
"""Sets the context in the user namespace."""
132-
from sqlmesh import configure_logging
132+
from sqlmesh import configure_logging, remove_excess_logs
133133

134134
args = parse_argstring(self.context, line)
135-
configs = load_configs(args.config, Context.CONFIG_TYPE, args.paths)
136-
log_limit = list(configs.values())[0].log_limit
135+
log_file_dir = args.log_file_dir
136+
137137
configure_logging(
138138
args.debug,
139-
log_limit=log_limit,
140-
log_file_dir=args.log_file_dir,
139+
log_file_dir=log_file_dir,
141140
ignore_warnings=args.ignore_warnings,
142141
)
143142
configure_console(ignore_warnings=args.ignore_warnings)
143+
144+
configs = load_configs(args.config, Context.CONFIG_TYPE, args.paths)
145+
log_limit = list(configs.values())[0].log_limit
146+
147+
remove_excess_logs(log_file_dir, log_limit)
148+
144149
try:
145150
context = Context(paths=args.paths, config=configs, gateway=args.gateway)
146151
self._shell.user_ns["context"] = context
147152
except Exception:
148153
if args.debug:
149154
logger.exception("Failed to initialize SQLMesh context")
150155
raise
156+
151157
context.console.log_success(f"SQLMesh project context set to: {', '.join(args.paths)}")
152158

153159
@magic_arguments()

tests/core/test_connection_config.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,10 @@ def test_engine_import_validator():
10161016
with pytest.raises(
10171017
ConfigError,
10181018
match=re.escape(
1019-
"""Failed to import the 'bigquery' engine library. Please run `pip install "sqlmesh[bigquery]"`."""
1019+
"Failed to import the 'bigquery' engine library. This may be due to a missing "
1020+
"or incompatible installation. Please ensure the required dependency is installed by "
1021+
'running: `pip install "sqlmesh[bigquery]"`. For more details, check the logs '
1022+
"in the 'logs/' folder, or rerun the command with the '--debug' flag."
10201023
),
10211024
):
10221025

@@ -1028,7 +1031,10 @@ class TestConfigA(PydanticModel):
10281031
with pytest.raises(
10291032
ConfigError,
10301033
match=re.escape(
1031-
"""Failed to import the 'bigquery' engine library. Please run `pip install "sqlmesh[bigquery_extra]"`."""
1034+
"Failed to import the 'bigquery' engine library. This may be due to a missing "
1035+
"or incompatible installation. Please ensure the required dependency is installed by "
1036+
'running: `pip install "sqlmesh[bigquery_extra]"`. For more details, check the logs '
1037+
"in the 'logs/' folder, or rerun the command with the '--debug' flag."
10321038
),
10331039
):
10341040

0 commit comments

Comments
 (0)