-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
209 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,34 +23,39 @@ line_length = 120 | |
multi_line_output = 5 | ||
|
||
[tool.poetry] | ||
name = "tail_jsonl" | ||
version = "0.0.1" | ||
description = "Tail JSONL Logs" | ||
license = "MIT" | ||
authors = ["Kyle King <[email protected]>"] | ||
maintainers = [] | ||
repository = "https://github.com/kyleking/tail-jsonl" | ||
classifiers = [ | ||
"Development Status :: 1 - Planning", | ||
"License :: OSI Approved :: MIT License", | ||
"Operating System :: OS Independent", | ||
"Programming Language :: Python :: 3.10", | ||
"Programming Language :: Python :: 3.11", | ||
"Programming Language :: Python :: 3.8", | ||
"Programming Language :: Python :: 3.9" | ||
] # https://pypi.org/classifiers/ | ||
description = "Tail JSONL Logs" | ||
documentation = "https://tail-jsonl.kyleking.me" | ||
readme = "docs/README.md" | ||
include = ["LICENSE.md"] | ||
keywords = [] | ||
classifiers = [ | ||
"Development Status :: 1 - Planning", | ||
"License :: OSI Approved :: MIT License", | ||
"Operating System :: OS Independent", | ||
"Programming Language :: Python :: 3.8", | ||
"Programming Language :: Python :: 3.9", | ||
"Programming Language :: Python :: 3.10", | ||
"Programming Language :: Python :: 3.11", | ||
] # https://pypi.org/classifiers/ | ||
license = "MIT" | ||
maintainers = [] | ||
name = "tail_jsonl" | ||
readme = "docs/README.md" | ||
repository = "https://github.com/kyleking/tail-jsonl" | ||
version = "0.0.1" | ||
|
||
[tool.poetry.urls] | ||
"Bug Tracker" = "https://github.com/kyleking/tail-jsonl/issues" | ||
"Changelog" = "https://github.com/kyleking/tail-jsonl/blob/main/docs/docs/CHANGELOG.md" | ||
|
||
[tool.poetry.scripts] | ||
tail-jsonl = "tail_jsonl:main" | ||
|
||
[tool.poetry.dependencies] | ||
python = "^3.8.12" | ||
calcipy = ">=0.21.3" | ||
python = "^3.8.12" | ||
rich = ">=13.3.1" | ||
|
||
[tool.poetry.dev-dependencies] | ||
calcipy = { version = "*", extras = ["dev", "lint", "test"] } | ||
calcipy = {extras = ["dev", "lint", "test"], version = "*"} | ||
|
||
[tool.poetry.urls] | ||
"Bug Tracker" = "https://github.com/kyleking/tail-jsonl/issues" | ||
"Changelog" = "https://github.com/kyleking/tail-jsonl/blob/main/docs/docs/CHANGELOG.md" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
"""tail_jsonl.""" | ||
|
||
from loguru import logger | ||
from loguru import logger # noqa: F401 | ||
|
||
__version__ = '0.0.1' | ||
__pkg_name__ = 'tail_jsonl' | ||
|
||
logger.disable(__pkg_name__) | ||
|
||
# ====== Above is the recommended code from calcipy_template and may be updated on new releases ====== | ||
|
||
from .main import main # noqa: F401 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
"""Configuration.""" | ||
|
||
from functools import cached_property | ||
|
||
from beartype import beartype | ||
from pydantic import BaseModel, Field | ||
|
||
|
||
class Styles(BaseModel): | ||
"""Styles configuration. | ||
Refer to https://rich.readthedocs.io/en/latest/style.html for available style | ||
Inspired by: https://github.com/Delgan/loguru/blob/07f94f3c8373733119f85aa8b9ca05ace3325a4b/loguru/_defaults.py#L31-L73 | ||
And: https://github.com/hynek/structlog/blob/bcfc7f9e60640c150bffbdaeed6328e582f93d1e/src/structlog/dev.py#L126-L141 | ||
""" | ||
|
||
timestamp: str = 'dim grey' | ||
message: str = '' | ||
|
||
level_error: str = 'bold red' | ||
level_warn: str = 'bold yellow' | ||
level_info: str = 'bold green' | ||
level_debug: str = 'bold blue' | ||
level_fallback: str = '' | ||
|
||
key: str = 'green' | ||
value: str = '' | ||
|
||
@cached_property | ||
def _level_lookup(self) -> dict[str, str]: | ||
return { | ||
'ERROR': self.level_error, | ||
'WARNING': self.level_warn, | ||
'WARN': self.level_warn, | ||
'INFO': self.level_info, | ||
'DEBUG': self.level_debug, | ||
} | ||
|
||
@beartype | ||
def get_level_style(self, level: str) -> str: | ||
"""Return the right style for the specified level.""" | ||
return self._level_lookup.get(level.upper(), self.level_fallback) | ||
|
||
|
||
class Keys(BaseModel): | ||
"""Special Keys.""" | ||
|
||
timestamp: list[str] = Field(default_factory=lambda: ['timestamp']) | ||
level: list[str] = Field(default_factory=lambda: ['level']) | ||
message: list[str] = Field(default_factory=lambda: ['event', 'message']) | ||
|
||
on_own_line: list[str] = Field(default_factory=lambda: ['exception']) | ||
|
||
|
||
class Config(BaseModel): | ||
"""`tail-jsonl` config.""" | ||
|
||
styles: Styles = Field(default_factory=Styles) | ||
keys: Keys = Field(default_factory=Keys) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
"""Core print logic.""" | ||
|
||
import json | ||
from copy import copy | ||
from typing import Any | ||
|
||
from beartype import beartype | ||
from loguru import logger | ||
from pydantic import BaseModel | ||
from rich.console import Console | ||
from rich.text import Text | ||
|
||
from .config import Config | ||
|
||
|
||
@beartype | ||
def pop_key(data: dict, keys: list[str], fallback: str) -> Any: | ||
"""Recursively pop whichever key matches first or default to the fallback.""" | ||
try: | ||
key = keys.pop(0) | ||
return data.pop(key, None) or pop_key(data, keys, fallback) | ||
except IndexError: | ||
return fallback | ||
|
||
|
||
class Record(BaseModel): | ||
"""Record Model.""" | ||
|
||
timestamp: str | ||
level: str | ||
message: str | ||
data: dict | ||
|
||
@classmethod | ||
def from_line(cls, data: dict, config: Config) -> 'Record': | ||
"""Extract Record from jsonl.""" | ||
return cls( | ||
timestamp=pop_key(data, copy(config.keys.timestamp), '<no timestamp>'), | ||
level=pop_key(data, copy(config.keys.level), '<no level>'), | ||
message=pop_key(data, copy(config.keys.message), '<no message>'), | ||
data=data, | ||
) | ||
|
||
|
||
@beartype | ||
def print_record(line: str, console: Console, config: Config) -> None: | ||
"""Format and print the record.""" | ||
try: | ||
record = Record.from_line(json.loads(line), config=config) | ||
except Exception: | ||
logger.exception('Error in tail-json to parse line', line=line) | ||
console.print('') # Line break | ||
return | ||
|
||
text = Text(tab_size=4) # FIXME: Why isn't this indenting what is wrapped? | ||
text.append(f'{record.timestamp: <28}', style=config.styles.timestamp) | ||
text.append(f' {record.level: <7}', style=config.styles.get_level_style(record.level)) | ||
text.append(f' {record.message: <20}', style=config.styles.message) | ||
|
||
full_lines = [] | ||
for key in config.keys.on_own_line: | ||
line = record.data.pop(key, None) | ||
if line: | ||
full_lines.append((key, line)) | ||
|
||
for key, value in record.data.items(): | ||
text.append(f' {key}:', style=config.styles.key) | ||
text.append(f' {str(value): <10}', style=config.styles.value) | ||
|
||
console.print(text) | ||
for key, line in full_lines: | ||
new_text = Text() | ||
new_text.append(f' ∟ {key}', style='bold green') | ||
new_text.append(f': {line}') | ||
console.print(new_text) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
"""Public CLI interface.""" | ||
|
||
import fileinput | ||
|
||
from beartype import beartype | ||
from rich.console import Console | ||
|
||
from ._private.config import Config | ||
from ._private.core import print_record | ||
|
||
|
||
@beartype | ||
def main() -> None: | ||
"""CLI Entrypoint.""" | ||
console = Console() | ||
# PLANNED: Support configuration with argparse or environment variable | ||
config = Config() | ||
for line in fileinput.input(): | ||
print_record(line, console, config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{"jsonrpc": "2.0", "method": "sum", "params": [ null, 1, 2, 4, false, true], "id": "1"} | ||
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]} | ||
{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "2"} | ||
{"event": "Testing", "user_id": "UUID('f86f89aa-a260-431a-a3a6-7b37a695e012'), UUID('f86f89aa-a260-431a-a3a6-7b37a695e012'), UUID('f86f89aa-a260-431a-a3a6-7b37a695e012'), UUID('f86f89aa-a260-431a-a3a6-7b37a695e012'), UUID('f86f89aa-a260-431a-a3a6-7b37a695e012'), UUID('f86f89aa-a260-431a-a3a6-7b37a695e012')", "something": 123, "level": "debug", "timestamp": "2022-12-22T16:52:28.380253Z", "func_name": "<module>", "filename": "test.py", "lineno": 15, "exception": "line 1\n\t\tline 2 .... \n\t...?"} |