Skip to content

Commit

Permalink
feat: Added a --log-format option as a shorcut to quickly change th…
Browse files Browse the repository at this point in the history
…e format of logs
  • Loading branch information
edgarrmondragon committed Jun 19, 2024
1 parent a396e3b commit 54944f7
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 14 deletions.
1 change: 1 addition & 0 deletions docs/docs/reference/command-line-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The following CLI options are available for the top-level `meltano` command:
### Log Configurations

- [`--log-config`](/reference/settings#clilog_config) - Path to a logging configuration file. See [Logging](/guide/logging) for more information.
- `--log-format` - Shortcut for setting the log format instead of using `--log-config`. See the CLI output for available options.
- [`--log-level`](/reference/settings#clilog_level) - Set the log level for the command. Valid values are `debug`, `info`, `warning`, `error`, and `critical`.

### No Color
Expand Down
6 changes: 4 additions & 2 deletions src/meltano/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from meltano.cli.utils import InstrumentedGroup
from meltano.core.behavior.versioned import IncompatibleVersionError
from meltano.core.error import EmptyMeltanoFileException, ProjectNotFound
from meltano.core.logging import LEVELS, setup_logging
from meltano.core.logging import LEVELS, LogFormat, setup_logging
from meltano.core.project import PROJECT_ENVIRONMENT_ENV, Project
from meltano.core.project_settings_service import ProjectSettingsService
from meltano.core.tracking import Tracker
Expand Down Expand Up @@ -57,6 +57,7 @@ def main(self, *args, **kwargs) -> t.Any:
context_settings={"token_normalize_func": lambda x: x.replace("_", "-")},
)
@click.option("--log-level", type=click.Choice(tuple(LEVELS)))
@click.option("--log-format", type=click.Choice(tuple(LogFormat)))
@click.option(
"--log-config",
type=str,
Expand All @@ -79,6 +80,7 @@ def main(self, *args, **kwargs) -> t.Any:
def cli( # noqa: C901,WPS231
ctx: click.Context,
log_level: str,
log_format: str,
log_config: str,
environment: str,
no_environment: bool,
Expand Down Expand Up @@ -110,7 +112,7 @@ def cli( # noqa: C901,WPS231

try:
project = Project.find()
setup_logging(project)
setup_logging(project, log_format=LogFormat(log_format))
logger.debug("meltano %s, %s", __version__, platform.system()) # noqa: WPS323
if project.readonly:
logger.debug("Project is read-only.")
Expand Down
9 changes: 8 additions & 1 deletion src/meltano/core/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@
SizeThresholdJobLogException,
)
from .output_logger import OutputLogger
from .utils import DEFAULT_LEVEL, LEVELS, capture_subprocess_output, setup_logging
from .utils import (
DEFAULT_LEVEL,
LEVELS,
LogFormat,
capture_subprocess_output,
setup_logging,
)

__all__ = [
"JobLoggingService",
"LogFormat",
"MissingJobLogException",
"SizeThresholdJobLogException",
"OutputLogger",
Expand Down
81 changes: 70 additions & 11 deletions src/meltano/core/logging/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import asyncio
import enum
import logging
import os
import typing as t
Expand Down Expand Up @@ -34,6 +35,31 @@
)


class LogFormat(str, enum.Enum):
"""Log format options."""

COLORED = "colored"
UNCOLORED = "uncolored"
JSON = "json"
KEY_VALUE = "key_value"

def __str__(self) -> str:
"""Return the str() of the enum member.
Returns:
Value of the enum as a string.
"""
return self.value

def __repr__(self) -> str:
"""Return the repr() of the enum member.
Returns:
Value of the enum as a string.
"""
return self.value


def parse_log_level(log_level: str) -> int:
"""Parse a level descriptor into an logging level.
Expand Down Expand Up @@ -62,11 +88,12 @@ def read_config(config_file: os.PathLike | None = None) -> dict | None:
return None


def default_config(log_level: str) -> dict:
def default_config(log_level: str, log_format: LogFormat) -> dict:
"""Generate a default logging config.
Args:
log_level: set log levels to provided level.
log_format: set log format to provided format.
Returns:
A logging config suitable for use with `logging.config.dictConfig`.
Expand All @@ -78,24 +105,54 @@ def default_config(log_level: str) -> dict:
else:
formatter = rich_exception_formatter_factory(color_system="truecolor")

if log_format == LogFormat.COLORED:
key = "colored"
formatter_config = {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(
colors=not no_color,
exception_formatter=formatter,
),
"foreign_pre_chain": LEVELED_TIMESTAMPED_PRE_CHAIN,
}
elif log_format == LogFormat.JSON:
key = "json"
formatter_config = {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
"foreign_pre_chain": LEVELED_TIMESTAMPED_PRE_CHAIN,
}
elif log_format == LogFormat.KEY_VALUE:
key = "key_value"
formatter_config = {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.KeyValueRenderer(
key_order=["timestamp", "level", "event", "logger"]
),
"foreign_pre_chain": LEVELED_TIMESTAMPED_PRE_CHAIN,
}
elif log_format == LogFormat.UNCOLORED:
key = "uncolored"
formatter_config = {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(
colors=False,
exception_formatter=formatter,
),
"foreign_pre_chain": LEVELED_TIMESTAMPED_PRE_CHAIN,
}

return {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"colored": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(
colors=not no_color,
exception_formatter=formatter,
),
"foreign_pre_chain": LEVELED_TIMESTAMPED_PRE_CHAIN,
},
key: formatter_config,
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": log_level.upper(),
"formatter": "colored",
"formatter": key,
"stream": "ext://sys.stderr",
},
},
Expand Down Expand Up @@ -127,13 +184,15 @@ def setup_logging( # noqa: WPS210
project: Project | None = None,
log_level: str = DEFAULT_LEVEL,
log_config: os.PathLike | None = None,
log_format: LogFormat = LogFormat.COLORED,
) -> None:
"""Configure logging for a meltano project.
Args:
project: the meltano project
log_level: set log levels to provided level.
log_config: a logging config suitable for use with `logging.config.dictConfig`.
log_format: set log format to provided format.
"""
logging.basicConfig(force=True)
log_level = log_level.upper()
Expand All @@ -142,7 +201,7 @@ def setup_logging( # noqa: WPS210
log_config = log_config or project.settings.get("cli.log_config")
log_level = project.settings.get("cli.log_level")

config = read_config(log_config) or default_config(log_level)
config = read_config(log_config) or default_config(log_level, log_format=log_format)
logging_config.dictConfig(config)
structlog.configure(
processors=[
Expand Down

0 comments on commit 54944f7

Please sign in to comment.